From 4f4c6d904ef03cf03c54fd84519be5107062915b Mon Sep 17 00:00:00 2001 From: Bahfir Abbes Date: Sun, 20 Dec 2015 13:37:46 +0100 Subject: [PATCH 001/834] Update edit.php allow cherry-pick of a sub-menu by changing its parent identifier --- htdocs/admin/menus/edit.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/admin/menus/edit.php b/htdocs/admin/menus/edit.php index f4f1bbb96bf..60fc9758987 100644 --- a/htdocs/admin/menus/edit.php +++ b/htdocs/admin/menus/edit.php @@ -75,6 +75,7 @@ if ($action == 'update') $menu->perms=$_POST['perms']; $menu->target=$_POST['target']; $menu->user=$_POST['user']; + $menu->fk_menu=$_POST['fk_menu']; $result=$menu->update($user); if ($result > 0) { @@ -415,7 +416,7 @@ elseif ($action == 'edit') print ''.$langs->trans('Type').''.$langs->trans(ucfirst($menu->type)).''.$langs->trans('DetailType').''; // MenuId Parent - print ''.$langs->trans('MenuIdParent').''; + print ''; //$menu_handler //print ''; print ''.$menu->fk_menu.''; From 61ef9921bd3e8f6a0ea5223b61323b3494f8c25c Mon Sep 17 00:00:00 2001 From: Bahfir Abbes Date: Sun, 27 Dec 2015 17:50:29 +0100 Subject: [PATCH 002/834] correction of precedent commit I do not kow how I deleted a line instead of another. Sorry for it. Hope it's resolved --- htdocs/admin/menus/edit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/menus/edit.php b/htdocs/admin/menus/edit.php index 60fc9758987..e29b47daeab 100644 --- a/htdocs/admin/menus/edit.php +++ b/htdocs/admin/menus/edit.php @@ -416,10 +416,10 @@ elseif ($action == 'edit') print ''.$langs->trans('Type').''.$langs->trans(ucfirst($menu->type)).''.$langs->trans('DetailType').''; // MenuId Parent + print ''.$langs->trans('MenuIdParent').''; print ''; //$menu_handler //print ''; - print ''.$menu->fk_menu.''; print ''.$langs->trans('DetailMenuIdParent').''; // Niveau From 0467dac43d22fcb5f5c23d756c449ee1a2e321f0 Mon Sep 17 00:00:00 2001 From: Sergio Sanchis Climent Date: Tue, 5 Jan 2016 09:47:14 +0100 Subject: [PATCH 003/834] Fix userlocaltax Is posible userlocaltax1_rate or userlocaltax2_rate have value 0.0000 and no enter in if, now check if diferent 0 and now enter --- htdocs/core/lib/price.lib.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/core/lib/price.lib.php b/htdocs/core/lib/price.lib.php index 528abfcd6a5..be392fcad91 100644 --- a/htdocs/core/lib/price.lib.php +++ b/htdocs/core/lib/price.lib.php @@ -170,7 +170,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax1_rate && $apply_tax) { + if ($uselocaltax1_rate!=0 && $apply_tax) { $result[14] = price2num(($tot_sans_remise_wt * (1 + ( $localtax1_rate / 100))) - $tot_sans_remise_wt, 'MT'); $localtaxes[0] += $result[14]; @@ -193,7 +193,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax2_rate && $apply_tax) { + if ($uselocaltax2_rate!=0 && $apply_tax) { $result[15] = price2num(($tot_sans_remise_wt * (1 + ( $localtax2_rate / 100))) - $tot_sans_remise_wt, 'MT'); $localtaxes[0] += $result[15]; @@ -264,7 +264,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax1_rate && $apply_tax) { + if ($uselocaltax1_rate!=0 && $apply_tax) { $result[14] = price2num(($tot_sans_remise * (1 + ( $localtax1_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax1 for total_ht_without_discount $result[8] += $result[14]; // total_ttc_without_discount + tax1 @@ -287,7 +287,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax2_rate && $apply_tax) { + if ($uselocaltax2_rate!=0 && $apply_tax) { $result[15] = price2num(($tot_sans_remise * (1 + ( $localtax2_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax2 for total_ht_without_discount $result[8] += $result[15]; // total_ttc_without_discount + tax2 From 4d7503968807f97f685401a4c75336e3bd2b9817 Mon Sep 17 00:00:00 2001 From: Sergio Sanchis Climent Date: Thu, 7 Jan 2016 09:37:04 +0100 Subject: [PATCH 004/834] Edited correctly for check values --- htdocs/core/lib/price.lib.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/htdocs/core/lib/price.lib.php b/htdocs/core/lib/price.lib.php index be392fcad91..03eb651e4db 100644 --- a/htdocs/core/lib/price.lib.php +++ b/htdocs/core/lib/price.lib.php @@ -93,10 +93,14 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt //dol_syslog("Price.lib::calcul_price_total qty=".$qty." pu=".$pu." remiserpercent_ligne=".$remise_percent_ligne." txtva=".$txtva." uselocaltax1_rate=".$uselocaltax1_rate." uselocaltax2_rate=".$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$ice_base_type.' type='.$type.' progress='.$progress); $countryid=$seller->country_id; + + if (is_numeric($uselocaltax1_rate)) $uselocaltax1_rate=(float) $uselocaltax1_rate; + if (is_numeric($uselocaltax2_rate)) $uselocaltax2_rate=(float) $uselocaltax2_rate; + if ($uselocaltax1_rate < 0) $uselocaltax1_rate=$seller->localtax1_assuj; if ($uselocaltax2_rate < 0) $uselocaltax2_rate=$seller->localtax2_assuj; - dol_syslog('Price.lib::calcul_price_total qty='.$qty.' pu='.$pu.' remise_percent_ligne='.$remise_percent_ligne.' txtva='.$txtva.' uselocaltax1_rate='.$uselocaltax1_rate.' uselocaltax2_rate='.$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$ice_base_type.' type='.$type.' progress='.$progress); + dol_syslog('Price.lib::calcul_price_total qty='.$qty.' pu='.$pu.' remise_percent_ligne='.$remise_percent_ligne.' txtva='.$txtva.' uselocaltax1_rate='.$uselocaltax1_rate.' uselocaltax2_rate='.$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$price_base_type.' type='.$type.' progress='.$progress); // Now we search localtaxes information ourself (rates and types). $localtax1_type=0; @@ -170,7 +174,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax1_rate!=0 && $apply_tax) { + if ($uselocaltax1_rate && $apply_tax) { $result[14] = price2num(($tot_sans_remise_wt * (1 + ( $localtax1_rate / 100))) - $tot_sans_remise_wt, 'MT'); $localtaxes[0] += $result[14]; @@ -193,7 +197,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax2_rate!=0 && $apply_tax) { + if ($uselocaltax2_rate && $apply_tax) { $result[15] = price2num(($tot_sans_remise_wt * (1 + ( $localtax2_rate / 100))) - $tot_sans_remise_wt, 'MT'); $localtaxes[0] += $result[15]; @@ -264,7 +268,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax1_rate!=0 && $apply_tax) { + if ($uselocaltax1_rate && $apply_tax) { $result[14] = price2num(($tot_sans_remise * (1 + ( $localtax1_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax1 for total_ht_without_discount $result[8] += $result[14]; // total_ttc_without_discount + tax1 @@ -287,7 +291,7 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt if ($type == 1) $apply_tax = true; break; } - if ($uselocaltax2_rate!=0 && $apply_tax) { + if ($uselocaltax2_rate && $apply_tax) { $result[15] = price2num(($tot_sans_remise * (1 + ( $localtax2_rate / 100))) - $tot_sans_remise, 'MT'); // amount tax2 for total_ht_without_discount $result[8] += $result[15]; // total_ttc_without_discount + tax2 From 236d4d777184dcdf012ff52df591f334bb911871 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 10 Jan 2016 13:05:22 +0100 Subject: [PATCH 005/834] Fix add Croatia into list of country in EEC --- htdocs/core/class/commonobject.class.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index cd3386d5d90..78f544cc8ab 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2918,6 +2918,7 @@ abstract class CommonObject /** * Return if a country is inside the EEC (European Economic Community) + * TODO Add a field into dictionary * * @return boolean true = country inside EEC, false = country outside EEC */ @@ -2939,7 +2940,8 @@ abstract class CommonObject 'FR', // France 'GB', // United Kingdom 'GR', // Greece - 'NL', // Holland + 'HR', // Croatia + 'NL', // Holland 'HU', // Hungary 'IE', // Ireland 'IM', // Isle of Man - Included in UK @@ -2949,7 +2951,7 @@ abstract class CommonObject 'LV', // Latvia 'MC', // Monaco - Included in France 'MT', // Malta - //'NO', // Norway + //'NO', // Norway 'PL', // Poland 'PT', // Portugal 'RO', // Romania From 988c0131d211138e32f265cc470c2286b435038f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 10 Jan 2016 18:28:50 +0100 Subject: [PATCH 006/834] Fix: markRate can be 100 --- htdocs/core/tpl/objectline_edit.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/tpl/objectline_edit.tpl.php b/htdocs/core/tpl/objectline_edit.tpl.php index 4316ab0b318..2b693573df2 100644 --- a/htdocs/core/tpl/objectline_edit.tpl.php +++ b/htdocs/core/tpl/objectline_edit.tpl.php @@ -341,7 +341,7 @@ if (! empty($conf->margin->enabled)) setTimeout(function () { rate.focus() }, 50); return false; } - if (npRate == "np_markRate" && rate.val() >= 100) + if (npRate == "np_markRate" && rate.val() > 100) { alert('transnoentitiesnoconv("markRateShouldBeLesserThan100"); ?>'); e.stopPropagation(); From 217fef3d49fa608d4227708fad27deb4dee5cfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Doursenaud?= Date: Fri, 8 Jan 2016 06:50:16 +0100 Subject: [PATCH 007/834] FIX #4291 Correctly filter bank card GETPOSTs --- htdocs/compta/bank/card.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/compta/bank/card.php b/htdocs/compta/bank/card.php index dbe0722c77d..8a2ee5bce35 100644 --- a/htdocs/compta/bank/card.php +++ b/htdocs/compta/bank/card.php @@ -276,7 +276,7 @@ if ($action == 'create') // Label print ''.$langs->trans("LabelBankCashAccount").''; - print ''; + print ''; // Type print ''.$langs->trans("AccountType").''; @@ -477,19 +477,19 @@ if ($action == 'create') if (! empty($conf->global->MAIN_BANK_ACCOUNTANCY_CODE_ALWAYS_REQUIRED)) { print ''.$langs->trans("AccountancyCode").''; - print 'account_number).'">'; + print 'account_number).'">'; } else { print ''.$langs->trans("AccountancyCode").''; - print 'account_number).'">'; + print 'account_number).'">'; } // Accountancy journal if (! empty($conf->accounting->enabled)) { print ''.$langs->trans("AccountancyJournal").''; - print 'accountancy_journal).'">'; + print 'accountancy_journal).'">'; } print ''; From 7e1c3d9f1bcba2b16967fbb4c20f54df078febae Mon Sep 17 00:00:00 2001 From: aspangaro Date: Mon, 11 Jan 2016 21:18:12 +0100 Subject: [PATCH 008/834] Fix: #4394 Untranslated label in list of expense reports --- htdocs/expensereport/list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/expensereport/list.php b/htdocs/expensereport/list.php index fe775139362..8a58541772a 100644 --- a/htdocs/expensereport/list.php +++ b/htdocs/expensereport/list.php @@ -222,7 +222,7 @@ if ($resql) print_liste_field_titre($langs->trans("TotalHT"),$_SERVER["PHP_SELF"],"d.total_ht","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("TotalVAT"),$_SERVER["PHP_SELF"],"d.total_tva","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("TotalTTC"),$_SERVER["PHP_SELF"],"d.total_ttc","",$param,'align="right"',$sortfield,$sortorder); - print_liste_field_titre($langs->trans("Statut"),$_SERVER["PHP_SELF"],"","",$param,'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch '); print "\n"; From 0993ac90ebf69dc6110b30ecfa8d68b31b150fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 12 Jan 2016 10:09:02 +0100 Subject: [PATCH 009/834] Update facture.php --- htdocs/compta/facture.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index de1cba15e17..eb3f969890b 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -3667,7 +3667,7 @@ else if ($id > 0 || ! empty($ref)) } else { - print ''; + print ''; } } From 605d830d107d0fc104dbfd6585ad8bd7311ca387 Mon Sep 17 00:00:00 2001 From: phf Date: Tue, 12 Jan 2016 11:09:47 +0100 Subject: [PATCH 010/834] FIX if we dont use SUPPLIER_ORDER_USE_HOUR_FOR_DELIVERY_DATE the hour is displayed on pdf --- htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php index 006b349c965..b881f45dab0 100644 --- a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php +++ b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php @@ -1088,7 +1088,7 @@ class pdf_muscadet extends ModelePDFSuppliersOrders $pdf->SetTextColor(0,0,60); $usehourmin='day'; - if (empty($conf->global->SUPPLIER_ORDER_USE_HOUR_FOR_DELIVERY_DATE)) $usehourmin='dayhour'; + if (!empty($conf->global->SUPPLIER_ORDER_USE_HOUR_FOR_DELIVERY_DATE)) $usehourmin='dayhour'; if (! empty($object->date_livraison)) { $posy+=4; From 81f91487615deca5988952c4a4a095564b383176 Mon Sep 17 00:00:00 2001 From: phf Date: Wed, 13 Jan 2016 14:41:32 +0100 Subject: [PATCH 011/834] FIX can't clone event --- htdocs/comm/action/class/actioncomm.class.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index 68eb6994e5a..e91ffe18d45 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -421,6 +421,20 @@ class ActionComm extends CommonObject $this->id=0; + if (!is_object($fuser)) + { + if ($fuser > 0) + { + $u = new User($db); + $u->fetch($fuser); + $fuser = $u; + } + else + { + $fuser = $user; + } + } + // Create clone $result=$this->add($fuser); if ($result < 0) $error++; From d0aca962d69d022b7eb20ebfb6c1b4574b6a7c98 Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Wed, 13 Jan 2016 15:44:26 +0100 Subject: [PATCH 012/834] Fix: ajax error with multicompany module --- htdocs/opensurvey/wizard/index.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/htdocs/opensurvey/wizard/index.php b/htdocs/opensurvey/wizard/index.php index 68f75f3bce7..0fdde4bbe40 100644 --- a/htdocs/opensurvey/wizard/index.php +++ b/htdocs/opensurvey/wizard/index.php @@ -1,6 +1,7 @@ - * Copyright (C) 2014 Marcos García +/* Copyright (C) 2013 Laurent Destailleur + * Copyright (C) 2014 Marcos García + * Copyright (C) 2016 Regis Houssin * * 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 @@ -22,7 +23,7 @@ //if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); //if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); -if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); +//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; From b8f52edf8420cf71fb42ec1e038b1838d4ad5a0f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 16 Jan 2016 10:20:14 +0100 Subject: [PATCH 013/834] FIX: Sql syntax error in doc_generic_task_odt --- .../project/task/doc/doc_generic_task_odt.modules.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php index 31172a32f43..2b60695db51 100644 --- a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php +++ b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php @@ -585,9 +585,9 @@ class doc_generic_task_odt extends ModelePDFTask $odfHandler->mergeSegment($listlinestaskres); } - //Time ressources + // Time ressources $sql = "SELECT t.rowid, t.task_date, t.task_duration, t.fk_user, t.note"; - $sql.= ", u.name, u.firstname"; + $sql.= ", u.lastname, u.firstname"; $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; $sql .= " , ".MAIN_DB_PREFIX."user as u"; $sql .= " WHERE t.fk_task =".$object->id; @@ -607,6 +607,7 @@ class doc_generic_task_odt extends ModelePDFTask if (!empty($row['fk_user'])) { $objectdetail=new User($this->db); $objectdetail->fetch($row['fk_user']); + // TODO Use a cache to aoid fetch for same user $row['fullcivname']=$objectdetail->getFullName($outputlangs,1); } else { $row['fullcivname']=''; From f64615da62eab4c5b4bc7ae7e4ca6a897506f685 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 16 Jan 2016 17:41:17 +0100 Subject: [PATCH 014/834] Complete demo samples --- dev/initdata/img/applepieproduct.jpg | Bin 0 -> 15410 bytes dev/initdata/img/dolidroid_114x114.png | Bin 0 -> 8406 bytes dev/initdata/img/dolidroid_512x512_en.png | Bin 0 -> 83178 bytes .../dolidroid_screenshot_home_720x1280.png | Bin 0 -> 33693 bytes dev/initdata/img/genericcustomer.png | Bin 0 -> 7472 bytes dev/initdata/img/indiancompany.png | Bin 0 -> 4229 bytes dev/initdata/img/logo_nltechno_94x100.png | Bin 0 -> 4251 bytes dev/initdata/img/nocountrycomp.png | Bin 0 -> 3761 bytes dev/initdata/img/pearpieproduct.jpg | Bin 0 -> 19420 bytes dev/initdata/img/pinkdressproduct.jpg | Bin 0 -> 18005 bytes dev/initdata/img/printcompany.png | Bin 105005 -> 20188 bytes dev/initdata/img/rolluproduct.jpg | Bin 0 -> 14319 bytes dev/initdata/img/spanishcompany.png | Bin 0 -> 6865 bytes dev/initdata/img/swisstouch.png | Bin 0 -> 5766 bytes dev/initdata/img/teclibcompany.png | Bin 0 -> 26702 bytes 15 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev/initdata/img/applepieproduct.jpg create mode 100644 dev/initdata/img/dolidroid_114x114.png create mode 100644 dev/initdata/img/dolidroid_512x512_en.png create mode 100644 dev/initdata/img/dolidroid_screenshot_home_720x1280.png create mode 100644 dev/initdata/img/genericcustomer.png create mode 100644 dev/initdata/img/indiancompany.png create mode 100644 dev/initdata/img/logo_nltechno_94x100.png create mode 100644 dev/initdata/img/nocountrycomp.png create mode 100644 dev/initdata/img/pearpieproduct.jpg create mode 100644 dev/initdata/img/pinkdressproduct.jpg create mode 100644 dev/initdata/img/rolluproduct.jpg create mode 100644 dev/initdata/img/spanishcompany.png create mode 100644 dev/initdata/img/swisstouch.png create mode 100644 dev/initdata/img/teclibcompany.png diff --git a/dev/initdata/img/applepieproduct.jpg b/dev/initdata/img/applepieproduct.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f63ddacc7702dbd9dd12886f5663d3ad796d6ac8 GIT binary patch literal 15410 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/dolidroid_114x114.png b/dev/initdata/img/dolidroid_114x114.png new file mode 100644 index 0000000000000000000000000000000000000000..88c6bf0c3da46ec8c7628c24c48f2073ca1de37e GIT binary patch literal 8406 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/dolidroid_512x512_en.png b/dev/initdata/img/dolidroid_512x512_en.png new file mode 100644 index 0000000000000000000000000000000000000000..9de25fb5ce9698fcdac2435fac43ba3909c42ea3 GIT binary patch literal 83178 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 VxA}3gu`vSvQR#IHubLm*$ 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 e$IUns`GoWp-7LDE=(9!gj;0AuCW6ZrxF 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 z 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/pearpieproduct.jpg b/dev/initdata/img/pearpieproduct.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3eb696c54bfad7b3c3263ec559bbfcf4d2c55fc1 GIT binary patch literal 19420 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/pinkdressproduct.jpg b/dev/initdata/img/pinkdressproduct.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f38b413f8e5ca765e8e5709884462d762cace871 GIT binary patch literal 18005 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

    QEwVf3oDPRgoJs>YPGt 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/printcompany.png b/dev/initdata/img/printcompany.png index f3e51e3b5a78f795c822c42aa35d91965adaf627..b745fc77174e9cbf3d6e69aa7dd1a51490cff985 100644 GIT binary patch literal 20188 zcmeEucQl+`yzY=hBwC7)sF94`l8i1$h%yAxdkaPxy^T&1iG&~-b@VWL?`1GT5SpeoO_ zK_GHN;76B|0(hqzZI}oAp?ImL{0wyd_dByWFBW*`ikpg&CkW&i^!JCXWI)>tc=NIs zR9*4%FS2XbsILU)ri_9>cR|o+Pj!7K*YSQyEPBnI+jah2RG)Y*+PW(U^WD8Sf6LSz z!^h`xH}W1i6c#Kq5n7&~Ny(`7IS2~N4UvKVG5)3C8tC$Dfu%6nCp{&f%X9+~f#QrrUHvRLlj&}EaR77^Rb z;y*?`L)9u>TNxON+WSa{dqm0*?t6#6(hV-J5efvomm9LAykd z8*UIDhqeoS_p@u#u8K@a=e{ch)3=EjFPz}#`G%-(P za|OxZ^YY8i`)i=k0eubknJ+q2@qhR5|CQmhch`y>U`2l3je+gwL;($Y2D^M!uHvK7E_Gnm8pe&pb`Mv9R>*^&R)k@DH zQKW1VZauHzBuhhJ+>`?0<+z&;JP+7mtk@{|ft7q6D(GvUe4s&f7TKICGCJw8p9d6I zj=BVqcAKcQH9HD4AC0|`Y@*3z)%o$xpfs&G-fKh%g5fKTY1g8s5x;8+#ict8q#jSV z6ng*c+ne?r^hQlwA3er=rC_H1=VSLp4?l^p7cv>?+Cqt*GBPrspAo4=h{mz?m-sUV zm*pvMg5QdWb>b68PcB+Cr>v~3bT4`|D1g(FhJz&4agqe{zY}nLa#DbYmmS{JN%jUi zG++|fWERrX*=T5Jy8D2UZ!#B33{b(TB^!R^L~LsK$wlcbx(6`n>zlkbDJS{xH@h&k z6J%4Dj=Ppe%2bYhlp+s*5D0{JpxNHhClWgq0nBNayNqM>RT`CWCpyL*=FsK5_&eJA zKEzu!8mYc{Vb^LC_rl}EU=?WI{ZuQ;r!X;d+#}ob&wu2hRQ>C7Boe8gbLH0?x-o~m zG^J$Ys;=q-lACo!8aic7w93(vjfqLy)pgSr>C?OTXgGBGef;f@u57q1MJQvn{n=SG zx?MY&=#GpoBtEz6OZwuz&>3Y>>ol>$z%$#>-rnxnUo7D8l3tO2YVWacCHexSDY)ZM zlfTgzCqzSKC>XcOxk7_xDJ&q*|A(yI&CNG-`bCRYpz!S*>-plKkr5!VB* zWM9!=^f(&L(O#L+E?8uu_C)Ga)6o@`mAPgE3)rSW7x(%S)z1q1Bh0u`IN$!>-gE67 z$(>sL*<3ROUNmXq=BS%u#_O@d$Tx4^JgVYeXl>%)e50?$%J8jkj6YtLBh1aU$W^Do z)LdW%s-V(vt99yyum_;}Ju#quMLf1{=G`RP{D#^}}r3#LM5TT#pPG<=Dm* z+in@x&~7q~&#_MVdeRX#HfX~5ua%Ge%6vmxSW9mzymG=?JxdU)Qru?cs*$)q-+0Ik zmCF?wYV$rch8p;^9Vls`4|DfpaoF%3vehp+?AOzSk5>wPC^ny5PMRPiXmBq&O&_^} zSFWg%mpC4*PnhxR7vClf^`C!1rTv(l4e@n#Ei1IZgF3TpP0IV$@vJPRN(l-Ns!9ra zq70g+M5``P8rUmhHfl|3XhRy`wCjyp!F(QSQVb1~@wa4}brDP!H(_FCg&$Mi?k(;f ztqxIfs~SK1ka=k^yYY0V*A}J>JtpJ42c7Kg3;speb5~rxBmj~AL^a7ogPIW6klqkE zzAo~sO_zt>1{N7N^W^iBLUBs>kRmc@m-fBET?YuJ+EAb{FE1ST#O|U1B!zRu%V#R` z*cBrE1&g%^3JQu-Qk~;#d0+-!0jXXT41D8O3f7>9*M_551o3ymF+BdvwXM<1l+DAX3n|ZHSZ_%{*ArEq%jji%4*$Gq!v_7gjkMp{Yf&XY> zAr?JhP4gU(d{ngnewZq2tj$t7@M4iU+OVz-F#8tD4>LE&$KI!eGf(J!V1>g1FK4yv zZ>u3&l%Sb!*S55fOD}F@kB^Re=jpE0QB*6oG7ZO_QDO>UXRrg=2&;AffPlJBUbLLA z7-$UwB55~j67a9(zdU)GDMPN$Qfi9hQa22Ie-4;WEVNNx8w*a{Q-GeEo+0iW^sbEL zX=d_2buM(huBhcDo&z*NS#}dI(D^bM6_oVy$qoD+S&l#r?QDvBe$Q|R1lioN%JlT~ zalp#TK_~C3*-ng(?pI?pEyL!2zkB!Y$JA7SB+w5UeGde*u?kvLuB-hpB5RlGZJRp$ z_$yn$`gL{Gqs3+|`u-~%>!`W;CVAZP55DTaR^(oD-14~@;@mM!xD%n0EHeMw=L>q! zE>oZ@Wx|%wBCRL8eaTTyl$6~+OUJQCAKAgJOzH}hEfbZ=O?qhkj)HY6rM7kxMvzTZ{&S<+$7NB=rtZ)n za_=IWi$?_k2J-}`p~l4O3aXgPKe8b#%1e(AUosYrw(9}gV)`r(IhFzRP&Ee$ZZ^gN zj{Ij1tM?nXI5aU=Ox)buCxQ=xJ_onJYOEY}|2F9ALhq|5`?;Q<)(jrNpaJ2#nd%RGr#+-(bD3mm~}vhz8WMcp|i$etzs!pB|6t zxjaH?09fEfjM>3eDm8;#WMG%E2DQC2k^5gHRLfV{ftm!8(560zuy~! zIqz-jH?BMRZY30}W&}JQkBAZ2y+p+zBD$sK56&1=USF;C(q>&AJy{aR`u%CWKxewo zzBK6SQkQ>NbafZp52<>G?RJJKs~x)zd@)QhO>r?O!(hdG6>feT^cMf@6?l^&r1;T6 zt~K*XGDevYj0gz{S<;WL2r6ed|F}jNs_25j4~snq3aFvt)}0?$?-Mq~eQ!l`n0{Fx z*KaTluEEen)vFDH5eAx?G1F(1ddF#^lXL~7g3SeBX!>v4*gp*-kd7VkLF*M=j~b8{ zE?nqY-t{2LfYWflQhuKuA0O(ANr5dJ0z^bayh4hKU2A5eFc!Qk&NSwym;0T@XJ*W! zUrR-1{kcqF(-YR1Udifk`bBuFUx0Zl|HZ$`KOa59RR5=8w8iR)$>m}gmeVkN|2!M* z%-roP*f}l2cI$acJm8%~Xe}9>qoHu)2f&edlK-Pl3wY z3tfo1cAxr_S+MBN)GKDxgcF<9Xn*kN7smQ3#Yn%GHbwBrbh#pqH3%|6 z?59F+$^Q(VY4XbvYkbqB9~E?#^h$Znx)GD1_Av93pf@pUgvGbvGY1@SBC&i6KSkRT zB|@^yF6g^f7@n$OHsTKV7a+_vk0H_@G35`Bk}<;Q+e2B)Dcu{_N()KHnW-zgM@vApa@-Txg%uW|BTG|1 z-1nBnpyI~ zec+Ilw70ka^As9&*tj|aG+fE=YL=yFwobqjS~oN>1xFkGB#;=Tq4oB&$AM3~K45D^ zG1q$^RY``0*Ca0!Vs`DXl>i%bW7eSyak?{L#4Ehi{zItRqjmLXm%)AG^=NqOeC~C& zF%RZ-+3-5S;VcdFa?7tf8qzJ19a+<7bx8S$$cebsxB4`OYWTBtjTx=x=4KCKHdA?i zUGjttZIq}~vws_f@z8bF8L70@M20>m=EB>?4=q`zR-_%jDK=xtB2&7p+Zb0@SEXK) zT^cT&Yr`A(IIejG%3}ByJDT1&zDuAoBn02X6yhRo$scN5VAz*ryV=qj12~Fw>nBgE zv#zyur=8kQ;q`tLdjDtvxC1c9mPRIv*3+ zF`prQHheHNGz3AWtGZvGaq@S5J|t> z%-RvDf7MX4{v^ta&s@;GF=I5eBk&{Ae9O;km8xN-uNc0`$J?z@4#AgwVCG_P&QhP%R6KX{t*iq&{&&5Ql_IaJV z@veV1H}mceMGRz9rZ4A;*bPYD6uenJeYO;x>Uh-8q%}xpK)=C{cKM<7svN6whnXM(z&3o8?0{$kwh3IjzMVeE&9u@NZ#aV)9RQ ze8UL$s5eM(1z{a{(f`io5({)$2-P9H$HOIE79dixV9`r z^j#JpG%0OvIt(KO4Iq7@pof^1y`nf)kQl%N! zhUECaI-c@L04{hxuQ+yL%zHiAJ2K^bMD|0AgS~zJ{a*2id&cEMmA5V9;C%ViaE}WZ z7=C`~yjr1^HqE%@t^Axh(32n95t+QUBkJ4tM#y^cj8$+YGkpg+jUa_^h7Aa?&dX1D zUNee5Z(HkKR462WuB7i`U!M#JQ>%cy0$q@0XaUV<6475g`YJs;k zl3(ZIT}uvr+k;M8fe{R_d95_HFM`O{E+Nbzb^|tpU{B?q-4ON52`k#G9LBeUD9Omj zsHX9=vxDh(a(wgO<>{C|=MLr|T>(D*sTu*mw9R)dD@>Gx*$O!}9;N(zrP!O74D?_~ zpKR^AkJ?MRyvOGnPG9|#9p5rA@Q{aw?mq_Xt(K2k8^nRqAsiR=7s;r0YyCS-33#N0 zWe%&V_ z-%=jxKN{Q5>RY#6PxvOr3E)FdT8&fGe+r`btCV?OfgA7(H)t(tw@>Tr94ugpc*(nXi`< zfb9QEIWRhJLifplszE%9N9V!z_V52TNJGvbXqasYV^&FyTY5BoW~|Bt3O%4Sd{k9e z6)*;1NP{7~iqxPd%$Ee97ytq}7@BMr2I@!r9e`%?F}tg8$ph>+4%Gcf*zEUYnH!4q zPwg%hvrfne5A>Pm#GP0`j~@a(a(0#I@IzcLue@_lRt`ZhEhIV4E9af$G_xS=s0Dz9 zSK~bJ;Xwq3bvnWH>{yfSte$SKzC%yyj5(FI$L+ zI{@}({X@OArN_}EtZoAfJs^8kko*L9O?B8 zu=YW+Tht&{YsHA&nUA6I&M=r+TJXiB30kD~dDth+c^KW}3s1Uy1v>n&??U5)X6J7Y zL1_%%{q2bs0RS*fZj@JU@>`I05~V9|@e=}Sve9&+mhO?h@S7khWqL(Kp2SJ_#bSo7 z-?5ssKJyw8C+2DRP(_&~0(PN99q1Pj*`|vu&yI{#Ns{rO`1d)jO;p(-`e=c`?NL<$ zveuz>USmP`0w~QE@Wd0kZJG=XDGBuASC%P2X)AN|%V!C( z8d3!dSF3Mb7-Q~Rw}eDWSzSgMWh?0}*8n4HS_h1*8LjmC19JUH3DLyf=BBhR$43y< z1RL_e@!L)o&#|ji9q`DrktrkQ?!`eKR>o<=CNT zS8lqmc70=m)3PS%EfCHt*Sgg=8fq&jzwjoex3tJ=@Pa_lWA_UQ?#dO0y3#S^L(ybF zHf;+_tn29+NR_N&DkT+A&~!a$GeGNQ%#ne-BoOE7_YN8|6N_KYAV z_n<~D??kG2!X9+=>8l*aPzaNR5OPmJpvSYEG(BuYwMHeRzrX+FBm2e4>etU7Rn754 z$Y#uWKQ7eelM!+-y_wJqVVxoa72yxnaF=LIe_!q^PVHSJS;11&aKR@V<6?^wX%+i^ z&vBAS+Jg3`MzEPZY!~djL}W8Z zUe1y@-H3Aw75AW01TncQbamj*KpLNe7Lb?Kpe{j)DwF;Ab9^Sj`u(C2z%tRHOozu!Dwh*1p}0QYHnF~gS8xqFsfychm+-d-R3?_!G?4?RgT83hzw{uXb@{~YKO z`&jVUGo7z_DKpsA5UnCvR1V-rPbtnh62a$ z%;qJ1`px=G^50iUG{{nSVuP+X=*esNRuD`-?uXikA6z5tA^<#Lp(fSRU39%8q_ZO= z>+>An0-HOL4g$Uay89teRv587KU_-2uYps}$HnlLV{Rnkm%JP=gTQ(`^o8g3j5))( zz^B6*abDsW_@3B|?*hx*RnUV@=6yBJG(jfGNny(l1=jEQa|N7-&qU+fYn^h(f3jb> zT>{;XJUk8vMW^saiBTE4zb+;G{;*!@{jSJ8N891AL*oIQLvtzC_4D3cP8^I3{#!$T z&Fg_{HL!9U!8F3A^`8Lm_hbJb?7B%6XYp;J?cl26XxihK%^5aCsJr5MIQj}lm`8;n z`WXo1jU@@0HHy@q93bl>UF$+c@7cUfY}w1v0iPv+OQ@!U;85FRQ!)@vTYy2-bLn|Q zfF`9CvnsluTwl~i=ID3qSNyD?gLxjI3D`Jugp+}!hi~dGk%0=1U9ID2|D|Vp>yWspnPfoSp z1U@khE=a}6b~pm?UOI7;Kq^L?EXa+kCJRCVILHhLCv&vun?=IP)6x4255yj}_+T|~ z3|jQgx8;*qGS0j5>R0PxR;ClZ$RSftaqQZYNgV`P&VE*B7&FvA1zi-M#?TZFP+IS^ z{|I+`-^Z7$_4EiHQD zM}O8m4ZAKC2g+g#71B}8cOCel7I9uGZ-`UdNTI77)5Zb}NAE>JAZPRf`IuC#a~9eF zI{6$Y&B@BS!s(r~52S&TTJ$mkD>Ll#uCU&b&oM|6#J-KQnxLA zYYYV#q=L=?3#S0Am{E@@C7d0nGl#=X=)(N2vLxx~CXx`lXN@>70SBEfu#f8*n2jDR z0*Lxa0f5d%^x9fOZ_fLVvW^$1;5?v_3!WRB__NEYPn8UaJ4dJQIKqUXlU%YcT`~Cq zT7fgc0NgJ}fAuX@;qFhwY>@Yi-?CQH#o|U&37}JQP}k4ZdZt&p=4GMcYK?OZ%jqXe zz%S`(a>i3VvXMRjCB`FXxK=o=lQx=>rHNh@m=LTP-_{lG)J)-!qh4q)A{UM4Y}Ub1 zTwy#aM$f8Bwd2bkRDH)QTv!c*PU_$^M1Q^lrN>p1ctT`n@ypR3EmZT? z_GeP1gqP6e`jf9l*iRoH=k`0fLNk~Zz8807fYX)<+?pA(W>gFU-*t}w%)?nrC~=c* z!%qpPj6bsj*vReub$DU5?&yd&lGcqV&E)0ea@S#nrj*IVi* zjN_G%xsgh#KgRa$(h$7cv)E62Wm4h%&}4xXe%L`Znr^H}XdPGql8_VIz^CWW(#HK> zqc8NY8$mPrT1jk)zXpwkUAAXC4sR#xFS~K-v9Yt;z-*zjPD_&3>af0~iho=aqHn#k zUO-4YXWw&|qIAH7GtCB;1DMFS?nbsN^C^3eA<5q7!jOQwE$Pm&ZBkfe+y?9*0K#k` zgU#u{XMEnnW(#DO9v+@(E0x}d%NQh0q+UG1^2@%v(JSiPUdY%~wQ%7)^e}ApIeY9L zBLpKIk(I6_RPXOGu;?)x)w@8Y{*zKgN6)F?#{1U40B~4FJe6vJu2fqe$qB|0 zkyWO8JV;%vuYvpFn-(3121v8I{wuI=q8k)Yxx~E)3IQKwJ>x8*y7c>JT3)Jc-LP>C zr`+3LJWva$SF7?c>L^fl3LrXvmAuG_(HnE(a44ggaaIC+qyvmjRqFADsq<9{d-H@E z>0NNHdWyb`jFx6XzbC!K6&{CUQ39#n;nWojD0Bu|*_F7*ePQ>jFn{f>_J z%H|Wsir`>Kmqf1@kUDH$p557@()?86o>_n4Lf<+t{H6ONEpwht82~MN{-E0$K*Gdpyt2DREv-fF&1K|yi|mUV zwIA!osdC18g)>b#$xVLRvZq(nG2Q?t4KCNX&B>!C@P$oQYlRBzV4@qU5Ni87OQiQ^Bevt=#< z;#}Cdbuq{Bc-0)ukPWy6ydXXuVy96

    =WWQ_^3%4@;Qzltjph&4I&h0>y)Q=MFMb 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 DaJEMr literal 105005 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 literal 0 HcmV?d00001 diff --git a/dev/initdata/img/swisstouch.png b/dev/initdata/img/swisstouch.png new file mode 100644 index 0000000000000000000000000000000000000000..49dcb0aeb6df24a179a63afa7b0c61d6e1a7e366 GIT binary patch literal 5766 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 R$Nsx3FQXz|`^7Be{{iJXaQXlM literal 0 HcmV?d00001 From ff94a5ec71503fc90b779da6cf3a1c94e69d0b11 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 17 Jan 2016 17:35:02 +0100 Subject: [PATCH 015/834] Fix missing translation key --- htdocs/langs/en_US/sms.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/langs/en_US/sms.lang b/htdocs/langs/en_US/sms.lang index 4e89bb24730..70066caac96 100644 --- a/htdocs/langs/en_US/sms.lang +++ b/htdocs/langs/en_US/sms.lang @@ -49,5 +49,6 @@ SendSms=Send SMS SmsInfoCharRemain=Nb of remaining characters SmsInfoNumero= (format international ie : +33899701761) DelayBeforeSending=Delay before sending (minutes) +SmsNoPossibleSenderFound=No sender available. Check setup of your SMS provider. SmsNoPossibleRecipientFound=No target available. Check setup of your SMS provider. From c0515a469dc7c2bc369c31dd2f52dded0ec48513 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 18 Jan 2016 14:49:15 +0100 Subject: [PATCH 016/834] FIX Option to disable meteo was not set correctly in edit mode --- htdocs/admin/delais.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/delais.php b/htdocs/admin/delais.php index 7239ee89a4b..b43235ca1ec 100644 --- a/htdocs/admin/delais.php +++ b/htdocs/admin/delais.php @@ -177,7 +177,7 @@ if ($action == 'edit') $var=false; print ''; - print ''.$langs->trans("MAIN_DISABLE_METEO").'' .$form->selectyesno('MAIN_DISABLE_METEO',(isset($conf->global->MAIN_DISABLE_METEO)?1:0),1) . ''; + print ''.$langs->trans("MAIN_DISABLE_METEO").'' .$form->selectyesno('MAIN_DISABLE_METEO',(empty($conf->global->MAIN_DISABLE_METEO)?0:1),1) . ''; print ''; From 7c4a9d140b7cb5c70cc5ff577175f55802898216 Mon Sep 17 00:00:00 2001 From: jfefe Date: Thu, 21 Jan 2016 11:18:29 +0100 Subject: [PATCH 017/834] Replace reserved keyword "Resource" by "DolResource" # Conflicts: # htdocs/resource/list.php --- htdocs/core/lib/functions2.lib.php | 5 + htdocs/core/tpl/resource_add.tpl.php | 2 +- htdocs/resource/add.php | 197 +++++----- htdocs/resource/card.php | 4 +- ...source.class.php => dolresource.class.php} | 10 +- .../class/html.formresource.class.php | 8 +- htdocs/resource/element_resource.php | 338 +++++++++--------- htdocs/resource/list.php | 137 ++++--- 8 files changed, 362 insertions(+), 339 deletions(-) rename htdocs/resource/class/{resource.class.php => dolresource.class.php} (99%) diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index f2b13d79d0f..f61b6ee4073 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -1882,6 +1882,11 @@ function getElementProperties($element_type) $module='ficheinter'; $subelement='fichinter'; } + if ($element_type == 'dolresource') { + $classpath = 'resource/class'; + $module='resource'; + $subelement='dolresource'; + } $classfile = strtolower($subelement); $classname = ucfirst($subelement); diff --git a/htdocs/core/tpl/resource_add.tpl.php b/htdocs/core/tpl/resource_add.tpl.php index e2add5fb266..482dad50085 100644 --- a/htdocs/core/tpl/resource_add.tpl.php +++ b/htdocs/core/tpl/resource_add.tpl.php @@ -13,7 +13,7 @@ $out .= ''; $out .= ''; $out .= ''; $out .= ''; -$out .= ''; +$out .= ''; // Place diff --git a/htdocs/resource/add.php b/htdocs/resource/add.php index 679ef545442..cf13c79560f 100644 --- a/htdocs/resource/add.php +++ b/htdocs/resource/add.php @@ -1,7 +1,7 @@ - * Copyright (C) 2015 Alexandre Spangaro - * Copyright (C) 2015 Laurent Destailleur +/* Copyright (C) 2013 Jean-François Ferry + * Copyright (C) 2015 Alexandre Spangaro + * Copyright (C) 2015 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,15 +18,14 @@ */ /** - * \file resource/add.php - * \ingroup resource - * \brief Page to manage resource object - * Initialy built by build_class_from_table on 2013-07-24 16:03 + * \file resource/add.php + * \ingroup resource + * \brief Page to manage resource object */ require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/resource/class/resource.class.php'; +require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php'; require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php'; // Load traductions files required by page @@ -36,15 +35,15 @@ $langs->load("other"); $langs->load("resource"); // Get parameters -$id = GETPOST('id','int'); -$action = GETPOST('action','alpha'); -$cancel = GETPOST('cancel','alpha'); +$id = GETPOST('id','int'); +$action = GETPOST('action','alpha'); +$cancel = GETPOST('cancel','alpha'); if (empty($sortorder)) $sortorder="DESC"; if (empty($sortfield)) $sortfield="t.rowid"; if (empty($arch)) $arch = 0; if ($page == -1) { - $page = 0 ; + $page = 0 ; } $limit = $conf->global->limit; @@ -56,60 +55,60 @@ $pagenext = $page + 1; // Protection if external user if ($user->societe_id > 0) { - accessforbidden(); + accessforbidden(); } -$object = new Resource($db); +$object = new DolResource($db); if ($action == 'confirm_add_resource') { - if (! $cancel) - { - $error=''; + if (! $cancel) + { + $error=''; - $ref=GETPOST('ref','alpha'); - $description=GETPOST('description','alpha'); - $fk_code_type_resource=GETPOST('fk_code_type_resource','alpha'); + $ref=GETPOST('ref','alpha'); + $description=GETPOST('description','alpha'); + $fk_code_type_resource=GETPOST('fk_code_type_resource','alpha'); - if (empty($ref)) - { - $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Ref")); - setEventMessages($mesg, null, 'errors'); - $error++; - } + if (empty($ref)) + { + $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Ref")); + setEventMessages($mesg, null, 'errors'); + $error++; + } - if (! $error) - { - $object=new Resource($db); - $object->ref=$ref; - $object->description=$description; - $object->fk_code_type_resource=$fk_code_type_resource; + if (! $error) + { + $object=new Dolresource($db); + $object->ref=$ref; + $object->description=$description; + $object->fk_code_type_resource=$fk_code_type_resource; - $result=$object->create($user); - if ($result > 0) - { - // Creation OK - $db->commit(); - setEventMessages($langs->trans('ResourceCreatedWithSuccess'), null, 'mesgs'); - Header("Location: card.php?id=" . $object->id); - return; - } - else - { - // Creation KO - setEventMessages($object->error, $object->errors, 'errors'); - $action = ''; - } - } - else - { - $action = ''; - } - } - else - { - Header("Location: list.php"); - } + $result=$object->create($user); + if ($result > 0) + { + // Creation OK + $db->commit(); + setEventMessages($langs->trans('ResourceCreatedWithSuccess'), null, 'mesgs'); + Header("Location: card.php?id=" . $object->id); + return; + } + else + { + // Creation KO + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } + } + else + { + $action = ''; + } + } + else + { + Header("Location: list.php"); + } } @@ -122,58 +121,58 @@ $formresource = new FormResource($db); if (! $action) { - $pagetitle=$langs->trans('AddResource'); - llxHeader('',$pagetitle,''); - print load_fiche_titre($pagetitle,'','title_generic'); + $pagetitle=$langs->trans('AddResource'); + llxHeader('',$pagetitle,''); + print load_fiche_titre($pagetitle,'','title_generic'); - print '

    '; - print ''; + print ''; + print ''; - dol_fiche_head(''); + dol_fiche_head(''); - print ''; + print '
    '; - // Ref / label - $field = 'ref'; - print ''; - print ''; - print ''; - print ''; + // Ref / label + $field = 'ref'; + print ''; + print ''; + print ''; + print ''; - // Type - print ''; - print ''; + // Type + print ''; + print ''; - // Description - $field = 'description'; - print ''; - print ''; - print ''; - print ''; + // Description + $field = 'description'; + print ''; + print ''; + print ''; + print ''; - print '
    '; - print $langs->trans('ResourceFormLabel_'.$field); - print ''; - print ''; - print '
    '; + print $langs->trans('ResourceFormLabel_'.$field); + print ''; + print ''; + print '
    '.$langs->trans("ResourceType").''; - $ret = $formresource->select_types_resource($object->fk_code_type_resource, 'fk_code_type_resource', '', 2, 1); - print '
    '.$langs->trans("ResourceType").''; + $ret = $formresource->select_types_resource($object->fk_code_type_resource, 'fk_code_type_resource', '', 2, 1); + print '
    '; - print $langs->trans('ResourceFormLabel_'.$field); - print ''; - require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor = new DolEditor($field, $$field, 160, '', '', false); - $doleditor->Create(); - print '
    '; + print $langs->trans('ResourceFormLabel_'.$field); + print ''; + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor = new DolEditor($field, $$field, 160, '', '', false); + $doleditor->Create(); + print '
    '; + print ''; - dol_fiche_end(''); + dol_fiche_end(''); - echo '
    ', - '', - '   ', - '', - '
    '; + echo '
    ', + '', + '   ', + '', + '
    '; - print ''; + print ''; } diff --git a/htdocs/resource/card.php b/htdocs/resource/card.php index 31fb6a955b1..206c2eea644 100644 --- a/htdocs/resource/card.php +++ b/htdocs/resource/card.php @@ -29,7 +29,7 @@ if (! $res) $res=@include("../../main.inc.php"); // For "custom" directory if (! $res) die("Include of main fails"); require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; -require_once 'class/resource.class.php'; +require_once 'class/dolresource.class.php'; require_once 'class/html.formresource.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/resource.lib.php'; @@ -56,7 +56,7 @@ if ($user->societe_id > 0) if( ! $user->rights->resource->read) accessforbidden(); -$object = new Resource($db); +$object = new Dolresource($db); $hookmanager->initHooks(array('resource_card','globalcard')); $parameters=array('resource_id'=>$id); diff --git a/htdocs/resource/class/resource.class.php b/htdocs/resource/class/dolresource.class.php similarity index 99% rename from htdocs/resource/class/resource.class.php rename to htdocs/resource/class/dolresource.class.php index c323ad17e69..de4d25854f3 100644 --- a/htdocs/resource/class/resource.class.php +++ b/htdocs/resource/class/dolresource.class.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2013-2015 Jean-François Ferry * * 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 @@ -28,7 +28,7 @@ require_once DOL_DOCUMENT_ROOT."/core/lib/functions2.lib.php"; /** * DAO Resource object */ -class Resource extends CommonObject +class Dolresource extends CommonObject { var $element='resource'; //!< Id that identify managed objects var $table_element='resource'; //!< Name of table without prefix where object is stored @@ -414,7 +414,7 @@ class Resource extends CommonObject while ($i < $num) { $obj = $this->db->fetch_object($resql); - $line = new Resource($this->db); + $line = new Dolresource($this->db); $line->id = $obj->rowid; $line->ref = $obj->ref; $line->description = $obj->description; @@ -488,7 +488,7 @@ class Resource extends CommonObject while ($i < $num) { $obj = $this->db->fetch_object($resql); - $line = new Resource($this->db); + $line = new Dolresource($this->db); $line->id = $obj->rowid; $line->resource_id = $obj->resource_id; $line->resource_type = $obj->resource_type; @@ -574,7 +574,7 @@ class Resource extends CommonObject while ($i < $num) { $obj = $this->db->fetch_object($resql); - $line = new Resource($this->db); + $line = new Dolresource($this->db); $line->id = $obj->rowid; $line->resource_id = $obj->resource_id; $line->resource_type = $obj->resource_type; diff --git a/htdocs/resource/class/html.formresource.class.php b/htdocs/resource/class/html.formresource.class.php index 4128de6fead..82b0ebe7d1a 100644 --- a/htdocs/resource/class/html.formresource.class.php +++ b/htdocs/resource/class/html.formresource.class.php @@ -1,5 +1,5 @@ +/* Copyright (C) - 2013-2015 Jean-François FERRY * * 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 @@ -17,7 +17,7 @@ */ /** - * \file place/class/html.place.class.php + * \file place/class/html.formresource.class.php * \ingroup core * \brief Class file to manage forms into resource module */ @@ -75,7 +75,7 @@ class FormResource $out=''; $outarray=array(); - $resourcestat = new Resource($this->db); + $resourcestat = new Dolresource($this->db); $resources_used = $resourcestat->fetch_all('ASC', 't.rowid', $limit, $offset, $filter=''); @@ -159,7 +159,7 @@ class FormResource { global $langs,$user; - $resourcestat = new Resource($this->db); + $resourcestat = new Dolresource($this->db); dol_syslog(get_class($this)."::select_types_resource ".$selected.", ".$htmlname.", ".$filtertype.", ".$format,LOG_DEBUG); diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index e4477e1674a..62caa43f428 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2013 Jean-François Ferry * * 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,18 +16,18 @@ */ /** - * \file resource/element_resource.php - * \ingroup resource - * \brief Page to show and manage linked resources to an element + * \file resource/element_resource.php + * \ingroup resource + * \brief Page to show and manage linked resources to an element */ $res=0; -$res=@include("../main.inc.php"); // For root directory -if (! $res) $res=@include("../../main.inc.php"); // For "custom" directory +$res=@include("../main.inc.php"); // For root directory +if (! $res) $res=@include("../../main.inc.php"); // For "custom" directory if (! $res) die("Include of main fails"); -require 'class/resource.class.php'; +require 'class/dolresource.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; // Load traductions files requiredby by page @@ -35,92 +35,92 @@ $langs->load("resource"); $langs->load("other"); /* -$sortorder = GETPOST('sortorder','alpha'); -$sortfield = GETPOST('sortfield','alpha'); -$page = GETPOST('page','int'); +$sortorder = GETPOST('sortorder','alpha'); +$sortfield = GETPOST('sortfield','alpha'); +$page = GETPOST('page','int'); */ if( ! $user->rights->resource->read) - accessforbidden(); + accessforbidden(); -$object=new Resource($db); +$object=new Dolresource($db); $hookmanager->initHooks(array('element_resource')); -$object->available_resources = array('resource'); +$object->available_resources = array('dolresource'); // Get parameters -$id = GETPOST('id','int'); -$action = GETPOST('action','alpha'); -$mode = GETPOST('mode','alpha'); -$lineid = GETPOST('lineid','int'); -$element = GETPOST('element','alpha'); // element_type -$element_id = GETPOST('element_id','int'); -$resource_id = GETPOST('fk_resource','int'); -$resource_type = GETPOST('resource_type','alpha'); -$busy = GETPOST('busy','int'); -$mandatory = GETPOST('mandatory','int'); -$cancel = GETPOST('cancel','alpha'); -$confirm = GETPOST('confirm','alpha'); -$socid = GETPOST('socid','int'); +$id = GETPOST('id','int'); +$action = GETPOST('action','alpha'); +$mode = GETPOST('mode','alpha'); +$lineid = GETPOST('lineid','int'); +$element = GETPOST('element','alpha'); // element_type +$element_id = GETPOST('element_id','int'); +$resource_id = GETPOST('fk_resource','int'); +$resource_type = GETPOST('resource_type','alpha'); +$busy = GETPOST('busy','int'); +$mandatory = GETPOST('mandatory','int'); +$cancel = GETPOST('cancel','alpha'); +$confirm = GETPOST('confirm','alpha'); +$socid = GETPOST('socid','int'); -if ($socid > 0) +if ($socid > 0) { $element_id = $socid; $element = 'societe'; } - - + + /* * Actions */ if ($action == 'add_element_resource' && ! $cancel) { - $error++; - $res = 0; - if (! ($resource_id > 0)) - { - $error++; + $error++; + $res = 0; + if (! ($resource_id > 0)) + { + $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Resource")), null, 'errors'); $action=''; - } - else - { + } + else + { $objstat = fetchObjectByElement($element_id, $element); - + $res = $objstat->add_element_resource($resource_id, $resource_type, $busy, $mandatory); - } - if (! $error && $res > 0) - { - setEventMessages($langs->trans('ResourceLinkedWithSuccess'), null, 'mesgs'); - header("Location: ".$_SERVER['PHP_SELF'].'?element='.$element.'&element_id='.$element_id); - exit; - } + } + if (! $error && $res > 0) + { + setEventMessages($langs->trans('ResourceLinkedWithSuccess'), null, 'mesgs'); + header("Location: ".$_SERVER['PHP_SELF'].'?element='.$element.'&element_id='.$element_id); + exit; + } } // Update ressource if ($action == 'update_linked_resource' && $user->rights->resource->write && !GETPOST('cancel') ) { - $res = $object->fetch_element_resource($lineid); - if($res) - { - $object->busy = $busy; - $object->mandatory = $mandatory; + $res = $object->fetch_element_resource($lineid); + if($res) + { + $object->busy = $busy; + $object->mandatory = $mandatory; - $result = $object->update_element_resource($user); + $result = $object->update_element_resource($user); - if ($result >= 0) - { - setEventMessages($langs->trans('RessourceLineSuccessfullyUpdated'), null, 'mesgs'); - header("Location: ".$_SERVER['PHP_SELF']."?element=".$element."&element_id=".$element_id); - exit; - } - else - { - setEventMessages($object->error, $object->errors, 'errors'); - } - } + if ($result >= 0) + { + setEventMessages($langs->trans('RessourceLineSuccessfullyUpdated'), null, 'mesgs'); + header("Location: ".$_SERVER['PHP_SELF']."?element=".$element."&element_id=".$element_id); + exit; + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + } } // Delete a resource linked to an element @@ -134,7 +134,7 @@ if ($action == 'confirm_delete_linked_resource' && $user->rights->resource->dele header("Location: ".$_SERVER['PHP_SELF']."?element=".$element."&element_id=".$element_id); exit; } - else + else { setEventMessages($object->error, $object->errors, 'errors'); } @@ -164,149 +164,149 @@ llxHeader('',$pagetitle,''); // Load available resource, declared by modules $ret = count($object->available_resources); if($ret == -1) { - dol_print_error($db,$object->error); - exit; + dol_print_error($db,$object->error); + exit; } if(!$ret) { - print '
    '.$langs->trans('NoResourceInDatabase').'
    '; + print '
    '.$langs->trans('NoResourceInDatabase').'
    '; } else { - // Confirmation suppression resource line - if ($action == 'delete_resource') - { - print $form->formconfirm("element_resource.php?element=".$element."&element_id=".$element_id."&id=".$id."&lineid=".$lineid,$langs->trans("DeleteResource"),$langs->trans("ConfirmDeleteResourceElement"),"confirm_delete_linked_resource",'','',1); - } + // Confirmation suppression resource line + if ($action == 'delete_resource') + { + print $form->formconfirm("element_resource.php?element=".$element."&element_id=".$element_id."&id=".$id."&lineid=".$lineid,$langs->trans("DeleteResource"),$langs->trans("ConfirmDeleteResourceElement"),"confirm_delete_linked_resource",'','',1); + } - /* - * Specific to agenda module - */ - if ($element_id && $element == 'action') - { - require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php'; + /* + * Specific to agenda module + */ + if ($element_id && $element == 'action') + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php'; - $act = fetchObjectByElement($element_id,$element); - if (is_object($act)) - { + $act = fetchObjectByElement($element_id,$element); + if (is_object($act)) + { - $head=actions_prepare_head($act); + $head=actions_prepare_head($act); - dol_fiche_head($head, 'resources', $langs->trans("Action"),0,'action'); + dol_fiche_head($head, 'resources', $langs->trans("Action"),0,'action'); - // Affichage fiche action en mode visu - print ''; + // Affichage fiche action en mode visu + print '
    '; - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; - // Ref - print ''; + // Ref + print ''; - // Type - if (! empty($conf->global->AGENDA_USE_EVENT_TYPE)) - { - print ''; - } + // Type + if (! empty($conf->global->AGENDA_USE_EVENT_TYPE)) + { + print ''; + } - // Title - print ''; - print '
    '.$langs->trans("Ref").''; - print $form->showrefnav($act, 'id', $linkback, ($user->societe_id?0:1), 'id', 'ref', ''); - print '
    '.$langs->trans("Ref").''; + print $form->showrefnav($act, 'id', $linkback, ($user->societe_id?0:1), 'id', 'ref', ''); + print '
    '.$langs->trans("Type").''.$act->type.'
    '.$langs->trans("Type").''.$act->type.'
    '.$langs->trans("Title").''.$act->label.'
    '; + // Title + print ''.$langs->trans("Title").''.$act->label.''; + print ''; - dol_fiche_end(); - } - } + dol_fiche_end(); + } + } - /* - * Specific to thirdparty module - */ - if ($element_id && $element == 'societe') - { - $socstatic = fetchObjectByElement($element_id,$element); - if (is_object($socstatic)) - { - $savobject = $object; - - $object = $socstatic; - - require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - $head = societe_prepare_head($socstatic); + /* + * Specific to thirdparty module + */ + if ($element_id && $element == 'societe') + { + $socstatic = fetchObjectByElement($element_id,$element); + if (is_object($socstatic)) + { + $savobject = $object; - dol_fiche_head($head, 'resources', $langs->trans("ThirdParty"),0,'company'); + $object = $socstatic; + + require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; + $head = societe_prepare_head($socstatic); + + dol_fiche_head($head, 'resources', $langs->trans("ThirdParty"),0,'company'); dol_banner_tab($socstatic, 'socid', '', ($user->societe_id?0:1), 'rowid', 'nom'); - - print '
    '; - + + print '
    '; + print '
    '; - print ''; - - // Alias name (commercial, trademark or alias name) - print '"; - - print '
    '.$langs->trans('AliasNames').''; - print $socstatic->name_alias; - print "
    '; + print ''; - print ''; - - dol_fiche_end(); - - $object = $savobject; - } - } + // Alias name (commercial, trademark or alias name) + print '"; + + print '
    '.$langs->trans('AliasNames').''; + print $socstatic->name_alias; + print "
    '; + + print '
    '; + + dol_fiche_end(); + + $object = $savobject; + } + } - //print load_fiche_titre($langs->trans('ResourcesLinkedToElement'),'',''); + //print load_fiche_titre($langs->trans('ResourcesLinkedToElement'),'',''); - foreach ($object->available_resources as $modresources => $resources) - { - $resources=(array) $resources; // To be sure $resources is an array - foreach($resources as $resource_obj) - { - $element_prop = getElementProperties($resource_obj); + foreach ($object->available_resources as $modresources => $resources) + { + $resources=(array) $resources; // To be sure $resources is an array + foreach($resources as $resource_obj) + { + $element_prop = getElementProperties($resource_obj); - //print '/'.$modresources.'/class/'.$resource_obj.'.class.php
    '; + //print '/'.$modresources.'/class/'.$resource_obj.'.class.php
    '; - $path = ''; - if(strpos($resource_obj,'@')) - $path .= '/'.$element_prop['module']; + $path = ''; + if(strpos($resource_obj,'@')) + $path .= '/'.$element_prop['module']; - $linked_resources = $object->getElementResources($element,$element_id,$resource_obj); + $linked_resources = $object->getElementResources($element,$element_id,$resource_obj); - // If we have a specific template we use it - if(file_exists(dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_add.tpl.php'))) - { - $res=include dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_add.tpl.php'); - } - else - { - $res=include DOL_DOCUMENT_ROOT . '/core/tpl/resource_add.tpl.php'; - } + // If we have a specific template we use it + if(file_exists(dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_add.tpl.php'))) + { + $res=include dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_add.tpl.php'); + } + else + { + $res=include DOL_DOCUMENT_ROOT . '/core/tpl/resource_add.tpl.php'; + } //var_dump($element_id); - if ($mode != 'add' || $resource_obj != $resource_type) - { - //print load_fiche_titre($langs->trans(ucfirst($element_prop['element']).'Singular')); + if ($mode != 'add' || $resource_obj != $resource_type) + { + //print load_fiche_titre($langs->trans(ucfirst($element_prop['element']).'Singular')); - // If we have a specific template we use it - if(file_exists(dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_view.tpl.php'))) - { - $res=@include dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_view.tpl.php'); + // If we have a specific template we use it + if(file_exists(dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_view.tpl.php'))) + { + $res=@include dol_buildpath($path.'/core/tpl/resource_'.$element_prop['element'].'_view.tpl.php'); - } - else - { - $res=include DOL_DOCUMENT_ROOT . '/core/tpl/resource_view.tpl.php'; - } - } - } - } + } + else + { + $res=include DOL_DOCUMENT_ROOT . '/core/tpl/resource_view.tpl.php'; + } + } + } + } } llxFooter(); diff --git a/htdocs/resource/list.php b/htdocs/resource/list.php index 9bfa7309167..d5b56cc9781 100644 --- a/htdocs/resource/list.php +++ b/htdocs/resource/list.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2013-2014 Jean-François Ferry * * 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,34 +16,34 @@ */ /** - * \file resource/index.php - * \ingroup resource - * \brief Page to manage resource objects + * \file resource/index.php + * \ingroup resource + * \brief Page to manage resource objects */ require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/resource/class/resource.class.php'; +require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php'; -// Load translations files requiredby by page +// Load translations files required by page $langs->load("resource"); $langs->load("companies"); $langs->load("other"); // Get parameters -$id = GETPOST('id','int'); -$action = GETPOST('action','alpha'); +$id = GETPOST('id','int'); +$action = GETPOST('action','alpha'); -$lineid = GETPOST('lineid','int'); -$element = GETPOST('element','alpha'); -$element_id = GETPOST('element_id','int'); -$resource_id = GETPOST('resource_id','int'); +$lineid = GETPOST('lineid','int'); +$element = GETPOST('element','alpha'); +$element_id = GETPOST('element_id','int'); +$resource_id = GETPOST('resource_id','int'); -$sortorder = GETPOST('sortorder','alpha'); -$sortfield = GETPOST('sortfield','alpha'); -$page = GETPOST('page','int'); +$sortorder = GETPOST('sortorder','alpha'); +$sortfield = GETPOST('sortfield','alpha'); +$page = GETPOST('page','int'); -$object = new Resource($db); +$object = new Dolresource($db); $hookmanager->initHooks(array('resource_list')); @@ -52,7 +52,7 @@ if (empty($sortfield)) $sortfield="t.rowid"; if (empty($arch)) $arch = 0; if ($page == -1) { - $page = 0 ; + $page = 0 ; } $limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; @@ -61,7 +61,7 @@ $pageprev = $page - 1; $pagenext = $page + 1; if( ! $user->rights->resource->read) - accessforbidden(); + accessforbidden(); /* @@ -86,72 +86,91 @@ llxHeader('',$pagetitle,''); // Confirmation suppression resource line if ($action == 'delete_resource') { - print $form->formconfirm($_SERVER['PHP_SELF']."?element=".$element."&element_id=".$element_id."&lineid=".$lineid,$langs->trans("DeleteResource"),$langs->trans("ConfirmDeleteResourceElement"),"confirm_delete_resource",'','',1); + print $form->formconfirm($_SERVER['PHP_SELF']."?element=".$element."&element_id=".$element_id."&lineid=".$lineid,$langs->trans("DeleteResource"),$langs->trans("ConfirmDeleteResourceElement"),"confirm_delete_resource",'','',1); } // Load object list $ret = $object->fetch_all($sortorder, $sortfield, $limit, $offset); if($ret == -1) { - dol_print_error($db,$object->error); - exit; + dol_print_error($db,$object->error); + exit; } else { print_barre_liste($pagetitle, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $ret+1, $object->num_all,'title_generic.png'); } if(!$ret) { - print '
    '.$langs->trans('NoResourceInDatabase').'
    '; + print '
    '.$langs->trans('NoResourceInDatabase').'
    '; } else { - $var=true; + $var=true; - print ''."\n"; - print ''; - print_liste_field_titre($langs->trans('Id'),$_SERVER['PHP_SELF'],'t.rowid','',$param,'',$sortfield,$sortorder); - print_liste_field_titre($langs->trans('Ref'),$_SERVER['PHP_SELF'],'t.ref','',$param,'',$sortfield,$sortorder); - print_liste_field_titre($langs->trans('ResourceType'),$_SERVER['PHP_SELF'],'ty.code','',$param,'',$sortfield,$sortorder); - print_liste_field_titre($langs->trans('Action'),"","","","",'width="60" align="center"',"",""); - print "\n"; + print '
    '."\n"; + print ''; + print_liste_field_titre($langs->trans('Id'),$_SERVER['PHP_SELF'],'t.rowid','',$param,'',$sortfield,$sortorder); + print_liste_field_titre($langs->trans('Ref'),$_SERVER['PHP_SELF'],'t.ref','',$param,'',$sortfield,$sortorder); + print_liste_field_titre($langs->trans('ResourceType'),$_SERVER['PHP_SELF'],'ty.code','',$param,'',$sortfield,$sortorder); + print_liste_field_titre($langs->trans('Action'),"","","","",'width="60" align="center"',"",""); + print "\n"; - foreach ($object->lines as $resource) - { - $var=!$var; + foreach ($object->lines as $resource) + { + $var=!$var; - $style=''; - if($resource->id == GETPOST('lineid')) - $style='style="background: orange;"'; + $style=''; + if($resource->id == GETPOST('lineid')) + $style='style="background: orange;"'; - print ''; + print ''; - print ''; + print ''; - print ''; + print ''; - print ''; + print ''; - print ''; - } + print ''; + } - print '
    '; - print ''.$resource->id.''; - print '
    '; + print ''.$resource->id.''; + print ''; - print $resource->ref; - print ''; + print $resource->ref; + print ''; - print $resource->type_label; - print ''; + print $resource->type_label; + print ''; - print ''; - print img_edit(); - print ''; - print ' '; - print ''; - print img_delete(); - print ''; - print ''; + print ''; + print img_edit(); + print ''; + print ' '; + print ''; + print img_delete(); + print ''; + print '
    '; + print ''; } +/* + * Boutons actions +*/ +print '
    '; +$parameters = array(); +$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been +// modified by hook +if (empty($reshook)) +{ + if ($action != "edit" ) + { + // Edit resource + if($user->rights->resource->write) + { + print '
    '; + print ''.$langs->trans('AddResource').''; + print '
    '; + } + } +} +print '
    '; llxFooter(); $db->close(); - - From 581ea0f36b3bd90b3c9f271136c2f353ce8339ab Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:17:45 +0100 Subject: [PATCH 018/834] adding resource in contact type list --- htdocs/admin/dict.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 17033845f8c..f52a29249ea 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -466,6 +466,7 @@ if ($id == 11) 'propal' => $langs->trans('Proposal'), 'commande' => $langs->trans('Order'), 'facture' => $langs->trans('Bill'), + 'resource' => $langs->trans('Resource'), // 'facture_fourn' => $langs->trans('SupplierBill'), 'fichinter' => $langs->trans('InterventionCard') ); From 4c025d432316ac7958faf3605b29625500b1c1c9 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:23:05 +0100 Subject: [PATCH 019/834] Adding additional tabs --- htdocs/core/lib/resource.lib.php | 40 ++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/htdocs/core/lib/resource.lib.php b/htdocs/core/lib/resource.lib.php index e92d59dcac7..b56f361110a 100644 --- a/htdocs/core/lib/resource.lib.php +++ b/htdocs/core/lib/resource.lib.php @@ -1,6 +1,7 @@ + * Copyright (C) 2016 Gilles Poirier * * 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 @@ -28,7 +29,7 @@ * @param Object $object Object * @return array Array of head entries */ -function resourcePrepareHead($object) +function resource_prepare_head($object) { global $langs, $conf, $user; $h = 0; @@ -36,15 +37,50 @@ function resourcePrepareHead($object) $head[$h][0] = dol_buildpath('/resource/card.php',1).'?id='.$object->id; $head[$h][1] = $langs->trans("ResourceCard"); - $head[$h][2] = 'resource'; + $head[$h][2] = 'resource'; $h++; + if (empty($conf->global->MAIN_DISABLE_CONTACTS_TAB)) + { + $head[$h][0] = DOL_URL_ROOT.'/resource/contact.php?id='.$object->id; + $head[$h][1] = $langs->trans('Contact'); + $head[$h][2] = 'contact'; + $h++; + } + // Show more tabs from modules // Entries must be declared in modules descriptor with line // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab // $this->tabs = array('entity:-tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to remove a tab complete_head_from_modules($conf,$langs,$object,$head,$h,'resource'); + if (empty($conf->global->MAIN_DISABLE_NOTES_TAB)) + { + $nbNote = 0; + if(!empty($object->note_private)) $nbNote++; + if(!empty($object->note_public)) $nbNote++; + $head[$h][0] = DOL_URL_ROOT.'/fichinter/note.php?id='.$object->id; + $head[$h][1] = $langs->trans('Notes'); + if ($nbNote > 0) $head[$h][1].= ' '.$nbNote.''; + $head[$h][2] = 'note'; + $h++; + } + + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + $upload_dir = $conf->ficheinter->dir_output . "/" . dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview\.png)$')); + $head[$h][0] = DOL_URL_ROOT.'/fichinter/document.php?id='.$object->id; + $head[$h][1] = $langs->trans("Documents"); + if($nbFiles > 0) $head[$h][1].= ' '.$nbFiles.''; + $head[$h][2] = 'documents'; + $h++; + + $head[$h][0] = DOL_URL_ROOT.'/fichinter/info.php?id='.$object->id; + $head[$h][1] = $langs->trans('Info'); + $head[$h][2] = 'info'; + $h++; + + complete_head_from_modules($conf,$langs,$object,$head,$h,'intervention','remove'); return $head; } From d1018760dfdd3e233707e09cb2aa461f56e54e7f Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:24:19 +0100 Subject: [PATCH 020/834] Update resource.lib.php --- htdocs/core/lib/resource.lib.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/core/lib/resource.lib.php b/htdocs/core/lib/resource.lib.php index b56f361110a..dd7b7ef699f 100644 --- a/htdocs/core/lib/resource.lib.php +++ b/htdocs/core/lib/resource.lib.php @@ -57,30 +57,30 @@ function resource_prepare_head($object) if (empty($conf->global->MAIN_DISABLE_NOTES_TAB)) { $nbNote = 0; - if(!empty($object->note_private)) $nbNote++; + if(!empty($object->note_private)) $nbNote++; if(!empty($object->note_public)) $nbNote++; - $head[$h][0] = DOL_URL_ROOT.'/fichinter/note.php?id='.$object->id; + $head[$h][0] = DOL_URL_ROOT.'/resource/note.php?id='.$object->id; $head[$h][1] = $langs->trans('Notes'); if ($nbNote > 0) $head[$h][1].= ' '.$nbNote.''; $head[$h][2] = 'note'; $h++; } - + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - $upload_dir = $conf->ficheinter->dir_output . "/" . dol_sanitizeFileName($object->ref); + $upload_dir = $conf->resource->dir_output . "/" . dol_sanitizeFileName($object->ref); $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview\.png)$')); - $head[$h][0] = DOL_URL_ROOT.'/fichinter/document.php?id='.$object->id; + $head[$h][0] = DOL_URL_ROOT.'/resource/document.php?id='.$object->id; $head[$h][1] = $langs->trans("Documents"); if($nbFiles > 0) $head[$h][1].= ' '.$nbFiles.''; $head[$h][2] = 'documents'; $h++; - - $head[$h][0] = DOL_URL_ROOT.'/fichinter/info.php?id='.$object->id; + + $head[$h][0] = DOL_URL_ROOT.'/resource/info.php?id='.$object->id; $head[$h][1] = $langs->trans('Info'); $head[$h][2] = 'info'; $h++; - complete_head_from_modules($conf,$langs,$object,$head,$h,'intervention','remove'); + complete_head_from_modules($conf,$langs,$object,$head,$h,'resource', 'remove'); return $head; } From c34fd7b354c2b21227beadcabcc884c16c152564 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:26:12 +0100 Subject: [PATCH 021/834] add contact tabs on resource --- htdocs/resource/contact.php | 159 ++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 htdocs/resource/contact.php diff --git a/htdocs/resource/contact.php b/htdocs/resource/contact.php new file mode 100644 index 00000000000..70c64568bf6 --- /dev/null +++ b/htdocs/resource/contact.php @@ -0,0 +1,159 @@ + + * Copyright (C) 2007-2009 Laurent Destailleur + * Copyright (C) 2012 Juanjo Menent + * Copyright (C) 2016 Gilles Poirier + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/resource/contact.php + * \ingroup resource + * \brief Onglet de gestion des contacts des resources + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/resource/class/resource.class.php'; +require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/resource.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; + +$langs->load("resource"); +$langs->load("sendings"); +$langs->load("companies"); + +$id = GETPOST('id','int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action','alpha'); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'resource', $id, 'resource'); + +$object = new Resource($db); +$result = $object->fetch($id,$ref); + + +/* + * Ajout d'un nouveau contact + */ + +if ($action == 'addcontact' && $user->rights->resource->write) +{ + if ($result > 0 && $id > 0) + { + $contactid = (GETPOST('userid','int') ? GETPOST('userid','int') : GETPOST('contactid','int')); + $result = $object->add_contact($contactid, GETPOST('type','int'), GETPOST('source','alpha')); + } + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') { + $langs->load("errors"); + $mesg = $langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"); + } else { + $mesg = $object->error; + } + + setEventMessage($mesg, 'errors'); + } +} + +// bascule du statut d'un contact +else if ($action == 'swapstatut' && $user->rights->resource->write) +{ + $result=$object->swapContactStatus(GETPOST('ligne','int')); +} + +// Efface un contact +else if ($action == 'deletecontact' && $user->rights->resource->write) +{ + $result = $object->delete_contact(GETPOST('lineid','int')); + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else { + dol_print_error($db); + } +} + + +/* + * View + */ + +$form = new Form($db); +$formcompany = new FormCompany($db); +$contactstatic=new Contact($db); +$userstatic=new User($db); + +llxHeader('',$langs->trans("Resource")); + +// Mode vue et edition + +if ($id > 0 || ! empty($ref)) +{ + $soc = new Societe($db); + $soc->fetch($object->socid); + + + $head = resource_prepare_head($object); + dol_fiche_head($head, 'contact', $langs->trans("ResourceSingular"), 0, 'resource@resource'); + + + /* + * Resource synthese pour rappel + */ + print ''; + + print ''; + print ''; + + // Resource type + print ''; + print ''; + print ''; + print ''; + + print '
    '.$langs->trans("ResourceFormLabel_ref").''; + $linkback = $objet->ref.' '.$langs->trans("BackToList").''; + print $form->showrefnav($object, 'id', $linkback,1,"rowid"); + print '
    ' . $langs->trans("ResourceType") . ''; + print $object->type_label; + print '
    '; + print '
    '; + + print '
    '; + + if (! empty($conf->global->RESOURCE_HIDE_ADD_CONTACT_USER)) $hideaddcontactforuser=1; + if (! empty($conf->global->RESOURCE_HIDE_ADD_CONTACT_THIPARTY)) $hideaddcontactforthirdparty=1; + + $permission=1; + // Contacts lines + include DOL_DOCUMENT_ROOT.'/core/tpl/contacts.tpl.php'; +} + + +llxFooter(); +$db->close(); From 265f01806cdcd6eb61b38f3d7ff1d81363e973f4 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:27:46 +0100 Subject: [PATCH 022/834] add document feature on resource modules --- htdocs/resource/document.php | 141 +++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 htdocs/resource/document.php diff --git a/htdocs/resource/document.php b/htdocs/resource/document.php new file mode 100644 index 00000000000..cbe53be0ad1 --- /dev/null +++ b/htdocs/resource/document.php @@ -0,0 +1,141 @@ + + * Copyright (C) 2004-2010 Laurent Destailleur + * Copyright (C) 2005 Marc Barilley / Ocebo + * Copyright (C) 2005-2009 Regis Houssin + * Copyright (C) 2005 Simon TOSSER + * Copyright (C) 2011-2012 Juanjo Menent + * Copyright (C) 2013 Cédric Salvador + * Copyright (C) 2016 Gilles Poirier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/resource/document.php + * \ingroup resource + * \brief Page des documents joints sur les resources + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/resource/class/resource.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/resource.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; + +$langs->load("other"); +$langs->load("resource"); +$langs->load("companies"); + +$id = GETPOST('id','int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action','alpha'); +$confirm = GETPOST('confirm','alpha'); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'resource', $id, 'resource'); + + +// Get parameters +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if ($page == -1) { $page = 0; } +$offset = $conf->liste_limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortorder) $sortorder="ASC"; +if (! $sortfield) $sortfield="name"; + + +$object = new Resource($db); +$object->fetch($id, $ref); + +$upload_dir = $conf->resource->dir_output.'/'.dol_sanitizeFileName($object->ref); +$modulepart='fichinter'; + + +/* + * Actions + */ + +include_once DOL_DOCUMENT_ROOT . '/core/tpl/document_actions_pre_headers.tpl.php'; + + +/* + * View + */ + +$form = new Form($db); + +llxHeader('',$langs->trans("Resource")); + +if ($object->id) +{ + $object->fetch_thirdparty(); + + $head=resource_prepare_head($object); + + dol_fiche_head($head, 'documents', $langs->trans("ResourceSingular"), 0, 'resource'); + + + // Construit liste des fichiers + $filearray=dol_dir_list($upload_dir,"files",0,'','(\.meta|_preview\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); + $totalsize=0; + foreach($filearray as $key => $file) + { + $totalsize+=$file['size']; + } + + + print ''; + + + print ''; + print ''; + + // Resource type + print ''; + print ''; + print ''; + print ''; + + print ''; + print ''; + print '
    '.$langs->trans("ResourceFormLabel_ref").''; + $linkback = $objet->ref.' '.$langs->trans("BackToList").''; + print $form->showrefnav($object, 'id', $linkback,1,"rowid"); + print '
    ' . $langs->trans("ResourceType") . ''; + print $object->type_label; + print '
    '.$langs->trans("NbOfAttachedFiles").''.count($filearray).'
    '.$langs->trans("TotalSizeOfAttachedFiles").''.$totalsize.' '.$langs->trans("bytes").'
    '; + + print ''; + + $modulepart = 'ficheinter'; + $permission = $user->rights->ficheinter->creer; + $param = '&id=' . $object->id; + include_once DOL_DOCUMENT_ROOT . '/core/tpl/document_actions_post_headers.tpl.php'; + +} +else +{ + print $langs->trans("ErrorUnknown"); +} + + +llxFooter(); + +$db->close(); From f4a6945aa099b8c2de8358503cfe10c968e0d3d0 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:30:15 +0100 Subject: [PATCH 023/834] add note feature on resource module --- htdocs/resource/note.php | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 htdocs/resource/note.php diff --git a/htdocs/resource/note.php b/htdocs/resource/note.php new file mode 100644 index 00000000000..f3da12c0b49 --- /dev/null +++ b/htdocs/resource/note.php @@ -0,0 +1,90 @@ + + * Copyright (C) 2011-2012 Juanjo Menent + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2016 Gilles Poirier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/resource/note.php + * \ingroup fichinter + * \brief Fiche d'information sur une resource + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/resource/class/resource.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/resource.lib.php'; + +$langs->load('companies'); +$langs->load("interventions"); + +$id = GETPOST('id','int'); +$ref = GETPOST('ref', 'alpha'); +$action=GETPOST('action','alpha'); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'resource', $id, 'resource'); + +$object = new Resource($db); +$object->fetch($id,$ref); + +$permissionnote=$user->rights->resource->write; // Used by the include of actions_setnotes.inc.php + +/* + * Actions + */ + +include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not includ_once + + +/* + * View + */ + +llxHeader(); + +$form = new Form($db); + +if ($id > 0 || ! empty($ref)) +{ + $head = resource_prepare_head($object); + dol_fiche_head($head, 'note', $langs->trans('ResourceSingular'), 0, 'resource@resource'); + + print ''; + print ''; + print ''; + + // Resource type + print ''; + print ''; + print ''; + print ''; print "
    '.$langs->trans("ResourceFormLabel_ref").''; + $linkback = $objet->ref.' '.$langs->trans("BackToList").''; + print $form->showrefnav($object, 'id', $linkback,1,"rowid"); + print '
    ' . $langs->trans("ResourceType") . ''; + print $object->type_label; + print '
    "; + + print '
    '; + $permission=$user->rights->resource->write; + include DOL_DOCUMENT_ROOT.'/core/tpl/notes.tpl.php'; + + dol_fiche_end(); +} + +llxFooter(); +$db->close(); From 91e46b1d35385a4b2722a5a13bd56a8fa664cf1c Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:37:03 +0100 Subject: [PATCH 024/834] add somes fields on resource --- htdocs/install/mysql/tables/llx_resource.sql | 25 +++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/htdocs/install/mysql/tables/llx_resource.sql b/htdocs/install/mysql/tables/llx_resource.sql index 0d67075f1ea..37e0fbc7494 100755 --- a/htdocs/install/mysql/tables/llx_resource.sql +++ b/htdocs/install/mysql/tables/llx_resource.sql @@ -16,12 +16,21 @@ CREATE TABLE llx_resource ( - rowid integer AUTO_INCREMENT PRIMARY KEY, - entity integer DEFAULT 1 NOT NULL, - ref varchar(255), - description text, + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1 NOT NULL, + ref varchar(255), + asset_number varchar(255), + description text, fk_code_type_resource varchar(32), - note_public text, - note_private text, - tms timestamp -)ENGINE=innodb; \ No newline at end of file + datec datetime DEFAULT NULL, + date_valid datetime DEFAULT NULL, + fk_user_author integer DEFAULT NULL, + fk_user_modif integer DEFAULT NULL, + fk_user_valid integer DEFAULT NULL, + fk_statut smallint NOT NULL DEFAULT '0', + note_public text, + note_private text, + import_key varchar(14), + extraparams varchar(255), -- for stock other parameters with json format + tms timestamp +)ENGINE=innodb; From b47065862a3f0f125f3ec457dc43832219aaab94 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:38:05 +0100 Subject: [PATCH 025/834] Update llx_resource.sql --- htdocs/install/mysql/tables/llx_resource.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/install/mysql/tables/llx_resource.sql b/htdocs/install/mysql/tables/llx_resource.sql index 37e0fbc7494..afb73e9fd5f 100755 --- a/htdocs/install/mysql/tables/llx_resource.sql +++ b/htdocs/install/mysql/tables/llx_resource.sql @@ -1,5 +1,6 @@ -- Module to manage resources into Dolibarr ERP/CRM --- Copyright (C) 2013 Jean-François Ferry +-- Copyright (C) 2013 Jean-François Ferry +-- Copyright (C) 2016 Gilles Poirier -- -- 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 From 1fbfdbfefd33a979bf5250c28ed730cf814fedad Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 12:47:18 +0100 Subject: [PATCH 026/834] add new ressources fields --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index f2b3ff181cc..e2cad614d91 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -39,4 +39,14 @@ ALTER TABLE llx_cronjob MODIFY COLUMN unitfrequency varchar(255) NOT NULL DEFAUL ALTER TABLE llx_facture ADD INDEX idx_facture_fk_statut (fk_statut); UPDATE llx_projet as p set p.opp_percent = (SELECT percent FROM llx_c_lead_status as cls WHERE cls.rowid = p.fk_opp_status) WHERE p.opp_percent IS NULL AND p.fk_opp_status IS NOT NULL; - \ No newline at end of file + +ALTER TABLE llx_resource ADD COLUMN asset_number varchar(255) after ref; +ALTER TABLE llx_resource ADD COLUMN datec datetime DEFAULT NULL; +ALTER TABLE llx_resource ADD COLUMN date_valid datetime DEFAULT NULL; +ALTER TABLE llx_resource ADD COLUMN fk_user_author integer DEFAULT NULL; +ALTER TABLE llx_resource ADD COLUMN fk_user_modif integer DEFAULT NULL; +ALTER TABLE llx_resource ADD COLUMN fk_user_valid integer DEFAULT NULL; +ALTER TABLE llx_resource ADD COLUMN fk_statut smallint NOT NULL DEFAULT '0'; +ALTER TABLE llx_resource ADD COLUMN import_key varchar(14); +ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); + From f6dc4757d0f4115aead107a1cd13cf457293190e Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 18:13:14 +0100 Subject: [PATCH 027/834] add fields for resource tracking --- htdocs/install/mysql/tables/llx_element_resources.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/install/mysql/tables/llx_element_resources.sql b/htdocs/install/mysql/tables/llx_element_resources.sql index 9c67a0d52af..4fac28de4b1 100644 --- a/htdocs/install/mysql/tables/llx_element_resources.sql +++ b/htdocs/install/mysql/tables/llx_element_resources.sql @@ -26,6 +26,10 @@ CREATE TABLE llx_element_resources resource_type varchar(64), -- resource or user busy integer, mandatory integer, + dateo date, -- date start of using ressource + datee date, -- date end of using ressource + datet date, -- date terminaison of using ressource + duree real, -- total duration of using ressource fk_user_create integer, tms timestamp )ENGINE=innodb; From e2404b57b3d78bbe17b4ed2e6f15ec2d568a2c1b Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 18:15:11 +0100 Subject: [PATCH 028/834] adding additionnal fields for tracking resource use --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index e2cad614d91..b7e5ab28c76 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -50,3 +50,7 @@ ALTER TABLE llx_resource ADD COLUMN fk_statut smallint NOT NULL DEFAULT '0 ALTER TABLE llx_resource ADD COLUMN import_key varchar(14); ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); +ALTER TABLE llx_element_resources ADD COLUMN dateo date, -- date start of using ressource +ALTER TABLE llx_element_resources ADD COLUMN datee date, -- date end of using ressource +ALTER TABLE llx_element_resources ADD COLUMN datet date, -- date terminaison of using ressource +ALTER TABLE llx_element_resources ADD COLUMN duree real, -- total duration of using ressource From ceb1cd57984f77be68ab736b9839a6a2172b48d4 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Sun, 31 Jan 2016 18:16:53 +0100 Subject: [PATCH 029/834] Update 3.9.0-4.0.0.sql --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index b7e5ab28c76..352d950ef02 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -50,7 +50,7 @@ ALTER TABLE llx_resource ADD COLUMN fk_statut smallint NOT NULL DEFAULT '0 ALTER TABLE llx_resource ADD COLUMN import_key varchar(14); ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); -ALTER TABLE llx_element_resources ADD COLUMN dateo date, -- date start of using ressource -ALTER TABLE llx_element_resources ADD COLUMN datee date, -- date end of using ressource -ALTER TABLE llx_element_resources ADD COLUMN datet date, -- date terminaison of using ressource -ALTER TABLE llx_element_resources ADD COLUMN duree real, -- total duration of using ressource +ALTER TABLE llx_element_resources ADD COLUMN dateo date; -- date start of using ressource +ALTER TABLE llx_element_resources ADD COLUMN datee date; -- date end of using ressource +ALTER TABLE llx_element_resources ADD COLUMN datet date; -- date terminaison of using ressource +ALTER TABLE llx_element_resources ADD COLUMN duree real; -- total duration of using ressource From acc9879c7c9b19a5b0932ce2395dfb56fff78e6a Mon Sep 17 00:00:00 2001 From: fmarcet Date: Mon, 1 Feb 2016 13:05:14 +0100 Subject: [PATCH 030/834] FIX: It doesn't check if there is enough stock to update the lines of orders/invoices --- htdocs/commande/class/commande.class.php | 23 ++++++++++++++++++- htdocs/compta/facture/class/facture.class.php | 14 +++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index 5d2907041b2..d807d67cb1e 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -8,6 +8,7 @@ * Copyright (C) 2012-2014 Christophe Battarel * Copyright (C) 2013 Florian Henry * Copyright (C) 2014 Marcos García + * Copyright (C) 2016 Ferran Marcet * * 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 @@ -2372,7 +2373,7 @@ class Commande extends CommonOrder */ function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0,$txlocaltax2=0.0, $price_base_type='HT', $info_bits=0, $date_start='', $date_end='', $type=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_option=0) { - global $conf, $mysoc; + global $conf, $mysoc, $langs; dol_syslog(get_class($this)."::updateline id=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, date_start=$date_start, date_end=$date_end, type=$type, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, special_code=$special_code"); include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; @@ -2426,6 +2427,26 @@ class Commande extends CommonOrder $line = new OrderLine($this->db); $line->fetch($rowid); + if (!empty($line->fk_product)) + { + $product=new Product($this->db); + $result=$product->fetch($line->fk_product); + $product_type=$product->type; + + if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty) + { + $this->error=$langs->trans('ErrorStockIsNotEnough'); + dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR); + $this->db->rollback(); + unset($_POST['productid']); + unset($_POST['tva_tx']); + unset($_POST['price_ht']); + unset($_POST['qty']); + unset($_POST['buying_price']); + return self::STOCK_NOT_ENOUGH_FOR_ORDER; + } + } + $staticline = clone $line; $line->oldline = $staticline; diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index b1ec15f7536..705a52074c9 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -12,6 +12,7 @@ * Copyright (C) 2012-2014 Marcos García * Copyright (C) 2013 Cedric Gross * Copyright (C) 2013 Florian Henry + * Copyright (C) 2016 Ferran Marcet * * 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 @@ -2199,6 +2200,19 @@ class Facture extends CommonInvoice $line = new FactureLigne($this->db); $line->fetch($rowid); + if (!empty($line->fk_product)) + { + $product=new Product($this->db); + $result=$product->fetch($line->fk_product); + $product_type=$product->type; + + if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_INVOICE) && $product_type == 0 && $product->stock_reel < $qty) { + $this->error=$langs->trans('ErrorStockIsNotEnough'); + $this->db->rollback(); + return -3; + } + } + $staticline = clone $line; $line->oldline = $staticline; From d6cda60ed1c1f239ee7e76e6fc48430870d1948a Mon Sep 17 00:00:00 2001 From: fmarcet Date: Mon, 1 Feb 2016 13:40:02 +0100 Subject: [PATCH 031/834] FIX: Check stock of product by warehouse if $entrepot_id defined on shippings --- htdocs/expedition/class/expedition.class.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 4ed4a061358..88d25121ade 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -8,6 +8,7 @@ * Copyright (C) 2014 Cedric GROSS * Copyright (C) 2014 Marcos García * Copyright (C) 2014 Francis Appels + * Copyright (C) 2016 Ferran Marcet * * 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 @@ -763,13 +764,19 @@ class Expedition extends CommonObject return -1; } - if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT) // FIXME Check is done for stock of product, it must be done for stock of product into warehouse if $entrepot_id defined + if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT) { $product=new Product($this->db); $result=$product->fetch($fk_product); + if ($entrepot_id > 0) { + $product->load_stock(); + $product_stock = $product->stock_warehouse[$entrepot_id]->real; + } + else + $product_stock = $product->stock_reel; $product_type=$product->type; - if ($product_type == 0 && $product->stock_reel < $qty) + if ($product_type == 0 && $product_stock < $qty) { $this->error=$langs->trans('ErrorStockIsNotEnough'); $this->db->rollback(); From 4e83abba30d11a3c7ce99f8949cbaed2219fda2b Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Mon, 1 Feb 2016 15:40:16 +0100 Subject: [PATCH 032/834] Update element_resource.php --- htdocs/resource/element_resource.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index e4477e1674a..4193ee44749 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -257,6 +257,10 @@ else } } + // hook for other elements linked + $parameters=array('element'=>$element, 'element_id'=>$element_id ); + $reshook=$hookmanager->executeHooks('printElementTab',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); //print load_fiche_titre($langs->trans('ResourcesLinkedToElement'),'',''); From c1ff21c30a860bcf9431f0a537bacb7b140d89fc Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Mon, 1 Feb 2016 16:09:58 +0100 Subject: [PATCH 033/834] add fichinter link to resources --- htdocs/core/lib/fichinter.lib.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/htdocs/core/lib/fichinter.lib.php b/htdocs/core/lib/fichinter.lib.php index a6f68800dc4..ff67de88132 100644 --- a/htdocs/core/lib/fichinter.lib.php +++ b/htdocs/core/lib/fichinter.lib.php @@ -2,6 +2,8 @@ /* Copyright (C) 2006-2007 Laurent Destailleur * Copyright (C) 2007 Rodolphe Quiedeville * Copyright (C) 2012 Regis Houssin + * Copyright (C) 2016 Gilles Poirier + * * 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 @@ -65,6 +67,15 @@ function fichinter_prepare_head($object) // $this->tabs = array('entity:-tabname); to remove a tab complete_head_from_modules($conf,$langs,$object,$head,$h,'intervention'); + // Tab to link resources + if ($conf->resource->enabled) + { + $head[$h][0] = DOL_URL_ROOT.'/resource/element_resource.php?element=ficheinter&element_id='.$object->id; + $head[$h][1] = $langs->trans("Resources"); + $head[$h][2] = 'resource'; + $h++; + } + if (empty($conf->global->MAIN_DISABLE_NOTES_TAB)) { $nbNote = 0; From 62b35ffa455acbf9e80a26172d3f076b92b490cc Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Mon, 1 Feb 2016 16:15:30 +0100 Subject: [PATCH 034/834] add fichinter tabs on resource element link --- htdocs/resource/element_resource.php | 40 +++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index 4193ee44749..80c9f1f97a7 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -1,5 +1,6 @@ +/* Copyright (C) 2013 Jean-François Ferry + * Copyright (C) 2016 Gilles Poirier * * 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 @@ -257,6 +258,43 @@ else } } + /* + * Specific to agenda module + */ + if ($element_id && $element == 'ficheinter') + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/fichinter.lib.php'; + + $fichinter = fetchObjectByElement($element_id, $element); + if (is_object($fichinter)) + { + $head=fichinter_prepare_head($fichinter); + dol_fiche_head($head, 'resources', $langs->trans("InterventionCard"),0,'intervention'); + + // Affichage fiche action en mode visu + print ''; + + $linkback = ''.$langs->trans("BackToList").''; + + // Ref + print ''; + + + // Customer + if ( is_null($fichinter->client) ) + $fichinter->fetch_thirdparty(); + + print ""; + print ''; + print "
    '.$langs->trans("Ref").''; + print $form->showrefnav($fichinter, 'id', $linkback, ($user->societe_id?0:1), 'ref', 'ref', ''); + print '
    ".$langs->trans("Company")."'.$fichinter->client->getNomUrl(1).'
    "; + + dol_fiche_end(); + } + } + + // hook for other elements linked $parameters=array('element'=>$element, 'element_id'=>$element_id ); $reshook=$hookmanager->executeHooks('printElementTab',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks From 6bb6e66e134efb3cf53a5e332db25da4482250bb Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 14:05:35 +0100 Subject: [PATCH 035/834] element name used to create the clas... --- htdocs/resource/element_resource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index 80c9f1f97a7..6ac787bc359 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -259,9 +259,9 @@ else } /* - * Specific to agenda module + * Specific to fichinter module */ - if ($element_id && $element == 'ficheinter') + if ($element_id && $element == 'fichinter') { require_once DOL_DOCUMENT_ROOT.'/core/lib/fichinter.lib.php'; From 4c0f29d23460d27b1b4824d1fdb04af3d64e846f Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 14:05:58 +0100 Subject: [PATCH 036/834] Update fichinter.lib.php --- htdocs/core/lib/fichinter.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/lib/fichinter.lib.php b/htdocs/core/lib/fichinter.lib.php index ff67de88132..390a7e14435 100644 --- a/htdocs/core/lib/fichinter.lib.php +++ b/htdocs/core/lib/fichinter.lib.php @@ -70,7 +70,7 @@ function fichinter_prepare_head($object) // Tab to link resources if ($conf->resource->enabled) { - $head[$h][0] = DOL_URL_ROOT.'/resource/element_resource.php?element=ficheinter&element_id='.$object->id; + $head[$h][0] = DOL_URL_ROOT.'/resource/element_resource.php?element=fichinter&element_id='.$object->id; $head[$h][1] = $langs->trans("Resources"); $head[$h][2] = 'resource'; $h++; From 835c2725c517527cd09031cca6cb0181f2156896 Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 14:14:01 +0100 Subject: [PATCH 037/834] Update element_resource.php --- htdocs/resource/element_resource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/resource/element_resource.php b/htdocs/resource/element_resource.php index 6ac787bc359..7dbcd3a5522 100644 --- a/htdocs/resource/element_resource.php +++ b/htdocs/resource/element_resource.php @@ -269,7 +269,7 @@ else if (is_object($fichinter)) { $head=fichinter_prepare_head($fichinter); - dol_fiche_head($head, 'resources', $langs->trans("InterventionCard"),0,'intervention'); + dol_fiche_head($head, 'resource', $langs->trans("InterventionCard"),0,'intervention'); // Affichage fiche action en mode visu print ''; From b0579cc39b8a8b1574e867c6a5ccb2c43e08867d Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 18:05:00 +0100 Subject: [PATCH 038/834] Update 3.9.0-4.0.0.sql --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 352d950ef02..d8e42cff15f 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -50,7 +50,4 @@ ALTER TABLE llx_resource ADD COLUMN fk_statut smallint NOT NULL DEFAULT '0 ALTER TABLE llx_resource ADD COLUMN import_key varchar(14); ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); -ALTER TABLE llx_element_resources ADD COLUMN dateo date; -- date start of using ressource -ALTER TABLE llx_element_resources ADD COLUMN datee date; -- date end of using ressource -ALTER TABLE llx_element_resources ADD COLUMN datet date; -- date terminaison of using ressource ALTER TABLE llx_element_resources ADD COLUMN duree real; -- total duration of using ressource From cc8af4fba118481eb86eac8e840da86cc791a7ca Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 18:05:24 +0100 Subject: [PATCH 039/834] Update llx_element_resources.sql --- htdocs/install/mysql/tables/llx_element_resources.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/htdocs/install/mysql/tables/llx_element_resources.sql b/htdocs/install/mysql/tables/llx_element_resources.sql index 4fac28de4b1..d619626d3d7 100644 --- a/htdocs/install/mysql/tables/llx_element_resources.sql +++ b/htdocs/install/mysql/tables/llx_element_resources.sql @@ -26,9 +26,6 @@ CREATE TABLE llx_element_resources resource_type varchar(64), -- resource or user busy integer, mandatory integer, - dateo date, -- date start of using ressource - datee date, -- date end of using ressource - datet date, -- date terminaison of using ressource duree real, -- total duration of using ressource fk_user_create integer, tms timestamp From ea5b8a409c243ce45159792bcee21bbd80d0791e Mon Sep 17 00:00:00 2001 From: PeartreeG Date: Tue, 2 Feb 2016 18:37:54 +0100 Subject: [PATCH 040/834] little forget... --- htdocs/resource/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/resource/card.php b/htdocs/resource/card.php index 31fb6a955b1..59862a811ba 100644 --- a/htdocs/resource/card.php +++ b/htdocs/resource/card.php @@ -152,7 +152,7 @@ $formresource = new FormResource($db); if ( $object->fetch($id) > 0 ) { - $head=resourcePrepareHead($object); + $head=resource_prepare_head($object); if ($action == 'edit' ) From 5e2261c9bd53d45e8746fc2215533d3f26a422b1 Mon Sep 17 00:00:00 2001 From: abb Date: Sun, 14 Feb 2016 00:20:25 +0100 Subject: [PATCH 041/834] handle customer orders from supplier orders with id variable name correction Conflicts: htdocs/fourn/commande/card.php --- htdocs/fourn/commande/card.php | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index d7bd5d18696..dccdd9f1d69 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -941,9 +941,17 @@ if (empty($reshook)) // If creation from another object of another module (Example: origin=propal, originid=1) if (! empty($origin) && ! empty($originid)) { - $element = 'supplier_proposal'; - $subelement = 'supplier_proposal'; - + if ($origin == 'order' || $origin == 'commande') { + $element = $subelement = 'commande'; + } + elseif ($origin =='supplier_proposal'){ + $element = 'supplier_proposal'; + $subelement = 'supplier_proposal'; + } + else { + $element = 'comm/askpricesupplier'; + $subelement = 'askpricesupplier'; + } $object->origin = $origin; $object->origin_id = $originid; @@ -1003,18 +1011,17 @@ if (empty($reshook)) $lines[$i]->fetch_optionals($lines[$i]->rowid); $array_option = $lines[$i]->array_options; } - - $res = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty); - /*if ($productsupplier->id > 0) - { - $res = $productsupplier->fetch($productsupplier->id); - }*/ - + + $idprod = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty); + $res = $productsupplier->fetch($idprod); + $soc=new societe($db); + $soc->fetch($socid); + $tva_tx=($origin=="commande")?get_default_tva($soc,$mysoc,$lines[$i]->fk_product,$idprod):$lines[$i]->tva_tx; $result = $object->addline( $desc, $lines[$i]->subprice, $lines[$i]->qty, - $lines[$i]->tva_tx, + $tva_tx, $lines[$i]->localtax1_tx, $lines[$i]->localtax2_tx, $lines[$i]->fk_product, From 4cbc1539b99dcfb3460fc1e816d508bbffd44360 Mon Sep 17 00:00:00 2001 From: Bahfir Abbes Date: Fri, 26 Feb 2016 22:48:01 +0100 Subject: [PATCH 042/834] Fix: displays error when the supplier price does not exist. --- htdocs/fourn/commande/card.php | 55 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index dccdd9f1d69..48a32c15f44 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -1012,33 +1012,34 @@ if (empty($reshook)) $array_option = $lines[$i]->array_options; } - $idprod = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty); - $res = $productsupplier->fetch($idprod); - $soc=new societe($db); - $soc->fetch($socid); - $tva_tx=($origin=="commande")?get_default_tva($soc,$mysoc,$lines[$i]->fk_product,$idprod):$lines[$i]->tva_tx; - $result = $object->addline( - $desc, - $lines[$i]->subprice, - $lines[$i]->qty, - $tva_tx, - $lines[$i]->localtax1_tx, - $lines[$i]->localtax2_tx, - $lines[$i]->fk_product, - $productsupplier->product_fourn_price_id, - $productsupplier->ref_fourn, - $lines[$i]->remise_percent, - 'HT', - 0, - $lines[$i]->product_type, - '', - '', - null, - null, - array(), - $lines[$i]->fk_unit - ); - + $result = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty); + if ($result>0) { + $productsupplier->fetch($productsupplier->id); + $soc=new societe($db); + $soc->fetch($socid); + $tva_tx=($origin=="commande")?get_default_tva($soc,$mysoc,$lines[$i]->fk_product,$idprod):$lines[$i]->tva_tx; + $result = $object->addline( + $desc, + $lines[$i]->subprice, + $lines[$i]->qty, + $tva_tx, + $lines[$i]->localtax1_tx, + $lines[$i]->localtax2_tx, + $lines[$i]->fk_product, + $productsupplier->product_fourn_price_id, + $productsupplier->ref_fourn, + $lines[$i]->remise_percent, + 'HT', + 0, + $lines[$i]->product_type, + '', + '', + null, + null, + array(), + $lines[$i]->fk_unit + ); + } if ($result < 0) { $error++; break; From 0166d2ec3a374da8035fd7ab31f36f62c19c0df9 Mon Sep 17 00:00:00 2001 From: Benlo Date: Mon, 7 Mar 2016 17:49:26 +0100 Subject: [PATCH 043/834] FR4763 add possibility to export contacts/thirdparty categories --- .../core/modules/export/export_csv.modules.php | 2 +- .../core/modules/export/export_excel.modules.php | 2 +- .../core/modules/export/export_tsv.modules.php | 2 +- htdocs/core/modules/modSociete.class.php | 16 ++++++++++------ htdocs/exports/class/export.class.php | 6 +++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/htdocs/core/modules/export/export_csv.modules.php b/htdocs/core/modules/export/export_csv.modules.php index 3da80b1cc96..e8977c11789 100644 --- a/htdocs/core/modules/export/export_csv.modules.php +++ b/htdocs/core/modules/export/export_csv.modules.php @@ -240,7 +240,7 @@ class ExportCsv extends ModeleExports $this->col=0; foreach($array_selected_sorted as $code => $value) { - if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-'),'_',$code); + if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-','(',')'),'_',$code); else $alias=substr($code, strpos($code, ' as ') + 4); if (empty($alias)) dol_print_error('','Bad value for field with key='.$code.'. Try to redefine export.'); diff --git a/htdocs/core/modules/export/export_excel.modules.php b/htdocs/core/modules/export/export_excel.modules.php index ede00733695..91f78522973 100644 --- a/htdocs/core/modules/export/export_excel.modules.php +++ b/htdocs/core/modules/export/export_excel.modules.php @@ -319,7 +319,7 @@ class ExportExcel extends ModeleExports foreach($array_selected_sorted as $code => $value) { - if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-'),'_',$code); + if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-','(',')'),'_',$code); else $alias=substr($code, strpos($code, ' as ') + 4); if (empty($alias)) dol_print_error('','Bad value for field with code='.$code.'. Try to redefine export.'); $newvalue=$objp->$alias; diff --git a/htdocs/core/modules/export/export_tsv.modules.php b/htdocs/core/modules/export/export_tsv.modules.php index dad3a4b67f9..2945c5095b8 100644 --- a/htdocs/core/modules/export/export_tsv.modules.php +++ b/htdocs/core/modules/export/export_tsv.modules.php @@ -215,7 +215,7 @@ class ExportTsv extends ModeleExports $this->col=0; foreach($array_selected_sorted as $code => $value) { - if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-'),'_',$code); + if (strpos($code,' as ') == 0) $alias=str_replace(array('.','-','(',')'),'_',$code); else $alias=substr($code, strpos($code, ' as ') + 4); if (empty($alias)) dol_print_error('','Bad value for field with code='.$code.'. Try to redefine export.'); diff --git a/htdocs/core/modules/modSociete.class.php b/htdocs/core/modules/modSociete.class.php index f2ce7ebf0da..7a3224dea7a 100644 --- a/htdocs/core/modules/modSociete.class.php +++ b/htdocs/core/modules/modSociete.class.php @@ -253,13 +253,13 @@ class modSociete extends DolibarrModules $this->export_label[$r]='ExportDataset_company_1'; $this->export_icon[$r]='company'; $this->export_permission[$r]=array(array("societe","export")); - $this->export_fields_array[$r]=array('s.rowid'=>"Id",'s.nom'=>"Name",'s.status'=>"Status",'s.client'=>"Customer",'s.fournisseur'=>"Supplier",'s.datec'=>"DateCreation",'s.tms'=>"DateLastModification",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode",'s.code_compta'=>"AccountancyCode",'s.code_compta_fournisseur'=>"SupplierAccountancyCode",'s.address'=>"Address",'s.zip'=>"Zip",'s.town'=>"Town",'d.nom'=>'State','c.label'=>"Country",'c.code'=>"CountryCode",'s.phone'=>"Phone",'s.fax'=>"Fax",'s.url'=>"Url",'s.email'=>"Email",'s.default_lang'=>"DefaultLang",'s.siren'=>"ProfId1",'s.siret'=>"ProfId2",'s.ape'=>"ProfId3",'s.idprof4'=>"ProfId4",'s.idprof5'=>"ProfId5",'s.idprof6'=>"ProfId6",'s.tva_intra'=>"VATIntraShort",'s.capital'=>"Capital",'s.note_private'=>"NotePrivate",'s.note_public'=>"NotePublic",'t.libelle'=>"ThirdPartyType",'ce.code'=>"Staff","cfj.libelle"=>"JuridicalStatus",'s.fk_prospectlevel'=>'ProspectLevel','st.code'=>'ProspectStatus'); + $this->export_fields_array[$r]=array('s.rowid'=>"Id",'s.nom'=>"Name",'s.status'=>"Status",'s.client'=>"Customer",'s.fournisseur'=>"Supplier",'s.datec'=>"DateCreation",'s.tms'=>"DateLastModification",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode",'s.code_compta'=>"AccountancyCode",'s.code_compta_fournisseur'=>"SupplierAccountancyCode",'s.address'=>"Address",'s.zip'=>"Zip",'s.town'=>"Town",'d.nom'=>'State','c.label'=>"Country",'c.code'=>"CountryCode",'s.phone'=>"Phone",'s.fax'=>"Fax",'s.url'=>"Url",'s.email'=>"Email",'s.default_lang'=>"DefaultLang",'s.siren'=>"ProfId1",'s.siret'=>"ProfId2",'s.ape'=>"ProfId3",'s.idprof4'=>"ProfId4",'s.idprof5'=>"ProfId5",'s.idprof6'=>"ProfId6",'s.tva_intra'=>"VATIntraShort",'s.capital'=>"Capital",'s.note_private'=>"NotePrivate",'s.note_public'=>"NotePublic",'t.libelle'=>"ThirdPartyType",'ce.code'=>"Staff","cfj.libelle"=>"JuridicalStatus",'s.fk_prospectlevel'=>'ProspectLevel','st.code'=>'ProspectStatus','group_concat(cat.label)'=>'Categories'); if (! empty($conf->global->SOCIETE_USEPREFIX)) $this->export_fields_array[$r]['s.prefix']='Prefix'; $keyforselect='societe'; $keyforelement='company'; $keyforaliasextra='extra'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; $this->export_fields_array[$r]+=array('u.login'=>'SaleRepresentativeLogin','u.firstname'=>'SaleRepresentativeFirstname', 'u.lastname'=>'SaleRepresentativeLastname'); //$this->export_TypeFields_array[$r]=array('s.rowid'=>"List:societe:nom",'s.nom'=>"Text",'s.status'=>"Text",'s.client'=>"Boolean",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",'s.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.address'=>"Text",'s.zip'=>"Text",'s.town'=>"Text",'c.label'=>"List:c_country:label:label",'c.code'=>"Text",'s.phone'=>"Text",'s.fax'=>"Text",'s.url'=>"Text",'s.email'=>"Text",'s.default_lang'=>"Text",'s.siret'=>"Text",'s.siren'=>"Text",'s.ape'=>"Text",'s.idprof4'=>"Text",'s.idprof5'=>"Text",'s.idprof6'=>"Text",'s.tva_intra'=>"Text",'s.capital'=>"Numeric",'s.note'=>"Text",'t.libelle'=>"Text",'ce.code'=>"List:c_effectif:libelle:code","cfj.libelle"=>"Text",'s.fk_prospectlevel'=>'List:c_prospectlevel:label:code','s.fk_stcomm'=>'List:c_stcomm:libelle:code','d.nom'=>'List:c_departements:nom:rowid'); - $this->export_TypeFields_array[$r]=array('s.nom'=>"Text",'s.status'=>"Numeric",'s.client'=>"Numeric",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",'s.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.code_compta'=>"Text",'s.code_compta_fournisseur'=>"Text",'s.address'=>"Text",'s.zip'=>"Text",'s.town'=>"Text",'c.label'=>"List:c_country:label:label",'c.code'=>"Text",'s.phone'=>"Text",'s.fax'=>"Text",'s.url'=>"Text",'s.email'=>"Text",'s.default_lang'=>"Text",'s.siret'=>"Text",'s.siren'=>"Text",'s.ape'=>"Text",'s.idprof4'=>"Text",'s.idprof5'=>"Text",'s.idprof6'=>"Text",'s.tva_intra'=>"Text",'s.capital'=>"Numeric",'s.note_private'=>"Text",'s.note_public'=>"Text",'t.libelle'=>"Text",'ce.code'=>"List:c_effectif:libelle:code","cfj.libelle"=>"Text",'s.fk_prospectlevel'=>'List:c_prospectlevel:label:code','st.code'=>'List:c_stcomm:libelle:code','d.nom'=>'Text','u.login'=>'Text','u.firstname'=>'Text','u.lastname'=>'Text'); + $this->export_TypeFields_array[$r]=array('s.nom'=>"Text",'s.status'=>"Numeric",'s.client'=>"Numeric",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",'s.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.code_compta'=>"Text",'s.code_compta_fournisseur'=>"Text",'s.address'=>"Text",'s.zip'=>"Text",'s.town'=>"Text",'c.label'=>"List:c_country:label:label",'c.code'=>"Text",'s.phone'=>"Text",'s.fax'=>"Text",'s.url'=>"Text",'s.email'=>"Text",'s.default_lang'=>"Text",'s.siret'=>"Text",'s.siren'=>"Text",'s.ape'=>"Text",'s.idprof4'=>"Text",'s.idprof5'=>"Text",'s.idprof6'=>"Text",'s.tva_intra'=>"Text",'s.capital'=>"Numeric",'s.note_private'=>"Text",'s.note_public'=>"Text",'t.libelle'=>"Text",'ce.code'=>"List:c_effectif:libelle:code","cfj.libelle"=>"Text",'s.fk_prospectlevel'=>'List:c_prospectlevel:label:code','st.code'=>'List:c_stcomm:libelle:code','d.nom'=>'Text','u.login'=>'Text','u.firstname'=>'Text','u.lastname'=>'Text','group_concat(cat.label)'=>'Text'); $this->export_entities_array[$r]=array('u.login'=>'user','u.firstname'=>'user','u.lastname'=>'user'); // We define here only fields that use another picto $this->export_examplevalues_array[$r]=array('s.client'=>'0 (no customer no prospect)/1 (customer)/2 (prospect)/3 (customer and prospect)','s.fournisseur'=>'0 (not a supplier) or 1 (supplier)'); $this->export_sql_start[$r]='SELECT DISTINCT '; @@ -272,7 +272,9 @@ class modSociete extends DolibarrModules $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_departements as d ON s.fk_departement = d.rowid'; $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_stcomm as st ON s.fk_stcomm = st.id'; $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid LEFT JOIN '.MAIN_DB_PREFIX.'user as u ON sc.fk_user = u.rowid'; - $this->export_sql_end[$r] .=' WHERE s.entity IN ('.getEntity('societe', 1).')'; + $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_societe as cs on s.rowid=cs.fk_soc '; + $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'categorie as cat on cs.fk_categorie=cat.rowid'; + $this->export_sql_end[$r] .=' WHERE s.entity IN ('.getEntity('societe', 1).') GROUP BY s.rowid'; // Export list of contacts and attributes $r++; @@ -280,9 +282,9 @@ class modSociete extends DolibarrModules $this->export_label[$r]='ExportDataset_company_2'; $this->export_icon[$r]='contact'; $this->export_permission[$r]=array(array("societe","contact","export")); - $this->export_fields_array[$r]=array('c.rowid'=>"IdContact",'c.civility'=>"CivilityCode",'c.lastname'=>'Lastname','c.firstname'=>'Firstname','c.poste'=>'PostOrFunction','c.datec'=>"DateCreation",'c.tms'=>"DateLastModification",'c.priv'=>"ContactPrivate",'c.address'=>"Address",'c.zip'=>"Zip",'c.town'=>"Town",'d.nom'=>'State','co.label'=>"Country",'co.code'=>"CountryCode",'c.phone'=>"Phone",'c.fax'=>"Fax",'c.phone_mobile'=>"Mobile",'c.email'=>"EMail",'s.rowid'=>"IdCompany",'s.nom'=>"CompanyName",'s.status'=>"Status",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode", 's.client'=>'Customer','s.fournisseur'=>'Supplier'); + $this->export_fields_array[$r]=array('c.rowid'=>"IdContact",'c.civility'=>"CivilityCode",'c.lastname'=>'Lastname','c.firstname'=>'Firstname','c.poste'=>'PostOrFunction','c.datec'=>"DateCreation",'c.tms'=>"DateLastModification",'c.priv'=>"ContactPrivate",'c.address'=>"Address",'c.zip'=>"Zip",'c.town'=>"Town",'d.nom'=>'State','co.label'=>"Country",'co.code'=>"CountryCode",'c.phone'=>"Phone",'c.fax'=>"Fax",'c.phone_mobile'=>"Mobile",'c.email'=>"EMail",'s.rowid'=>"IdCompany",'s.nom'=>"CompanyName",'s.status'=>"Status",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode", 's.client'=>'Customer','s.fournisseur'=>'Supplier','group_concat(cat.label)'=>'Categories'); $this->export_examplevalues_array[$r]=array('s.client'=>'0 (no customer no prospect)/1 (customer)/2 (prospect)/3 (customer and prospect)','s.fournisseur'=>'0 (not a supplier) or 1 (supplier)'); - $this->export_TypeFields_array[$r]=array('c.civility'=>"List:c_civility:label:code",'c.lastname'=>'Text','c.firstname'=>'Text','c.poste'=>'Text','c.datec'=>"Date",'c.priv'=>"Boolean",'c.address'=>"Text",'c.zip'=>"Text",'c.town'=>"Text",'d.nom'=>'Text','co.label'=>"List:c_country:label:rowid",'co.code'=>"Text",'c.phone'=>"Text",'c.fax'=>"Text",'c.email'=>"Text",'s.rowid'=>"List:societe:nom",'s.nom'=>"Text",'s.status'=>"Status",'s.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.client'=>"Text",'s.fournisseur'=>"Text"); + $this->export_TypeFields_array[$r]=array('c.civility'=>"List:c_civility:label:code",'c.lastname'=>'Text','c.firstname'=>'Text','c.poste'=>'Text','c.datec'=>"Date",'c.priv'=>"Boolean",'c.address'=>"Text",'c.zip'=>"Text",'c.town'=>"Text",'d.nom'=>'Text','co.label'=>"List:c_country:label:rowid",'co.code'=>"Text",'c.phone'=>"Text",'c.fax'=>"Text",'c.email'=>"Text",'s.rowid'=>"List:societe:nom",'s.nom'=>"Text",'s.status'=>"Status",'s.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.client'=>"Text",'s.fournisseur'=>"Text",'group_concat(cat.label)'=>'Text'); $this->export_entities_array[$r]=array('s.rowid'=>"company",'s.nom'=>"company", 's.status'=>'company', 's.code_client'=>"company",'s.code_fournisseur'=>"company", 's.client'=>"company", 's.fournisseur'=>"company"); // We define here only fields that use another picto if (empty($conf->fournisseur->enabled)) { @@ -297,7 +299,9 @@ class modSociete extends DolibarrModules $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_departements as d ON c.fk_departement = d.rowid'; $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON c.fk_pays = co.rowid'; $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople_extrafields as extra ON extra.fk_object = c.rowid'; - $this->export_sql_end[$r] .=' WHERE c.entity IN ('.getEntity("societe", 1).')'; + $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_contact as cs on s.rowid=cs.fk_socpeople '; + $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'categorie as cat on cs.fk_categorie=cat.rowid'; + $this->export_sql_end[$r] .=' WHERE c.entity IN ('.getEntity("societe", 1).') GROUP BY s.rowid'; // Imports diff --git a/htdocs/exports/class/export.class.php b/htdocs/exports/class/export.class.php index 7912b4f189f..6632f050550 100644 --- a/htdocs/exports/class/export.class.php +++ b/htdocs/exports/class/export.class.php @@ -225,7 +225,7 @@ class Export else $i++; if (strpos($key, ' as ')===false) { - $newfield=$key.' as '.str_replace(array('.', '-'),'_',$key); + $newfield=$key.' as '.str_replace(array('.', '-','(',')'),'_',$key); } else { $newfield=$key; } @@ -587,14 +587,14 @@ class Export if ($this->array_export_special[$indice][$key]=='NULLIFNEG') { //$alias=$this->array_export_alias[$indice][$key]; - $alias=str_replace(array('.', '-'),'_',$key); + $alias=str_replace(array('.', '-','(',')'),'_',$key); if ($objp->$alias < 0) $objp->$alias=''; } // Operation ZEROIFNEG if ($this->array_export_special[$indice][$key]=='ZEROIFNEG') { //$alias=$this->array_export_alias[$indice][$key]; - $alias=str_replace(array('.', '-'),'_',$key); + $alias=str_replace(array('.', '-','(',')'),'_',$key); if ($objp->$alias < 0) $objp->$alias='0'; } } From da17e6e676de53dd5dafcc2c2746aa7b136e1f36 Mon Sep 17 00:00:00 2001 From: phf Date: Thu, 10 Mar 2016 23:52:06 +0100 Subject: [PATCH 044/834] New add sync with api to create/update all rates --- htdocs/admin/multicurrency.php | 103 ++++++++++++++++-- .../class/multicurrency.class.php | 11 ++ 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/htdocs/admin/multicurrency.php b/htdocs/admin/multicurrency.php index b1ad347d8b7..afb396a0217 100644 --- a/htdocs/admin/multicurrency.php +++ b/htdocs/admin/multicurrency.php @@ -31,6 +31,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/multicurrency.lib.php'; require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php'; + // Translations $langs->load("multicurrency"); @@ -41,10 +42,17 @@ if (! $user->admin) { // Parameters $action = GETPOST('action', 'alpha'); - +$sync_response = GETPOST('sync_response'); /* * Actions */ +if (!empty($sync_response)) +{ + $sync_response = json_decode($sync_response); + MultiCurrency::syncRates($sync_response); + exit; +} + if (preg_match('/set_(.*)/',$action,$reg)) { $code=$reg[1]; @@ -64,7 +72,7 @@ if (preg_match('/del_(.*)/',$action,$reg)) $code=$reg[1]; if (dolibarr_del_const($db, $code, 0) > 0) { - Header("Location: ".$_SERVER["PHP_SELF"]); + header("Location: ".$_SERVER["PHP_SELF"]); exit; } else @@ -224,8 +232,8 @@ print ''; print ''; @@ -244,16 +252,95 @@ print ''; print ''; print '
     '; print '
    '; print ''; -print ''; -print ' '; +print ''; +print ' '; print ''; print '
    '; print '
    '; - -print ''; - print '
    '; + +print ''; + + print ''; print ''; print ''."\n"; print ''; -print ''."\n"; +print ''."\n"; $var=!$var; print ''; diff --git a/htdocs/multicurrency/class/multicurrency.class.php b/htdocs/multicurrency/class/multicurrency.class.php index fd6da3816ea..c78e7784ad1 100644 --- a/htdocs/multicurrency/class/multicurrency.class.php +++ b/htdocs/multicurrency/class/multicurrency.class.php @@ -520,6 +520,17 @@ class MultiCurrency extends CommonObject return false; } + + /** + * Sync rates from api + * + * @param array $response array of reponse from api to sync dolibarr rates + */ + public static function syncRates($response) + { + $TRate = $response['quotes']; + $timestamp = $response['timestamp']; + } } /** From a45ffeecca5eba9e050b2c36207fc1c923897a26 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 11 Mar 2016 18:21:57 +0100 Subject: [PATCH 045/834] Missing changelog --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2144fe54620..dbc858dc1d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,18 @@ English Dolibarr ChangeLog -------------------------------------------------------------- + +***** ChangeLog for 3.5.8 compared to 3.5.7 ***** +FIX: #4291 Correctly filter external calendar GETPOSTs +FIX: bad calculation for stock value +FIX: bad stock valo +FIX: change order date on clone (as everywhere else) +FIX: CVE CVE-2015-8685 +FIX: The hours of date filter aren't correct +FIX: #3442 Remove useless syslog +FIX: #3448 Pass expected date format +FIX: #3471 3.5 Rounding issue when dispatching non-integer + ***** ChangeLog for 3.5.7 compared to 3.5.6 ***** Fix: Paypal link were broken due to SSL v3 closed. Fix: [ bug #1769 ] Error when installing to a PostgreSQL DB that contains numbers From 32d0f9a8d771685d962e405b9f0c76d9a586df19 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 11 Mar 2016 18:27:04 +0100 Subject: [PATCH 046/834] Prepare 3.6.7 --- htdocs/filefunc.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/filefunc.inc.php b/htdocs/filefunc.inc.php index d1784fb4863..3c1d1fdea0e 100644 --- a/htdocs/filefunc.inc.php +++ b/htdocs/filefunc.inc.php @@ -29,7 +29,7 @@ * \brief File that include conf.php file and commons lib like functions.lib.php */ -if (! defined('DOL_VERSION')) define('DOL_VERSION','3.6.6'); +if (! defined('DOL_VERSION')) define('DOL_VERSION','3.6.7'); if (! defined('EURO')) define('EURO',chr(128)); // Define syslog constants From e669dac3980f7da1e11d20a4b16d7ab0484497f3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 11 Mar 2016 18:28:39 +0100 Subject: [PATCH 047/834] Prepare 3.6.7 --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index fe638ab3902..9a0da671af3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ English Dolibarr ChangeLog -------------------------------------------------------------- +***** ChangeLog for 3.6.7 compared to 3.6.6 ***** +FIX: #4291 Correctly filter external calendar GETPOSTs +FIX: CVE CVE-2015-8685 ***** ChangeLog for 3.6.6 compared to 3.6.5 ***** FIX: #3734 Do not show empty links of deleted source objects in stock movement list From 444fce0bd9ccecef83a3dbbb8ed2704efcbd264d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 11 Mar 2016 18:34:05 +0100 Subject: [PATCH 048/834] Prepare 3.7.4 --- ChangeLog | 17 +++++++++++++++++ htdocs/filefunc.inc.php | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index ce76d999605..b28b5a4be64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,23 @@ Upgrading to any other version or database system is abolutely required BEFORE t make a Dolibarr upgrade. +***** ChangeLog for 3.7.4 compared to 3.7.3 ***** +FIX: #3694 +FIX: #4239 +FIX: #4291 Correctly filter external calendar GETPOSTs +FIX: #4341 +FIX: #4414 Supplier invoices use FAC_FORCE_DATE_VALIDATION client invoices property +FIX: #4440 Wrong price is filled by Product::fetch into multiprices arrays +FIX: add missing global def for ttc column +FIX: Contrat card don't consider user permissions to show active/unactive service button +FIX: CVE CVE-2015-8685 +FIX: Email templates not compatible with Multicompany +Fix: for avoid division by 0 +FIX: ISSUE #4506 : make working the PROPAL_CLONE_ON_CREATE_PAGE hidden constant +FIX: $outputlangs is not defined (dolibarr 3.7, 3.8, 3.9) +FIX: sql injection even when code is on several lines +FIX: The third dashboard don't consider user permissions + ***** ChangeLog for 3.7.3 compared to 3.7.2 ***** FIX: #3734 Do not show empty links of deleted source objects in stock movement list FIX: #3890 Expected transactions bank account page, shows negative numbers diff --git a/htdocs/filefunc.inc.php b/htdocs/filefunc.inc.php index fc909577415..66b0582b17b 100644 --- a/htdocs/filefunc.inc.php +++ b/htdocs/filefunc.inc.php @@ -29,7 +29,7 @@ * \brief File that include conf.php file and commons lib like functions.lib.php */ -if (! defined('DOL_VERSION')) define('DOL_VERSION','3.7.3'); +if (! defined('DOL_VERSION')) define('DOL_VERSION','3.7.4'); if (! defined('EURO')) define('EURO',chr(128)); // Define syslog constants From 3134af5013984bb356716e9254891788f4312ef4 Mon Sep 17 00:00:00 2001 From: phf Date: Fri, 11 Mar 2016 23:42:02 +0100 Subject: [PATCH 049/834] Fix sync with currencylayer --- htdocs/admin/multicurrency.php | 84 ++++++++++------ .../class/multicurrency.class.php | 95 +++++++++++++++++-- 2 files changed, 142 insertions(+), 37 deletions(-) diff --git a/htdocs/admin/multicurrency.php b/htdocs/admin/multicurrency.php index afb396a0217..e4f4fc34301 100644 --- a/htdocs/admin/multicurrency.php +++ b/htdocs/admin/multicurrency.php @@ -225,32 +225,6 @@ print '' print ''; print ''; -$var=!$var; -print ''; -print ''; -print ''; -print ''; - -$var=!$var; -print ''; -print ''; -print ''; -print ''; - print '
    '.$langs->trans("Currencies").' '.$langs->trans("Rate").''.$langs->trans("Rate").' 
    '.$langs->transnoentitiesnoconv("multicurrency_appId").' '; -print '
    '; -print ''; -print ''; -print ' '; -print ''; -print '
    '; -print '
    '.$langs->transnoentitiesnoconv("multicurrency_currencyFromToRate").' '; -print '
    '; -print ''; -print ''; -print ' '; // CURRENCY_BASE - CURRENCY_ENTITY - ID_ENTITY -print ''; -print '
    '; -print '
    '; print '
    '; @@ -306,7 +280,8 @@ print ''; +$var=false; +print ''; +print ''; +print ''."\n"; +print ''; +print ''."\n"; + +$var=!$var; +print ''; +print ''; +print ''; +print ''; + +$var=!$var; +print ''; +print ''; +print ''; +print ''; + +$var=!$var; +print ''; +print ''; +print ''; +print ''; + +print '
    '.$langs->trans("CurrencyLayerAccount").' '.$langs->trans("Value").' 
    '.$langs->transnoentitiesnoconv("multicurrency_appId").' '; +print '
    '; +print ''; +print ''; +print ' '; +print ''; +print '
    '; +print '
    '.$langs->transnoentitiesnoconv("multicurrency_appCurrencySource").' '; +print '
    '; +print ''; +print ''; +print ' '; // Default: USD +print ''; +print '
    '; +print '
    '.$langs->transnoentitiesnoconv("multicurrency_alternateCurrencySource").' '; +print '
    '; +print ''; +print ''; +print ' '; // Example: EUR +print ''; +print '
    '; +print '
    '; +print '
    '; print ''; print ''; print ''."\n"; print ''; -print ''."\n"; +print ''."\n"; $var=!$var; print ''; diff --git a/htdocs/multicurrency/class/multicurrency.class.php b/htdocs/multicurrency/class/multicurrency.class.php index c78e7784ad1..36c392b5930 100644 --- a/htdocs/multicurrency/class/multicurrency.class.php +++ b/htdocs/multicurrency/class/multicurrency.class.php @@ -208,7 +208,7 @@ class MultiCurrency extends CommonObject $this->errors[] = 'Error ' . $this->db->lasterror(); dol_syslog('Currency::fetch ' . join(',', $this->errors), LOG_ERR); - return - 1; + return -1; } } @@ -383,7 +383,7 @@ class MultiCurrency extends CommonObject * * @param double $rate rate value * - * @return bool false if KO, true if OK + * @return int -1 if KO, 1 if OK */ public function addRate($rate) { @@ -402,6 +402,40 @@ class MultiCurrency extends CommonObject } } + /** + * Try get label of code in llx_currency then add rate + * + * @param string $code currency code + * @param double $rate new rate + * + * @return int -1 if KO, 1 if OK, 2 if label found and OK + */ + function addRateFromDolibarr($code, $rate) + { + global $db, $user; + + $currency = new MultiCurrency($db); + $currency->code = $code; + $currency->name = $code; + + $sql = 'SELECT label FROM '.MAIN_DB_PREFIX.'c_currencies WHERE code_iso = "'.$db->escape($code).'"'; + $resql = $db->query($sql); + if ($resql && ($line = $db->fetch_object($resql))) + { + $currency->name = $line->label; + } + + if ($currency->create($user) > 0) + { + $currency->addRate($rate); + + if (!empty($line)) return 2; + else return 1; + } + + return -1; + } + /** * Update rate in database * @@ -520,7 +554,37 @@ class MultiCurrency extends CommonObject return false; } - + + /** + * With free account we can't set source then recalcul all rates to force another source + * + * @param stdClass $TRate Object containing all currencies rates + * @return -1 if KO, 0 if nothing, 1 if OK + */ + public static function recalculRates(&$TRate) + { + global $conf; + + if (!empty($conf->global->MULTICURRENCY_ALTERNATE_SOURCE)) + { + $alternate_source = 'USD'.$conf->global->MULTICURRENCY_ALTERNATE_SOURCE; + if (!empty($TRate->{$alternate_source})) + { + $coef = $TRate->USDUSD / $TRate->{$alternate_source}; + foreach ($TRate as $attr => &$rate) + { + $rate *= $coef; + } + + return 1; + } + + return -1; // Alternate souce not found + } + + return 0; // Nothing to do + } + /** * Sync rates from api * @@ -528,8 +592,27 @@ class MultiCurrency extends CommonObject */ public static function syncRates($response) { - $TRate = $response['quotes']; - $timestamp = $response['timestamp']; + global $db,$conf; + + $TRate = $response->quotes; + $timestamp = $response->timestamp; + + if (self::recalculRates($TRate) >= 0) + { + foreach ($TRate as $currency_code => $rate) + { + $code = substr($currency_code, 3, 3); + $obj = new MultiCurrency($db); + if ($obj->fetch(null, $code) > 0) + { + $obj->updateRate($rate); + } + else + { + self::addRateFromDolibarr($code, $rate); + } + } + } } } @@ -724,7 +807,7 @@ class CurrencyRate extends CommonObjectLine if ($error) { $this->db->rollback(); - return - 1 * $error; + return -1 * $error; } else { $this->db->commit(); From 0a533660b8834d83a5738e42c6da4e33c4bfdddb Mon Sep 17 00:00:00 2001 From: phf Date: Sat, 12 Mar 2016 21:17:09 +0100 Subject: [PATCH 050/834] Fix travis phpcs --- htdocs/compta/prelevement/create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/compta/prelevement/create.php b/htdocs/compta/prelevement/create.php index 33d02bff05b..c2607d5b83d 100644 --- a/htdocs/compta/prelevement/create.php +++ b/htdocs/compta/prelevement/create.php @@ -65,7 +65,7 @@ if ($action == 'create') $result=$bprev->create($conf->global->PRELEVEMENT_CODE_BANQUE, $conf->global->PRELEVEMENT_CODE_GUICHET); if ($result < 0) { - setEventMessages($bprev->error , $bprev->errors, 'errors'); + setEventMessages($bprev->error, $bprev->errors, 'errors'); } if ($result == 0) { From 28391a4e4de8b75d7a131b834cd6dd80d61a8a70 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Tue, 15 Mar 2016 11:57:53 +0100 Subject: [PATCH 051/834] Last version of JQuery 1.12 and DataTables Componenent with working export Excel,CVS,PDF, etc.. --- htdocs/includes/jquery/js/jquery.js | 4548 ++++++++++------- htdocs/includes/jquery/js/jquery.min.js | 10 +- .../jquery/plugins/datatables/Readme.md | 12 +- .../AutoFill/js/dataTables.autoFill.js | 1645 +++--- .../AutoFill/js/dataTables.autoFill.min.js | 41 +- .../extensions/ColReorder/License.txt | 2 + .../extensions/ColReorder/Readme.md | 36 +- .../ColReorder/js/dataTables.colReorder.js | 514 +- .../js/dataTables.colReorder.min.js | 49 +- .../extensions/FixedColumns/License.txt | 2 + .../extensions/FixedColumns/Readme.md | 30 +- .../js/dataTables.fixedColumns.js | 346 +- .../js/dataTables.fixedColumns.min.js | 61 +- .../FixedHeader/js/dataTables.fixedHeader.js | 1450 ++---- .../js/dataTables.fixedHeader.min.js | 43 +- .../KeyTable/js/dataTables.keyTable.js | 1973 +++---- .../KeyTable/js/dataTables.keyTable.min.js | 32 +- .../extensions/Responsive/License.txt | 2 + .../extensions/Responsive/Readme.md | 41 + .../Responsive/js/dataTables.responsive.js | 711 ++- .../js/dataTables.responsive.min.js | 40 +- .../Scroller/js/dataTables.scroller.js | 313 +- .../Scroller/js/dataTables.scroller.min.js | 47 +- .../jquery/plugins/datatables/license.txt | 2 +- .../media/css/jquery.dataTables.css | 48 +- .../media/css/jquery.dataTables.min.css | 2 +- .../datatables/media/js/jquery.dataTables.js | 1049 ++-- .../media/js/jquery.dataTables.min.js | 324 +- .../plugins/datatables/media/js/jquery.js | 10 +- htdocs/main.inc.php | 15 +- htdocs/public/test/test_arrays.php | 68 +- 31 files changed, 7292 insertions(+), 6174 deletions(-) diff --git a/htdocs/includes/jquery/js/jquery.js b/htdocs/includes/jquery/js/jquery.js index 6feb11086f4..4855adcbf14 100644 --- a/htdocs/includes/jquery/js/jquery.js +++ b/htdocs/includes/jquery/js/jquery.js @@ -1,27 +1,27 @@ /*! - * jQuery JavaScript Library v1.11.3 + * jQuery JavaScript Library v1.12.0 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-04-28T16:19Z + * Date: 2016-01-08T19:56Z */ (function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (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 #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { @@ -37,14 +37,15 @@ // Pass this if window is not defined yet }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { -// Can't do this because several apps including ASP.NET trace +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// - +//"use strict"; var deletedIds = []; +var document = window.document; + var slice = deletedIds.slice; var concat = deletedIds.concat; @@ -64,10 +65,11 @@ var support = {}; var - version = "1.11.3", + version = "1.12.0", // Define a local copy of jQuery jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); @@ -87,6 +89,7 @@ var }; jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used jquery: version, @@ -130,16 +133,14 @@ jQuery.fn = jQuery.prototype = { }, // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); + each: function( callback ) { + return jQuery.each( this, callback ); }, map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { return callback.call( elem, i, elem ); - })); + } ) ); }, slice: function() { @@ -157,11 +158,11 @@ jQuery.fn = jQuery.prototype = { eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { - return this.prevObject || this.constructor(null); + return this.prevObject || this.constructor(); }, // For internal use only. @@ -173,7 +174,7 @@ jQuery.fn = jQuery.prototype = { jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, - target = arguments[0] || {}, + target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; @@ -188,7 +189,7 @@ jQuery.extend = jQuery.fn.extend = function() { } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { target = {}; } @@ -199,8 +200,10 @@ jQuery.extend = jQuery.fn.extend = function() { } for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { + if ( ( options = arguments[ i ] ) != null ) { + // Extend the base object for ( name in options ) { src = target[ name ]; @@ -212,13 +215,15 @@ jQuery.extend = jQuery.fn.extend = function() { } // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + if ( copyIsArray ) { copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; + clone = src && jQuery.isArray( src ) ? src : []; } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; + clone = src && jQuery.isPlainObject( src ) ? src : {}; } // Never move original objects, clone them @@ -236,7 +241,8 @@ jQuery.extend = jQuery.fn.extend = function() { return target; }; -jQuery.extend({ +jQuery.extend( { + // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), @@ -253,11 +259,11 @@ jQuery.extend({ // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { - return jQuery.type(obj) === "function"; + return jQuery.type( obj ) === "function"; }, isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; + return jQuery.type( obj ) === "array"; }, isWindow: function( obj ) { @@ -266,11 +272,13 @@ jQuery.extend({ }, isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN // adding 1 corrects loss of precision from parseFloat (#15100) - return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; }, isEmptyObject: function( obj ) { @@ -287,25 +295,27 @@ jQuery.extend({ // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { + // Not own constructor property must be Object if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Support: IE<9 // Handle iteration over inherited properties before own properties. - if ( support.ownLast ) { + if ( !support.ownFirst ) { for ( key in obj ) { return hasOwn.call( obj, key ); } @@ -323,20 +333,20 @@ jQuery.extend({ return obj + ""; } return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : + class2type[ toString.call( obj ) ] || "object" : typeof obj; }, - // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); + window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation } )( data ); } }, @@ -351,49 +361,20 @@ jQuery.extend({ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); + each: function( obj, callback ) { + var length, i = 0; - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } - - // A special, fast, case for the most common use of each } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } } @@ -413,7 +394,7 @@ jQuery.extend({ var ret = results || []; if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { + if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr @@ -438,6 +419,7 @@ jQuery.extend({ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; @@ -460,7 +442,7 @@ jQuery.extend({ // Support: IE<9 // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) if ( len !== len ) { - while ( second[j] !== undefined ) { + while ( second[ j ] !== undefined ) { first[ i++ ] = second[ j++ ]; } } @@ -491,14 +473,13 @@ jQuery.extend({ // arg is for internal usage only map: function( elems, callback, arg ) { - var value, + var length, value, i = 0, - length = elems.length, - isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their new values - if ( isArray ) { + if ( isArrayLike( elems ) ) { + length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); @@ -561,43 +542,50 @@ jQuery.extend({ // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support -}); +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; +} +/* jshint ignore: end */ // Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); +} ); -function isArraylike( obj ) { +function isArrayLike( obj ) { // Support: iOS 8.2 (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE - var length = "length" in obj && obj.length, + var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } - if ( obj.nodeType === 1 && length ) { - return true; - } - return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.2.0-pre + * Sizzle CSS Selector Engine v2.2.1 * http://sizzlejs.com/ * - * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2014-12-16 + * Date: 2015-10-17 */ (function( window ) { @@ -665,25 +653,21 @@ var i, // Regular expressions - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", - pseudos = ":(" + characterEncoding + ")(?:\\((" + + pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + @@ -706,9 +690,9 @@ var i, ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + @@ -786,103 +770,129 @@ try { } function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; - context = context || document; results = results || []; - nodeType = context.nodeType; + // Return early from calls with invalid selector or context if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } - if ( !seed && documentIsHTML ) { + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { - // Try to shortcut find operations when possible (e.g., not under DocumentFragment) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { results.push( elem ); return results; } - } else { - return results; } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType !== 1 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } } } } @@ -895,7 +905,7 @@ function Sizzle( selector, context, results, seed ) { /** * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ @@ -950,7 +960,7 @@ function assert( fn ) { */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), - i = attrs.length; + i = arr.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; @@ -1063,33 +1073,29 @@ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, parent, doc = node ? node.ownerDocument || node : preferredDoc; - // If no document and documentElement is available, return + // Return early if doc is invalid or already selected if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } - // Set our document + // Update global variables document = doc; - docElem = doc.documentElement; - parent = doc.defaultView; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 if ( parent.addEventListener ) { parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only } else if ( parent.attachEvent ) { parent.attachEvent( "onunload", unloadHandler ); } } - /* Support tests - ---------------------------------------------------------------------- */ - documentIsHTML = !isXML( doc ); - /* Attributes ---------------------------------------------------------------------- */ @@ -1106,12 +1112,12 @@ setDocument = Sizzle.setDocument = function( node ) { // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); + div.appendChild( document.createComment("") ); return !div.getElementsByTagName("*").length; }); // Support: IE<9 - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name @@ -1119,7 +1125,7 @@ setDocument = Sizzle.setDocument = function( node ) { // so use a roundabout getElementsByName test support.getById = assert(function( div ) { docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + return !document.getElementsByName || !document.getElementsByName( expando ).length; }); // ID find and filter @@ -1127,9 +1133,7 @@ setDocument = Sizzle.setDocument = function( node ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; + return m ? [ m ] : []; } }; Expr.filter["ID"] = function( id ) { @@ -1146,7 +1150,8 @@ setDocument = Sizzle.setDocument = function( node ) { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); return node && node.value === attrId; }; }; @@ -1186,7 +1191,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( documentIsHTML ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } }; @@ -1206,7 +1211,7 @@ setDocument = Sizzle.setDocument = function( node ) { // See http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { @@ -1216,7 +1221,7 @@ setDocument = Sizzle.setDocument = function( node ) { // since its presence should be enough // http://bugs.jquery.com/ticket/12359 docElem.appendChild( div ).innerHTML = "" + - "" + ""; // Support: IE8, Opera 11-12.16 @@ -1233,7 +1238,7 @@ setDocument = Sizzle.setDocument = function( node ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } - // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } @@ -1256,7 +1261,7 @@ setDocument = Sizzle.setDocument = function( node ) { assert(function( div ) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); + var input = document.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "name", "D" ); @@ -1304,7 +1309,7 @@ setDocument = Sizzle.setDocument = function( node ) { hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another - // Purposefully does not implement inclusive descendent + // Purposefully self-exclusive // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { @@ -1358,10 +1363,10 @@ setDocument = Sizzle.setDocument = function( node ) { (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } @@ -1389,8 +1394,8 @@ setDocument = Sizzle.setDocument = function( node ) { // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : + return a === document ? -1 : + b === document ? 1 : aup ? -1 : bup ? 1 : sortInput ? @@ -1427,7 +1432,7 @@ setDocument = Sizzle.setDocument = function( node ) { 0; }; - return doc; + return document; }; Sizzle.matches = function( expr, elements ) { @@ -1444,6 +1449,7 @@ Sizzle.matchesSelector = function( elem, expr ) { expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { @@ -1717,11 +1723,12 @@ Expr = Sizzle.selectors = { } : function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, + var cache, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; + useCache = !xml && !ofType, + diff = false; if ( parent ) { @@ -1730,7 +1737,10 @@ Expr = Sizzle.selectors = { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + return false; } } @@ -1744,11 +1754,21 @@ Expr = Sizzle.selectors = { // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { + // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || @@ -1758,29 +1778,55 @@ Expr = Sizzle.selectors = { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); - if ( node === elem ) { - break; + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } } } } @@ -2142,10 +2188,10 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var oldCache, outerCache, + var oldCache, uniqueCache, outerCache, newCache = [ dirruns, doneName ]; - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { @@ -2158,14 +2204,19 @@ function addCombinator( matcher, combinator, base ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; + uniqueCache[ dir ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { @@ -2390,18 +2441,21 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { len = elems.length; if ( outermost ) { - outermostContext = context !== document && context; + outermostContext = context === document || context || outermost; } // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { + if ( matcher( elem, context || document, xml) ) { results.push( elem ); break; } @@ -2425,8 +2479,17 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } } - // Apply set filters to unmatched elements + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { @@ -2518,10 +2581,11 @@ select = Sizzle.select = function( selector, context, results, seed ) { results = results || []; - // Try to minimize operations if there is no seed and only one group + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) if ( match.length === 1 ) { - // Take a shortcut and set the context if the root selector is an ID + // Reduce context if the leading compound selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && @@ -2576,7 +2640,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { context, !documentIsHTML, results, - rsibling.test( selector ) && testContext( context.parentNode ) || context + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; @@ -2652,17 +2716,46 @@ return Sizzle; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + var rneedsContext = jQuery.expr.match.needsContext; -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); @@ -2674,14 +2767,14 @@ function winnow( elements, qualifier, not ) { return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; - }); + } ); } if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; - }); + } ); } @@ -2694,8 +2787,8 @@ function winnow( elements, qualifier, not ) { } return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; - }); + return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not; + } ); } jQuery.filter = function( expr, elems, not ) { @@ -2709,10 +2802,10 @@ jQuery.filter = function( expr, elems, not ) { jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; - })); + } ) ); }; -jQuery.fn.extend({ +jQuery.fn.extend( { find: function( selector ) { var i, ret = [], @@ -2720,13 +2813,13 @@ jQuery.fn.extend({ len = self.length; if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { + return this.pushStack( jQuery( selector ).filter( function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } - }) ); + } ) ); } for ( i = 0; i < len; i++ ) { @@ -2739,10 +2832,10 @@ jQuery.fn.extend({ return ret; }, filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); + return this.pushStack( winnow( this, selector || [], false ) ); }, not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); + return this.pushStack( winnow( this, selector || [], true ) ); }, is: function( selector ) { return !!winnow( @@ -2756,7 +2849,7 @@ jQuery.fn.extend({ false ).length; } -}); +} ); // Initialize a jQuery object @@ -2765,15 +2858,12 @@ jQuery.fn.extend({ // A central reference to the root jQuery(document) var rootjQuery, - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - // 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 <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - init = jQuery.fn.init = function( selector, context ) { + init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) @@ -2781,9 +2871,16 @@ var rootjQuery, return this; } + // init accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + // Handle HTML strings if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + if ( selector.charAt( 0 ) === "<" && + selector.charAt( selector.length - 1 ) === ">" && + selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; @@ -2792,23 +2889,24 @@ var rootjQuery, } // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { + if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; // scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( - match[1], + match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { + // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); @@ -2824,20 +2922,21 @@ var rootjQuery, // HANDLE: $(#id) } else { - elem = document.getElementById( match[2] ); + elem = document.getElementById( match[ 2 ] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items // by name instead of ID - if ( elem.id !== match[2] ) { + if ( elem.id !== match[ 2 ] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; - this[0] = elem; + this[ 0 ] = elem; } this.context = document; @@ -2847,7 +2946,7 @@ var rootjQuery, // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); + return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) @@ -2857,15 +2956,16 @@ var rootjQuery, // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { - this.context = this[0] = selector; + this.context = this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : + return typeof root.ready !== "undefined" ? + root.ready( selector ) : + // Execute immediately if ready is not present selector( jQuery ); } @@ -2886,6 +2986,7 @@ rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, @@ -2894,46 +2995,19 @@ var rparentsprev = /^(?:parents|prev(?:Until|All))/, prev: true }; -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -jQuery.fn.extend({ +jQuery.fn.extend( { has: function( target ) { var i, targets = jQuery( target, this ), len = targets.length; - return this.filter(function() { + return this.filter( function() { for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { + if ( jQuery.contains( this, targets[ i ] ) ) { return true; } } - }); + } ); }, closest: function( selectors, context ) { @@ -2946,14 +3020,15 @@ jQuery.fn.extend({ 0; for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { + jQuery.find.matchesSelector( cur, selectors ) ) ) { matched.push( cur ); break; @@ -2961,7 +3036,7 @@ jQuery.fn.extend({ } } - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, // Determine the position of an element within @@ -2970,23 +3045,24 @@ jQuery.fn.extend({ // No argument, return index in parent if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // index in selector if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); + return jQuery.inArray( this[ 0 ], jQuery( elem ) ); } // Locate the position of the desired element return jQuery.inArray( + // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); + elem.jquery ? elem[ 0 ] : elem, this ); }, add: function( selector, context ) { return this.pushStack( - jQuery.unique( + jQuery.uniqueSort( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); @@ -2994,10 +3070,10 @@ jQuery.fn.extend({ addBack: function( selector ) { return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) + this.prevObject : this.prevObject.filter( selector ) ); } -}); +} ); function sibling( cur, dir ) { do { @@ -3007,16 +3083,16 @@ function sibling( cur, dir ) { return cur; } -jQuery.each({ +jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); + return dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); + return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); @@ -3025,22 +3101,22 @@ jQuery.each({ return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); + return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); + return dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); + return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); + return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { - return jQuery.sibling( elem.firstChild ); + return siblings( elem.firstChild ); }, contents: function( elem ) { return jQuery.nodeName( elem, "iframe" ) ? @@ -3060,9 +3136,10 @@ jQuery.each({ } if ( this.length > 1 ) { + // Remove duplicates if ( !guaranteedUnique[ name ] ) { - ret = jQuery.unique( ret ); + ret = jQuery.uniqueSort( ret ); } // Reverse order for parents* and prev-derivatives @@ -3073,20 +3150,17 @@ jQuery.each({ return this.pushStack( ret ); }; -}); -var rnotwhite = (/\S+/g); +} ); +var rnotwhite = ( /\S+/g ); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache +// Convert String-formatted options into Object-formatted ones function createOptions( options ) { - var object = optionsCache[ options ] = {}; + var object = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; - }); + } ); return object; } @@ -3117,156 +3191,186 @@ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : + createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, - // Last fire value (for non-forgettable lists) + + // Last fire value for non-forgettable lists memory, + // Flag to know if list was already fired fired, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // First callback to fire (used internally by add and fireWith) - firingStart, + + // Flag to prevent firing + locked, + // Actual callback list list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } } } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { list = []; + + // Otherwise, this object is spent } else { - self.disable(); + list = ""; } } }, + // Actual Callbacks object self = { + // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { + if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } - } else if ( arg && arg.length && type !== "string" ) { + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + // Inspect recursively add( arg ); } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); } } return this; }, + // Remove a callback from the list remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; } - }); - } + } + } ); return this; }, + // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; }, + // Remove all callbacks from the list empty: function() { - list = []; - firingLength = 0; + if ( list ) { + list = []; + } return this; }, - // Have the list do nothing anymore + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values disable: function() { - list = stack = memory = undefined; + locked = queue = []; + list = memory = ""; return this; }, - // Is it disabled? disabled: function() { return !list; }, - // Lock the list in its current state + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions lock: function() { - stack = undefined; + locked = true; if ( !memory ) { self.disable(); } return this; }, - // Is it locked? locked: function() { - return !stack; + return !!locked; }, + // Call all callbacks with the given context and arguments fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { + if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); + queue.push( args ); + if ( !firing ) { + fire(); } } return this; }, + // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, + // To know if the callbacks have already been called at least once fired: function() { return !!fired; @@ -3277,14 +3381,15 @@ jQuery.Callbacks = function( options ) { }; -jQuery.extend({ +jQuery.extend( { Deferred: function( func ) { var tuples = [ + // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] ], state = "pending", promise = { @@ -3297,25 +3402,30 @@ jQuery.extend({ }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; - return jQuery.Deferred(function( newDefer ) { + return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { + deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() + .progress( newDefer.notify ) .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); + .fail( newDefer.reject ); } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); } - }); - }); + } ); + } ); fns = null; - }).promise(); + } ).promise(); }, + // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { @@ -3333,11 +3443,12 @@ jQuery.extend({ stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; + promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { - list.add(function() { + list.add( function() { + // state = [ resolved | rejected ] state = stateString; @@ -3346,12 +3457,12 @@ jQuery.extend({ } // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); // Make the deferred a promise promise.promise( deferred ); @@ -3372,9 +3483,11 @@ jQuery.extend({ length = resolveValues.length, // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values @@ -3385,7 +3498,7 @@ jQuery.extend({ if ( values === progressValues ) { deferred.notifyWith( contexts, values ); - } else if ( !(--remaining) ) { + } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; @@ -3401,9 +3514,9 @@ jQuery.extend({ for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); + .fail( deferred.reject ); } else { --remaining; } @@ -3417,20 +3530,22 @@ jQuery.extend({ return deferred.promise(); } -}); +} ); // The deferred used on DOM ready var readyList; jQuery.fn.ready = function( fn ) { + // Add the callback jQuery.ready.promise().done( fn ); return this; }; -jQuery.extend({ +jQuery.extend( { + // Is the DOM ready to be used? Set to true once it occurs. isReady: false, @@ -3455,11 +3570,6 @@ jQuery.extend({ return; } - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } - // Remember that the DOM is ready jQuery.isReady = true; @@ -3477,15 +3587,15 @@ jQuery.extend({ jQuery( document ).off( "ready" ); } } -}); +} ); /** * Clean-up method for dom ready events */ function detach() { if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); } else { document.detachEvent( "onreadystatechange", completed ); @@ -3497,8 +3607,12 @@ function detach() { * The ready event handler and self cleanup method */ function completed() { + // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + if ( document.addEventListener || + window.event.type === "load" || + document.readyState === "complete" ) { + detach(); jQuery.ready(); } @@ -3509,23 +3623,29 @@ jQuery.ready.promise = function( obj ) { readyList = jQuery.Deferred(); - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // we once tried to use readyState "interactive" here, + // but it caused issues like the one + // discovered by ChrisS here: + // http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); + window.setTimeout( jQuery.ready ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { + // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); + document.addEventListener( "DOMContentLoaded", completed ); // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); + window.addEventListener( "load", completed ); // If IE event model is used } else { + // Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); @@ -3538,18 +3658,19 @@ jQuery.ready.promise = function( obj ) { try { top = window.frameElement == null && document.documentElement; - } catch(e) {} + } catch ( e ) {} if ( top && top.doScroll ) { - (function doScrollCheck() { + ( function doScrollCheck() { if ( !jQuery.isReady ) { try { + // Use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); + top.doScroll( "left" ); + } catch ( e ) { + return window.setTimeout( doScrollCheck, 50 ); } // detach all dom ready events @@ -3558,15 +3679,16 @@ jQuery.ready.promise = function( obj ) { // and execute any waiting functions jQuery.ready(); } - })(); + } )(); } } } return readyList.promise( obj ); }; +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); -var strundefined = typeof undefined; @@ -3576,19 +3698,21 @@ var i; for ( i in jQuery( support ) ) { break; } -support.ownLast = i !== "0"; +support.ownFirst = i === "0"; // Note: most support tests are defined in their respective modules. // false until the test is run support.inlineBlockNeedsLayout = false; // Execute ASAP in case we need to set body.style.zoom -jQuery(function() { +jQuery( function() { + // Minified: var a,b,c,d var val, div, body, container; body = document.getElementsByTagName( "body" )[ 0 ]; if ( !body || !body.style ) { + // Return for frameset docs that don't have a body return; } @@ -3599,7 +3723,8 @@ jQuery(function() { container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; body.appendChild( container ).appendChild( div ); - if ( typeof div.style.zoom !== strundefined ) { + if ( typeof div.style.zoom !== "undefined" ) { + // Support: IE<8 // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving @@ -3608,6 +3733,7 @@ jQuery(function() { support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; if ( val ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 // Prevent IE from shrinking the body in IE 7 mode #12869 // Support: IE<8 @@ -3616,35 +3742,25 @@ jQuery(function() { } body.removeChild( container ); -}); +} ); - - -(function() { +( function() { var div = document.createElement( "div" ); - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch ( e ) { + support.deleteExpando = false; } // Null elements to avoid leaks in IE. div = null; -})(); - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( elem ) { - var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], +} )(); +var acceptData = function( elem ) { + var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ], nodeType = +elem.nodeType || 1; // Do not set data on non-element DOM nodes because it will not be cleared (#8335). @@ -3652,14 +3768,17 @@ jQuery.acceptData = function( elem ) { false : // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute("classid") === noData; + !noData || noData !== true && elem.getAttribute( "classid" ) === noData; }; + + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /([A-Z])/g; function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { @@ -3673,11 +3792,12 @@ function dataAttr( elem, key, data ) { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : + // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; - } catch( e ) {} + } catch ( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); @@ -3696,7 +3816,7 @@ function isEmptyDataObject( obj ) { for ( name in obj ) { // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { continue; } if ( name !== "toJSON" ) { @@ -3708,7 +3828,7 @@ function isEmptyDataObject( obj ) { } function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { + if ( !acceptData( elem ) ) { return; } @@ -3729,11 +3849,13 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) { // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && + data === undefined && typeof name === "string" ) { return; } if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { @@ -3744,6 +3866,7 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) { } if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; @@ -3797,7 +3920,7 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) { } function internalRemoveData( elem, name, pvt ) { - if ( !jQuery.acceptData( elem ) ) { + if ( !acceptData( elem ) ) { return; } @@ -3833,10 +3956,11 @@ function internalRemoveData( elem, name, pvt ) { if ( name in thisCache ) { name = [ name ]; } else { - name = name.split(" "); + name = name.split( " " ); } } } else { + // If "name" is an array of keys... // When data is initially created, via ("key", "val") signature, // keys will be converted to camelCase. @@ -3848,12 +3972,12 @@ function internalRemoveData( elem, name, pvt ) { i = name.length; while ( i-- ) { - delete thisCache[ name[i] ]; + delete thisCache[ name[ i ] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) { return; } } @@ -3880,13 +4004,13 @@ function internalRemoveData( elem, name, pvt ) { /* jshint eqeqeq: true */ delete cache[ id ]; - // When all else fails, null + // When all else fails, undefined } else { - cache[ id ] = null; + cache[ id ] = undefined; } } -jQuery.extend({ +jQuery.extend( { cache: {}, // The following elements (space-suffixed to avoid Object.prototype collisions) @@ -3894,12 +4018,13 @@ jQuery.extend({ noData: { "applet ": true, "embed ": true, + // ...but Flash objects (which have this classid) *can* handle expandos "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" }, hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); }, @@ -3919,12 +4044,12 @@ jQuery.extend({ _removeData: function( elem, name ) { return internalRemoveData( elem, name, true ); } -}); +} ); -jQuery.fn.extend({ +jQuery.fn.extend( { data: function( key, value ) { var i, name, data, - elem = this[0], + elem = this[ 0 ], attrs = elem && elem.attributes; // Special expections of .data basically thwart jQuery.access, @@ -3944,7 +4069,7 @@ jQuery.fn.extend({ if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); + name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } @@ -3958,17 +4083,17 @@ jQuery.fn.extend({ // Sets multiple values if ( typeof key === "object" ) { - return this.each(function() { + return this.each( function() { jQuery.data( this, key ); - }); + } ); } return arguments.length > 1 ? // Sets one value - this.each(function() { + this.each( function() { jQuery.data( this, key, value ); - }) : + } ) : // Gets one value // Try to fetch any internally stored data first @@ -3976,14 +4101,14 @@ jQuery.fn.extend({ }, removeData: function( key ) { - return this.each(function() { + return this.each( function() { jQuery.removeData( this, key ); - }); + } ); } -}); +} ); -jQuery.extend({ +jQuery.extend( { queue: function( elem, type, data ) { var queue; @@ -3993,8 +4118,8 @@ jQuery.extend({ // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + if ( !queue || jQuery.isArray( data ) ) { + queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } @@ -4038,19 +4163,20 @@ jQuery.extend({ } }, - // not intended for public consumption - generates a queueHooks object, or returns the current one + // not intended for public consumption - generates a queueHooks object, + // or returns the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { + empty: jQuery.Callbacks( "once memory" ).add( function() { jQuery._removeData( elem, type + "queue" ); jQuery._removeData( elem, key ); - }) - }); + } ) + } ); } -}); +} ); -jQuery.fn.extend({ +jQuery.fn.extend( { queue: function( type, data ) { var setter = 2; @@ -4061,30 +4187,31 @@ jQuery.fn.extend({ } if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); + return jQuery.queue( this[ 0 ], type ); } return data === undefined ? this : - this.each(function() { + this.each( function() { var queue = jQuery.queue( this, type, data ); // ensure a hooks for this queue jQuery._queueHooks( this, type ); - if ( type === "fx" && queue[0] !== "inprogress" ) { + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { jQuery.dequeue( this, type ); } - }); + } ); }, dequeue: function( type ) { - return this.each(function() { + return this.each( function() { jQuery.dequeue( this, type ); - }); + } ); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, + // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { @@ -4115,23 +4242,138 @@ jQuery.fn.extend({ resolve(); return defer.promise( obj ); } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; +} ); + + +( function() { + var shrinkWrapBlocksVal; + + support.shrinkWrapBlocks = function() { + if ( shrinkWrapBlocksVal != null ) { + return shrinkWrapBlocksVal; + } + + // Will be changed later if needed. + shrinkWrapBlocksVal = false; + + // Minified: var b,c,d + var div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Test fired too early or in an unsupported environment, exit. + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + if ( typeof div.style.zoom !== "undefined" ) { + + // Reset CSS: box-sizing; display; margin; border + div.style.cssText = + + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;" + + "padding:1px;width:1px;zoom:1"; + div.appendChild( document.createElement( "div" ) ).style.width = "5px"; + shrinkWrapBlocksVal = div.offsetWidth !== 3; + } + + body.removeChild( container ); + + return shrinkWrapBlocksVal; + }; + +} )(); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); }; +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, length = elems.length, bulk = key == null; @@ -4140,7 +4382,7 @@ var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGe if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value @@ -4152,6 +4394,7 @@ var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGe } if ( bulk ) { + // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); @@ -4168,7 +4411,11 @@ var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGe if ( fn ) { for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + fn( + elems[ i ], + key, + raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); } } } @@ -4179,17 +4426,41 @@ var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGe // Gets bulk ? fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; + length ? fn( elems[ 0 ], key ) : emptyGet; }; -var rcheckableType = (/^(?:checkbox|radio)$/i); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + +var rleadingWhitespace = ( /^\s+/ ); + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" + + "details|dialog|figcaption|figure|footer|header|hgroup|main|" + + "mark|meter|nav|output|picture|progress|section|summary|template|time|video"; -(function() { - // Minified: var a,b,c - var input = document.createElement( "input" ), - div = document.createElement( "div" ), - fragment = document.createDocumentFragment(); +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + + +( function() { + var div = document.createElement( "div" ), + fragment = document.createDocumentFragment(), + input = document.createElement( "input" ); // Setup div.innerHTML = "
    '.$langs->trans("Currencies").' '.$langs->trans("Rate").' '.$langs->trans("Rate").'
    a"; @@ -4224,62 +4495,267 @@ var rcheckableType = (/^(?:checkbox|radio)$/i); // #11217 - WebKit loses check when the name is after the checked attribute fragment.appendChild( div ); - div.innerHTML = ""; + + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input = document.createElement( "input" ); + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 // old WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE<9 - // Opera does not clone events (and typeof div.attachEvent === undefined). - // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() - support.noCloneEvent = true; - if ( div.attachEvent ) { - div.attachEvent( "onclick", function() { - support.noCloneEvent = false; - }); + // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+ + support.noCloneEvent = !!div.addEventListener; - div.cloneNode( true ).click(); - } + // Support: IE<9 + // Since attributes and properties are the same in IE, + // cleanData must set properties to undefined rather than use removeAttribute + div[ jQuery.expando ] = 1; + support.attributes = !div.getAttribute( jQuery.expando ); +} )(); - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
    ", "
    " ], + area: [ 1, "", "" ], + + // Support: IE8 + param: [ 1, "", "" ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + col: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
    ", "
    " ] +}; + +// Support: IE8-IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; + ( elem = elems[ i ] ) != null; + i++ + ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } } } -})(); + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} -(function() { +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + jQuery._data( + elem, + "globalEval", + !refElements || jQuery._data( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/, + rtbody = / from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[ 1 ] === "
    " && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && + !tbody.childNodes.length ) { + + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; +} + + +( function() { var i, eventName, div = document.createElement( "div" ); - // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) - for ( i in { submit: true, change: true, focusin: true }) { + // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events) + for ( i in { submit: true, change: true, focusin: true } ) { eventName = "on" + i; - if ( !(support[ i + "Bubbles" ] = eventName in window) ) { + if ( !( support[ i ] = eventName in window ) ) { + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) div.setAttribute( eventName, "t" ); - support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; + support[ i ] = div.attributes[ eventName ].expando === false; } } // Null elements to avoid leaks in IE. div = null; -})(); +} )(); var rformElems = /^(?:input|select|textarea)$/i, rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; @@ -4289,12 +4765,75 @@ function returnFalse() { return false; } +// Support: IE9 +// See #13393 for more info function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. @@ -4327,18 +4866,22 @@ jQuery.event = { } // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { + if ( !( events = elemData.events ) ) { events = elemData.events = {}; } - if ( !(eventHandle = elemData.handle) ) { + if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? + return typeof jQuery !== "undefined" && + ( !e || jQuery.event.triggered !== e.type ) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + + // Add elem as a property of the handle fn to prevent a memory leak + // with IE non-native events eventHandle.elem = elem; } @@ -4346,9 +4889,9 @@ jQuery.event = { types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { @@ -4365,7 +4908,7 @@ jQuery.event = { special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers - handleObj = jQuery.extend({ + handleObj = jQuery.extend( { type: type, origType: origType, data: data, @@ -4373,16 +4916,18 @@ jQuery.event = { guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") + namespace: namespaces.join( "." ) }, handleObjIn ); // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { + if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); @@ -4424,7 +4969,7 @@ jQuery.event = { namespaces, origType, elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - if ( !elemData || !(events = elemData.events) ) { + if ( !elemData || !( events = elemData.events ) ) { return; } @@ -4432,9 +4977,9 @@ jQuery.event = { types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { @@ -4447,7 +4992,8 @@ jQuery.event = { special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; @@ -4457,7 +5003,8 @@ jQuery.event = { if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { @@ -4472,7 +5019,9 @@ jQuery.event = { // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); } @@ -4495,7 +5044,7 @@ jQuery.event = { bubbleType, special, tmp, i, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; cur = tmp = elem = elem || document; @@ -4509,13 +5058,14 @@ jQuery.event = { return; } - if ( type.indexOf(".") >= 0 ) { + if ( type.indexOf( "." ) > -1 ) { + // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); + namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } - ontype = type.indexOf(":") < 0 && "on" + type; + ontype = type.indexOf( ":" ) < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? @@ -4524,9 +5074,9 @@ jQuery.event = { // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; // Clean up the event in case it is being reused @@ -4560,28 +5110,30 @@ jQuery.event = { } // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { + if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && + jQuery._data( cur, "handle" ); + if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + if ( handle && handle.apply && acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); @@ -4593,8 +5145,11 @@ jQuery.event = { // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { + if ( + ( !special._default || + special._default.apply( eventPath.pop(), data ) === false + ) && acceptData( elem ) + ) { // Call a native DOM method on the target with the same name name as the event. // Can't use an .isFunction() check here because IE6/7 fails that test. @@ -4613,6 +5168,7 @@ jQuery.event = { try { elem[ type ](); } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) // only reproducible on winXP IE8 native, not IE9 in IE8 mode } @@ -4633,14 +5189,14 @@ jQuery.event = { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); - var i, ret, handleObj, matched, j, + var i, j, ret, matched, handleObj, handlerQueue = [], args = slice.call( arguments ), handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; + args[ 0 ] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired @@ -4653,24 +5209,25 @@ jQuery.event = { // Run delegates first; they may want to stop propagation beneath us i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { + if ( ( event.result = ret ) === false ) { event.preventDefault(); event.stopPropagation(); } @@ -4688,15 +5245,19 @@ jQuery.event = { }, handlers: function( event, handlers ) { - var sel, handleObj, matches, i, + var i, matches, sel, handleObj, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; + // Support (at least): Chrome, IE9 // Find delegate handlers // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { /* jshint eqeqeq: false */ for ( ; cur != this; cur = cur.parentNode || this ) { @@ -4704,7 +5265,7 @@ jQuery.event = { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; @@ -4714,7 +5275,7 @@ jQuery.event = { if ( matches[ sel ] === undefined ) { matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : + jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) { @@ -4722,7 +5283,7 @@ jQuery.event = { } } if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); + handlerQueue.push( { elem: cur, handlers: matches } ); } } } @@ -4730,7 +5291,7 @@ jQuery.event = { // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; @@ -4769,7 +5330,7 @@ jQuery.event = { event.target = originalEvent.srcElement || document; } - // Support: Chrome 23+, Safari? + // Support: Safari 6-8+ // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; @@ -4783,12 +5344,13 @@ jQuery.event = { }, // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), fixHooks: {}, keyHooks: { - props: "char charCode key keyCode".split(" "), + props: "char charCode key keyCode".split( " " ), filter: function( event, original ) { // Add which for key events @@ -4801,7 +5363,8 @@ jQuery.event = { }, mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + props: ( "button buttons clientX clientY fromElement offsetX offsetY " + + "pageX pageY screenX screenY toElement" ).split( " " ), filter: function( event, original ) { var body, eventDoc, doc, button = original.button, @@ -4813,13 +5376,19 @@ jQuery.event = { doc = eventDoc.documentElement; body = eventDoc.body; - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add relatedTarget, if necessary if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + event.relatedTarget = fromElement === event.target ? + original.toElement : + fromElement; } // Add which for click: 1 === left; 2 === middle; 3 === right @@ -4834,10 +5403,12 @@ jQuery.event = { special: { load: { + // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { + // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { @@ -4845,6 +5416,7 @@ jQuery.event = { this.focus(); return false; } catch ( e ) { + // Support: IE<9 // If we error on focus to hidden element (#1486, #12518), // let .trigger() run the handlers @@ -4863,6 +5435,7 @@ jQuery.event = { delegateType: "focusout" }, click: { + // For checkbox, fire native event so checked state will be right trigger: function() { if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { @@ -4889,24 +5462,28 @@ jQuery.event = { } }, - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. + // Piggyback on a donor event to simulate a different one + simulate: function( type, elem, event ) { var e = jQuery.extend( new jQuery.Event(), event, { type: type, - isSimulated: true, - originalEvent: {} + isSimulated: true + + // Previously, `originalEvent: {}` was set here, so stopPropagation call + // would not be triggered on donor event, since in our own + // jQuery.event.stopPropagation function we had a check for existence of + // originalEvent.stopPropagation method, so, consequently it would be a noop. + // + // Guard for simulated events was moved to jQuery.event.stopPropagation function + // since `originalEvent` should point to the original event for the + // constancy with other events and for more focused logic } ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } + + jQuery.event.trigger( e, null, elem ); + if ( e.isDefaultPrevented() ) { event.preventDefault(); } @@ -4915,8 +5492,10 @@ jQuery.event = { jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { + + // This "if" is needed for plain objects if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); + elem.removeEventListener( type, handle ); } } : function( elem, type, handle ) { @@ -4925,8 +5504,9 @@ jQuery.removeEvent = document.removeEventListener ? if ( elem.detachEvent ) { // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === strundefined ) { + // detachEvent needed property on element, by name of that event, + // to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { elem[ name ] = null; } @@ -4935,8 +5515,9 @@ jQuery.removeEvent = document.removeEventListener ? }; jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { + if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } @@ -4949,6 +5530,7 @@ jQuery.Event = function( src, props ) { // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && + // Support: IE < 9, Android < 4.0 src.returnValue === false ? returnTrue : @@ -4974,6 +5556,7 @@ jQuery.Event = function( src, props ) { // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { + constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, @@ -5000,9 +5583,11 @@ jQuery.Event.prototype = { var e = this.originalEvent; this.isPropagationStopped = returnTrue; - if ( !e ) { + + if ( !e || this.isSimulated ) { return; } + // If stopPropagation exists, run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); @@ -5026,7 +5611,14 @@ jQuery.Event.prototype = { }; // Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", @@ -5042,9 +5634,9 @@ jQuery.each({ related = event.relatedTarget, handleObj = event.handleObj; - // For mousenter/leave call the handler if related is outside the target. + // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; @@ -5052,13 +5644,14 @@ jQuery.each({ return ret; } }; -}); +} ); // IE submit delegation -if ( !support.submitBubbles ) { +if ( !support.submit ) { jQuery.event.special.submit = { setup: function() { + // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; @@ -5066,30 +5659,42 @@ if ( !support.submitBubbles ) { // Lazy-add a submit handler when a descendant form may potentially be submitted jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "submitBubbles" ) ) { + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? + + // Support: IE <=8 + // We use jQuery.prop instead of elem.form + // to allow fixing the IE8 delegated submit issue (gh-2332) + // by 3rd party polyfills/workarounds. + jQuery.prop( elem, "form" ) : + undefined; + + if ( form && !jQuery._data( form, "submit" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "submitBubbles", true ); + event._submitBubble = true; + } ); + jQuery._data( form, "submit", true ); } - }); + } ); + // return undefined since we don't need an event listener }, postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; + if ( event._submitBubble ) { + delete event._submitBubble; if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); + jQuery.event.simulate( "submit", this.parentNode, event ); } } }, teardown: function() { + // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; @@ -5102,52 +5707,57 @@ if ( !support.submitBubbles ) { } // IE change delegation and checkbox/radio fix -if ( !support.changeBubbles ) { +if ( !support.change ) { jQuery.event.special.change = { setup: function() { if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click // after a propertychange. Eat the blur-change in special.change.handle. // This still fires onchange a second time for check/radio after blur. if ( this.type === "checkbox" || this.type === "radio" ) { jQuery.event.add( this, "propertychange._change", function( event ) { if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; + this._justChanged = true; } - }); + } ); jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; + if ( this._justChanged && !event.isTrigger ) { + this._justChanged = false; } + // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); + jQuery.event.simulate( "change", this, event ); + } ); } return false; } + // Delegated event; lazy-add a change handler on descendant inputs jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); + jQuery.event.simulate( "change", this.parentNode, event ); } - }); - jQuery._data( elem, "changeBubbles", true ); + } ); + jQuery._data( elem, "change", true ); } - }); + } ); }, handle: function( event ) { var elem = event.target; // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + if ( this !== elem || event.isSimulated || event.isTrigger || + ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { + return event.handleObj.handler.apply( this, arguments ); } }, @@ -5160,14 +5770,21 @@ if ( !support.changeBubbles ) { }; } -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { +// Support: Firefox +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome, Safari +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; jQuery.event.special[ fix ] = { setup: function() { @@ -5191,80 +5808,34 @@ if ( !support.focusinBubbles ) { } } }; - }); + } ); } -jQuery.fn.extend({ +jQuery.fn.extend( { - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var type, origFn; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); + return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { + // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); @@ -5272,6 +5843,7 @@ jQuery.fn.extend({ return this; } if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) fn = selector; selector = undefined; @@ -5279,105 +5851,40 @@ jQuery.fn.extend({ if ( fn === false ) { fn = returnFalse; } - return this.each(function() { + return this.each( function() { jQuery.event.remove( this, types, fn, selector ); - }); + } ); }, trigger: function( type, data ) { - return this.each(function() { + return this.each( function() { jQuery.event.trigger( type, data, this ); - }); + } ); }, triggerHandler: function( type, data ) { - var elem = this[0]; + var elem = this[ 0 ]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } -}); +} ); -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); +var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /]", "i"), - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
    ", "
    " ], - area: [ 1, "", "" ], - param: [ 1, "", "" ], - thead: [ 1, "
    ", "
    " ], - tr: [ 2, "", "
    " ], - col: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
    ", "
    " ] - }, safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : - undefined; - - if ( !found ) { - for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); - } - } - } - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; -} - -// Used in buildFragment, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} + fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); // Support: IE<8 // Manipulating tables requires a tbody @@ -5385,37 +5892,27 @@ function manipulationTarget( elem, content ) { return jQuery.nodeName( elem, "table" ) && jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { - elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + elem.type = ( jQuery.find.attr( elem, "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { - elem.type = match[1]; + elem.type = match[ 1 ]; } else { - elem.removeAttribute("type"); + elem.removeAttribute( "type" ); } return elem; } -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; (elem = elems[i]) != null; i++ ) { - jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); - } -} - function cloneCopyEvent( src, dest ) { - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { return; } @@ -5480,11 +5977,12 @@ function fixCloneNodeIssues( src, dest ) { // element in IE9, the outerHTML strategy above is not sufficient. // If the src has innerHTML and the destination does not, // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { dest.innerHTML = src.innerHTML; } } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox // or radio button. Worse, IE6-7 fail to give the cloned element // a checked appearance if the defaultChecked value isn't also set @@ -5509,12 +6007,137 @@ function fixCloneNodeIssues( src, dest ) { } } -jQuery.extend({ +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( + ( node.text || node.textContent || node.innerHTML || "" ) + .replace( rcleanScript, "" ) + ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + elems = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = elems[ i ] ) != null; i++ ) { + + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + clone: function( elem, dataAndEvents, deepDataAndEvents ) { var destElements, node, clone, i, srcElements, inPage = jQuery.contains( elem.ownerDocument, elem ); - if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + if ( support.html5Clone || jQuery.isXMLDoc( elem ) || + !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); // IE<=8 does not properly clone detached, unknown element nodes @@ -5523,18 +6146,19 @@ jQuery.extend({ fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); } - if ( (!support.noCloneEvent || !support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + if ( ( !support.noCloneEvent || !support.noCloneChecked ) && + ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); // Fix all IE cloning issues - for ( i = 0; (node = srcElements[i]) != null; ++i ) { + for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - fixCloneNodeIssues( node, destElements[i] ); + if ( destElements[ i ] ) { + fixCloneNodeIssues( node, destElements[ i ] ); } } } @@ -5545,8 +6169,8 @@ jQuery.extend({ srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); - for ( i = 0; (node = srcElements[i]) != null; i++ ) { - cloneCopyEvent( node, destElements[i] ); + for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { + cloneCopyEvent( node, destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); @@ -5565,143 +6189,16 @@ jQuery.extend({ return clone; }, - buildFragment: function( elems, context, scripts, selection ) { - var j, elem, contains, - tmp, tag, tbody, wrap, - l = elems.length, - - // Ensure a safe fragment - safe = createSafeFragment( context ), - - nodes = [], - i = 0; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || safe.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; - - // Descend through wrappers to the right content - j = wrap[0]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Manually add leading whitespace removed by IE - if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); - } - - // Remove IE's autoinserted from table fragments - if ( !support.tbody ) { - - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : - - // String was a bare or - wrap[1] === "
    " && !rtbody.test( elem ) ? - tmp : - 0; - - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { - elem.removeChild( tbody ); - } - } - } - - jQuery.merge( nodes, tmp.childNodes ); - - // Fix #12392 for WebKit and IE > 9 - tmp.textContent = ""; - - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); - } - - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; - } - } - } - - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); - } - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - tmp = null; - - return safe; - }, - - cleanData: function( elems, /* internal */ acceptData ) { + cleanData: function( elems, /* internal */ forceAcceptData ) { var elem, type, id, data, i = 0, internalKey = jQuery.expando, cache = jQuery.cache, - deleteExpando = support.deleteExpando, + attributes = support.attributes, special = jQuery.event.special; - for ( ; (elem = elems[i]) != null; i++ ) { - if ( acceptData || jQuery.acceptData( elem ) ) { + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + if ( forceAcceptData || acceptData( elem ) ) { id = elem[ internalKey ]; data = id && cache[ id ]; @@ -5724,17 +6221,18 @@ jQuery.extend({ delete cache[ id ]; - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( typeof elem.removeAttribute !== strundefined ) { + // Support: IE<9 + // IE does not allow us to delete expando properties from nodes + // IE creates expando attributes along with the property + // IE does not have a removeAttribute function on Document nodes + if ( !attributes && typeof elem.removeAttribute !== "undefined" ) { elem.removeAttribute( internalKey ); + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 } else { - elem[ internalKey ] = null; + elem[ internalKey ] = undefined; } deletedIds.push( id ); @@ -5743,78 +6241,71 @@ jQuery.extend({ } } } -}); +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, -jQuery.fn.extend({ text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + this.empty().append( + ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) + ); }, null, value, arguments.length ); }, append: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } - }); + } ); }, prepend: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } - }); + } ); }, before: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } - }); + } ); }, after: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; + } ); }, empty: function() { var elem, i = 0; - for ( ; (elem = this[i]) != null; i++ ) { + for ( ; ( elem = this[ i ] ) != null; i++ ) { + // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); @@ -5839,9 +6330,9 @@ jQuery.fn.extend({ dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - return this.map(function() { + return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); + } ); }, html: function( value ) { @@ -5860,14 +6351,15 @@ jQuery.fn.extend({ if ( typeof value === "string" && !rnoInnerhtml.test( value ) && ( support.htmlSerialize || !rnoshimcache.test( value ) ) && ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - value = value.replace( rxhtmlTag, "<$1>" ); + value = jQuery.htmlPrefilter( value ); try { - for (; i < l; i++ ) { + for ( ; i < l; i++ ) { + // Remove element nodes and prevent memory leaks - elem = this[i] || {}; + elem = this[ i ] || {}; if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; @@ -5877,7 +6369,7 @@ jQuery.fn.extend({ elem = 0; // If using innerHTML throws an exception, use the fallback method - } catch(e) {} + } catch ( e ) {} } if ( elem ) { @@ -5887,117 +6379,25 @@ jQuery.fn.extend({ }, replaceWith: function() { - var arg = arguments[ 0 ]; + var ignored = []; - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var first, node, hasScripts, - scripts, doc, fragment, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[0], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[0] = value.call( this, index, self.html() ); + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; } - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[i], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - } - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - } - } - - return this; + // Force callback invocation + }, ignored ); } -}); +} ); -jQuery.each({ +jQuery.each( { appendTo: "append", prependTo: "prepend", insertBefore: "before", @@ -6012,8 +6412,8 @@ jQuery.each({ last = insert.length - 1; for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone(true); - jQuery( insert[i] )[ original ]( elems ); + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() push.apply( ret, elems.get() ); @@ -6021,28 +6421,29 @@ jQuery.each({ return this.pushStack( ret ); }; -}); +} ); var iframe, - elemdisplay = {}; + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; /** * Retrieve the actual display of a element * @param {String} name nodeName of the element * @param {Object} doc Document object */ + // Called only from within defaultDisplay function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); + display = jQuery.css( elem[ 0 ], "display" ); // We don't have any data stored on the element, // so use "detach" method as fast way to get rid of the element @@ -6066,7 +6467,8 @@ function defaultDisplay( nodeName ) { if ( display === "none" || !display ) { // Use the already-created iframe if possible - iframe = (iframe || jQuery( "';*/ } else { From 3045449dbbd0a3b9358276ed0e2c2d0e4650bec1 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 1 May 2016 17:01:38 +0200 Subject: [PATCH 578/834] Work on website module. Can delete a page. --- htdocs/langs/en_US/website.lang | 3 ++- htdocs/theme/eldy/style.css.php | 33 ++++++++++---------------- htdocs/theme/md/style.css.php | 8 ++++++- htdocs/websites/index.php | 41 ++++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index 668ec7b3c07..3a09f226eb0 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -15,4 +15,5 @@ EditPageContent=Edit Content Website=Web site AddPage=Add page Page=Page -PreviewOfSiteNotYetAvailable=Preview of your website %s not yet available. You must first add a page. \ No newline at end of file +PreviewOfSiteNotYetAvailable=Preview of your website %s not yet available. You must first add a page. +PageDeleted=Page %s of website %s deleted \ No newline at end of file diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index c33489e46fe..162310f4697 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -350,7 +350,7 @@ legend { margin-bottom: 8px; } fieldset { border: 1px solid #AAAAAA !important; } -.button, input[name="sbmtConnexion"] { +.button, .buttonDelete, input[name="sbmtConnexion"] { font-family: ; border-color: #c5c5c5; border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); @@ -383,17 +383,17 @@ fieldset { border: 1px solid #AAAAAA !important; } -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } -.button:focus { +.button:focus, .buttonDelete:focus { -moz-box-shadow: 0px 0px 6px 1px rgba(0, 0, 60, 0.2), 0px 0px 0px rgba(60,60,60,0.1); -webkit-box-shadow: 0px 0px 6px 1px rgba(0, 0, 60, 0.2), 0px 0px 0px rgba(60,60,60,0.1); box-shadow: 0px 0px 6px 1px rgba(0, 0, 60, 0.2), 0px 0px 0px rgba(60,60,60,0.1); } -.button:hover { +.button:hover, .buttonDelete:hover { -moz-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); -webkit-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); } -.button:disabled { +.button:disabled, .buttonDelete:disabled { opacity: 0.4; filter: alpha(opacity=40); /* For IE8 and earlier */ box-shadow: none; @@ -1907,31 +1907,16 @@ span.butAction, span.butActionDelete { } .butAction:hover { -/* for bootstrap look - color: #fff; - background-color: #286090; - border-color: #204d74; -*/ -moz-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); -webkit-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); } -.butActionDelete, .butActionDelete:link, .butActionDelete:visited, .butActionDelete:hover, .butActionDelete:active { -/* for bootstrap look - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -*/ - color: #800; +.butActionDelete, .butActionDelete:link, .butActionDelete:visited, .butActionDelete:hover, .butActionDelete:active, .buttonDelete { + color: #800 !important; } .butActionDelete:hover { -/* for bootstrap look - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -*/ -moz-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); -webkit-box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2), 0px 0px 0px rgba(60,60,60,0.1); @@ -3128,6 +3113,12 @@ td.hidden { border-bottom: 1px solid #888; background: #eee; } +.websitebar .button, .websitebar .buttonDelete +{ + padding: 2px 5px 3px 5px !important; + margin: 2px 4px 2px 4px !important; + line-height: normal; +} .websiteselection { display: inline-block; padding-left: 10px; diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index 58629477e76..3de39efb50f 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -1913,7 +1913,7 @@ span.butAction, span.butActionDelete { background-repeat: repeat-x } -.butActionDelete { +.butActionDelete, .buttonDelete { color: #ffffff !important; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #cc6d00; @@ -2990,6 +2990,12 @@ td.hidden { border-bottom: 1px solid #888; background: #eee; } +.websitebar .button, .websitebar .buttonDelete +{ + padding: 2px 4px 2px 4px !important; + margin: 2px 4px 2px 4px !important; + line-height: normal; +} .websiteselection { display: inline-block; padding-left: 10px; diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php index ea03c9fd7b7..0696c28c86d 100644 --- a/htdocs/websites/index.php +++ b/htdocs/websites/index.php @@ -81,6 +81,7 @@ $page=GETPOST('page', 'alpha'); $pageid=GETPOST('pageid', 'alpha'); $action=GETPOST('action','alpha'); +if (GETPOST('delete')) { $action='delete'; } if (GETPOST('preview')) $action='preview'; if (GETPOST('create')) { $action='create'; } if (GETPOST('editmedia')) { $action='editmedia'; } @@ -110,7 +111,7 @@ if ($website) { $res = $object->fetch(0, $website); } -if ($pageid) +if ($pageid && $action != 'add') { $res = $objectpage->fetch($pageid); } @@ -207,6 +208,43 @@ if ($action == 'update') } } +// Update page +if ($action == 'delete') +{ + $db->begin(); + + $res = $object->fetch(0, $website); + + $res = $objectpage->fetch($pageid, $object->fk_website); + + if ($res > 0) + { + $res = $objectpage->delete($user); + if (! $res > 0) + { + $error++; + setEventMessages($objectpage->error, $objectpage->errors, 'errors'); + } + + if (! $error) + { + $db->commit(); + setEventMessages($langs->trans("PageDeleted", $objectpage->pageurl, $website), null, 'mesgs'); + + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website); + exit; + } + else + { + $db->rollback(); + } + } + else + { + dol_print_error($db); + } +} + // Update css if ($action == 'updatecss') { @@ -442,6 +480,7 @@ if (count($object->records) > 0) $out.=''; print $out; print ''; + print ''; //print $form->selectarray('page', $array); print ''; print '
    '; From 1f015388b6652a0b161e524e59ea50edfcac5a57 Mon Sep 17 00:00:00 2001 From: aspangaro Date: Sun, 1 May 2016 17:04:55 +0200 Subject: [PATCH 579/834] FIX: Fiscal year - Some mistakes on card --- htdocs/accountancy/admin/fiscalyear_card.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/htdocs/accountancy/admin/fiscalyear_card.php b/htdocs/accountancy/admin/fiscalyear_card.php index e04cc7132f0..e663557d22c 100644 --- a/htdocs/accountancy/admin/fiscalyear_card.php +++ b/htdocs/accountancy/admin/fiscalyear_card.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2014-2016 Alexandre Spangaro * * 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 @@ -205,8 +205,8 @@ if ($action == 'create') { // Ref print "
    "; - print ''; // Label @@ -252,11 +252,11 @@ if ($action == 'create') { print '
    ' . $langs->trans("Ref") . ''; - print $object->rowid; + print '' . $langs->trans("Ref") . ''; + print $object->ref; print '
    '; - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; // Ref print ''; @@ -270,16 +270,16 @@ if ($action == 'create') { // Date start print ''; // Date end print ''; // Statut From 170dc467ffe50690d6ee53c9261ebdac76bdc5cb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 1 May 2016 19:49:03 +0200 Subject: [PATCH 580/834] Better code for website edition --- htdocs/websites/index.php | 63 ++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php index 0696c28c86d..91c4921d29d 100644 --- a/htdocs/websites/index.php +++ b/htdocs/websites/index.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2016 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,11 +16,14 @@ */ /** - * \file htdocs/admin/website.php + * \file htdocs/website/index.php * \ingroup website - * \brief Page to setup the module Website + * \brief Page to website view/edit */ +define('NOSCANPOSTFORINJECTION',1); +define('NOSTYLECHECK',1); + /** * Show HTML header HTML + BODY + Top menu + left menu + DIV @@ -78,7 +81,7 @@ $conf->dol_hide_leftmenu = 1; $error=0; $website=GETPOST('website', 'alpha'); $page=GETPOST('page', 'alpha'); -$pageid=GETPOST('pageid', 'alpha'); +$pageid=GETPOST('pageid', 'int'); $action=GETPOST('action','alpha'); if (GETPOST('delete')) { $action='delete'; } @@ -106,12 +109,13 @@ if (empty($website)) break; } } - if ($website) { $res = $object->fetch(0, $website); } -if ($pageid && $action != 'add') + +if ($pageid < 0) $pageid = 0; +if ($pageid > 0 && $action != 'add') { $res = $objectpage->fetch($pageid); } @@ -327,6 +331,10 @@ if ($action == 'updatecontent') { $objectpage->content = GETPOST('PAGE_CONTENT'); + // Clean data. We remove all the head section. + $objectpage->content = preg_replace('//s', '', $objectpage->content); + /* $objectpage->content = preg_replace('//s', '', $objectpage->content); */ + $res = $objectpage->update($user); if (! $res > 0) { @@ -397,7 +405,6 @@ if ($action == 'edit') { print ''; } -if ($website) print ''; // Add a margin under toolbar ? @@ -414,21 +421,25 @@ if (count($object->records) > 0) print ''; print '
    '; + $out=''; + $out.=''; + print $out; + print ''; print '
    '; @@ -444,7 +455,13 @@ if (count($object->records) > 0) print ''; print ''; } - //else print ''; + + if (in_array($action, array('editcss','editmenu','create'))) + { + if ($action != 'preview') print ''; + if (preg_match('/^create/',$action)) print ''; + if (preg_match('/^edit/',$action)) print ''; + } print ''; @@ -495,13 +512,19 @@ if (count($object->records) > 0) if ($pageid > 0) { - print ''; - print ''; + print ''; + print ''; + //print ''.dol_escape_htmltag($langs->trans("EditPageMeta")).''; + //print ''.dol_escape_htmltag($langs->trans("EditPageContent")).''; } } - else print ''; - if (preg_match('/^create/',$action)) print ''; - if (preg_match('/^edit/',$action)) print ''; + + if (! in_array($action, array('editcss','editmenu','create'))) + { + if ($action != 'preview') print ''; + if (preg_match('/^create/',$action)) print ''; + if (preg_match('/^edit/',$action)) print ''; + } print ''; From 46e2ed281030367c5704bd5dc5b5b15a526de652 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 1 May 2016 20:33:21 +0200 Subject: [PATCH 581/834] NEW: All variant of ckeditor config can be tested into the setup page of module. --- htdocs/admin/fckeditor.php | 14 +++++++++++++- htdocs/core/class/doleditor.class.php | 8 ++++++-- htdocs/langs/en_US/admin.lang | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/htdocs/admin/fckeditor.php b/htdocs/admin/fckeditor.php index b5bd4d045f6..e244941ff58 100644 --- a/htdocs/admin/fckeditor.php +++ b/htdocs/admin/fckeditor.php @@ -189,7 +189,19 @@ else show_skin(null,1); print '
    '."\n"; - print load_fiche_titre($langs->trans("TestSubmitForm"),'(mode='.$mode.')',''); + $listofmodes=array('dolibarr_mailings','dolibarr_notes','dolibarr_details','Full'); + $linkstomode=''; + foreach($listofmodes as $newmode) + { + if ($linkstomode) $linkstomode.=' - '; + $linkstomode.=''; + if ($mode == $newmode) $linkstomode.=''; + $linkstomode.=$newmode; + if ($mode == $newmode) $linkstomode.=''; + $linkstomode.=''; + } + $linkstomode.=''; + print load_fiche_titre($langs->trans("TestSubmitForm"),$linkstomode,''); print ''; $uselocalbrowser=true; $readonly=($mode=='dolibarr_readonly'?1:0); diff --git a/htdocs/core/class/doleditor.class.php b/htdocs/core/class/doleditor.class.php index 2a9114cbd2c..ff31823976e 100644 --- a/htdocs/core/class/doleditor.class.php +++ b/htdocs/core/class/doleditor.class.php @@ -148,7 +148,10 @@ class DolEditor { global $conf,$langs; - $found=0; + $fullpage=False; + $disallowAnyContent=empty($conf->global->FCKEDITOR_ALLOW_ANY_CONTENT); // Only predefined list of html tags are allowed + + $found=0; $out=''; if ($this->tool == 'fckeditor') @@ -186,7 +189,8 @@ class DolEditor customConfig : ckeditorConfig, readOnly : '.($this->readonly?'true':'false').', htmlEncodeOutput :'.$htmlencode_force.', - allowedContent :'.(empty($conf->global->FCKEDITOR_ALLOW_ANY_CONTENT)?'false':'true').', + allowedContent :'.($disallowAnyContent?'false':'true').', + fullPage : '.($fullpage?'true':'false').', toolbar: \''.$this->toolbarname.'\', toolbarStartupExpanded: '.($this->toolbarstartexpanded ? 'true' : 'false').', width: '.($this->width ? '\''.$this->width.'\'' : '\'\'').', diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index e537b9cedbe..9122546a1f3 100755 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -521,7 +521,7 @@ Module1520Desc=Mass mail document generation Module1780Name=Tags/Categories Module1780Desc=Create tags/category (products, customers, suppliers, contacts or members) Module2000Name=WYSIWYG editor -Module2000Desc=Allow to edit some text area using an advanced editor +Module2000Desc=Allow to edit some text area using an advanced editor (Based on CKEditor) Module2200Name=Dynamic Prices Module2200Desc=Enable the usage of math expressions for prices Module2300Name=Cron From 5823055cdf58acdfdd3a2b80cdeb59563cd8ff6c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 1 May 2016 21:19:29 +0200 Subject: [PATCH 582/834] Work on website module. --- .../filemanagerdol/connectors/php/config.php | 14 +++++++++--- htdocs/langs/en_US/website.lang | 3 ++- htdocs/public/websites/index.php | 22 +++++++++++++++++-- htdocs/websites/class/websitepage.class.php | 4 ++-- htdocs/websites/index.php | 6 +++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/htdocs/core/filemanagerdol/connectors/php/config.php b/htdocs/core/filemanagerdol/connectors/php/config.php index 7b5bea4e97b..8733e6a7c17 100644 --- a/htdocs/core/filemanagerdol/connectors/php/config.php +++ b/htdocs/core/filemanagerdol/connectors/php/config.php @@ -67,7 +67,7 @@ $Config['SecureImageUploads'] = true; $Config['ConfigAllowedCommands'] = array('QuickUpload', 'FileUpload', 'GetFolders', 'GetFoldersAndFiles', 'CreateFolder'); // Allowed Resource Types. -$Config['ConfigAllowedTypes'] = array('File', 'Image', 'Flash', 'Media'); +$Config['ConfigAllowedTypes'] = array('File', 'Image', 'Media'); // For security, HTML is allowed in the first Kb of data for files having the // following extensions only. @@ -78,11 +78,19 @@ $Config['HtmlExtensions'] = array("html", "htm", "xml", "xsd", "txt", "js"); // If possible, it is recommended to set more restrictive permissions, like 0755. // Set to 0 to disable this feature. // Note: not needed on Windows-based servers. -$Config['ChmodOnUpload'] = 0775 ; +$newmask = '0644'; +if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK; +$Config['ChmodOnUpload'] = $newmask; // See comments above. // Used when creating folders that does not exist. -$Config['ChmodOnFolderCreate'] = 0775 ; +$newmask = '0755'; +$dirmaskdec=octdec($newmask); +if (! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK); +$dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files +$newmask = decoct($dirmaskdec); + +$Config['ChmodOnFolderCreate'] = $newmask; /* Configuration settings for each Resource Type diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index 3a09f226eb0..a0b2106ac63 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -16,4 +16,5 @@ Website=Web site AddPage=Add page Page=Page PreviewOfSiteNotYetAvailable=Preview of your website %s not yet available. You must first add a page. -PageDeleted=Page %s of website %s deleted \ No newline at end of file +PageDeleted=Page %s of website %s deleted +ViewInNewTab=View in new tab \ No newline at end of file diff --git a/htdocs/public/websites/index.php b/htdocs/public/websites/index.php index aa7d5d4941d..b54a69e90f9 100644 --- a/htdocs/public/websites/index.php +++ b/htdocs/public/websites/index.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2016 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,7 +16,7 @@ */ /** - * \file htdocs/public/paypal/index.php + * \file htdocs/public/websites/index.php * \ingroup core * \brief A redirect page to an error * \author Laurent Destailleur @@ -64,6 +64,24 @@ if (!empty($conf->global->MAIN_APPLICATION_TITLE)) $appli=$conf->global->MAIN_AP //print 'Directory with '.$appli.' websites.
    '; +if (empty($pageid)) +{ + require_once DOL_DOCUMENT_ROOT.'/websites/class/website.class.php'; + require_once DOL_DOCUMENT_ROOT.'/websites/class/websitepage.class.php'; + + $object=new Website($db); + $object->fetch(0, $website); + + $objectpage=new WebsitePage($db); + $array=$objectpage->fetchAll($object->id); + + if (count($array) > 0) + { + $firstrep=reset($array); + $pageid=$firstrep->id; + } +} + // Security: Delete string ../ into $original_file global $dolibarr_main_data_root; diff --git a/htdocs/websites/class/websitepage.class.php b/htdocs/websites/class/websitepage.class.php index 430cf4f0f3b..b363687765f 100644 --- a/htdocs/websites/class/websitepage.class.php +++ b/htdocs/websites/class/websitepage.class.php @@ -307,7 +307,7 @@ class WebsitePage extends CommonObject while ($obj = $this->db->fetch_object($resql)) { - $record = new Websitepage($this->db); + $record = new WebsitePage($this->db); $record->id = $obj->rowid; $record->fk_website = $obj->fk_website; @@ -320,7 +320,7 @@ class WebsitePage extends CommonObject $record->date_creation = $this->db->jdate($obj->date_creation); $record->date_modification = $this->db->jdate($obj->date_modification); $record->tms = $this->db->jdate($obj->tms); - + //var_dump($record->id); $records[$record->id] = $record; } $this->db->free($resql); diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php index 91c4921d29d..98efd23f45b 100644 --- a/htdocs/websites/index.php +++ b/htdocs/websites/index.php @@ -420,6 +420,7 @@ if (count($object->records) > 0) print $langs->trans("Website").': '; print ''; + // List of websites print '
    '; $out=''; $out.=''; + if ($website) + { + print ''.$langs->trans("ViewInNewTab").''; + } print '
    '; + // Button for websites print '
    '; if ($action == 'preview') From df4afdd6638938910493cb2fb33a4511c93e47d0 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 2 May 2016 09:49:58 +0200 Subject: [PATCH 583/834] NEW Can disable a module by renaming dir of module into module.disabled (this save time for maintenance when working with FTP). --- htdocs/core/lib/functions2.lib.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 85ebe30dec6..887b5f300cf 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -71,7 +71,8 @@ function jsUnEscape($source) /** - * Return list of modules directories + * Return list of modules directories. We detect directories that contains a subdirectory /core/modules + * We discard directory modules that contains 'disabled' into their name. * * @param string $subdir Sub directory (Example: '/mailings') * @return array Array of directories that can contains module descriptors @@ -95,6 +96,8 @@ function dolGetModulesDirs($subdir='') { while (($file = readdir($handle))!==false) { + if (preg_match('/disabled/',$file)) continue; // We discard module if it contains disabled into name. + if (is_dir($dirroot.'/'.$file) && substr($file, 0, 1) <> '.' && substr($file, 0, 3) <> 'CVS' && $file != 'includes') { if (is_dir($dirroot . '/' . $file . '/core/modules'.$subdir.'/')) From bc43c87f511f2db575098c346ba9ee6a60565077 Mon Sep 17 00:00:00 2001 From: fmarcet Date: Mon, 2 May 2016 11:36:29 +0200 Subject: [PATCH 584/834] =?UTF-8?q?New:=20Add=20Per=C3=BA=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../install/mysql/data/llx_10_c_regions.sql | 2 ++ .../mysql/data/llx_20_c_departements.sql | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/htdocs/install/mysql/data/llx_10_c_regions.sql b/htdocs/install/mysql/data/llx_10_c_regions.sql index d9cd97be585..e525ec1088f 100644 --- a/htdocs/install/mysql/data/llx_10_c_regions.sql +++ b/htdocs/install/mysql/data/llx_10_c_regions.sql @@ -294,3 +294,5 @@ INSERT INTO llx_c_regions (fk_pays, code_region, cheflieu, tncc, nom, active) va INSERT INTO llx_c_regions (fk_pays, code_region, cheflieu, tncc, nom, active) values ( 232, 23208, '', 0, 'Nor-Oriental', 1); INSERT INTO llx_c_regions (fk_pays, code_region, cheflieu, tncc, nom, active) values ( 232, 23209, '', 0, 'Zuliana', 1); +-- Regions Peru (id country=181) +insert into llx_c_regions (fk_pays, code_region, cheflieu, tncc, nom, active) values ( 181, 18101, '', 0, 'Perú', 1); \ No newline at end of file diff --git a/htdocs/install/mysql/data/llx_20_c_departements.sql b/htdocs/install/mysql/data/llx_20_c_departements.sql index 029ed4299c3..fb7fa83517b 100644 --- a/htdocs/install/mysql/data/llx_20_c_departements.sql +++ b/htdocs/install/mysql/data/llx_20_c_departements.sql @@ -1283,3 +1283,31 @@ INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, nc INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LU0010', 14003, '', 0, '', 'Esch-sur-Alzette', 1); INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LU0011', 14003, '', 0, '', 'Luxembourg', 1); INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LU0012', 14003, '', 0, '', 'Mersch', 1); + +-- Provinces Perú (id country=181) +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('AMA', 18101, '', 0, '', 'Amazonas', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('ANC', 18101, '', 0, '', 'Ancash', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('APU', 18101, '', 0, '', 'Apurimac', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('ARE', 18101, '', 0, '', 'Arequipa', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('AYA', 18101, '', 0, '', 'Ayacucho', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('CAJ', 18101, '', 0, '', 'Cajamarca', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('CUS', 18101, '', 0, '', 'Cuzco', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('CAL', 18101, '', 0, '', 'Callao', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('HUV', 18101, '', 0, '', 'Huancavelica', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('HUC', 18101, '', 0, '', 'Huanuco', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('ICA', 18101, '', 0, '', 'Ica', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('JUN', 18101, '', 0, '', 'Junin', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LAL', 18101, '', 0, '', 'La Libertad', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LAM', 18101, '', 0, '', 'Lambayeque', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LIM', 18101, '', 0, '', 'Lima', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LMA', 18101, '', 0, '', 'Lima Metropolitana', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('LOR', 18101, '', 0, '', 'Loreto', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('MDD', 18101, '', 0, '', 'Madre de Dios', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('MOQ', 18101, '', 0, '', 'Moquegua', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('PAS', 18101, '', 0, '', 'Pasco', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('PIU', 18101, '', 0, '', 'Piura', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('PUN', 18101, '', 0, '', 'Puno', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('SAM', 18101, '', 0, '', 'San Martin', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('TAC', 18101, '', 0, '', 'Tacna', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('TUM', 18101, '', 0, '', 'Tumbes', 1); +INSERT INTO llx_c_departements ( code_departement, fk_region, cheflieu, tncc, ncc, nom, active) VALUES ('UCA', 18101, '', 0, '', 'Ucayali', 1); \ No newline at end of file From d69904c14d75f7176e9bd5e6dca648e2ca96be07 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 2 May 2016 15:51:18 +0200 Subject: [PATCH 585/834] Can set the home page of web sites --- .../install/mysql/migration/3.9.0-4.0.0.sql | 3 +- htdocs/install/mysql/tables/llx_website.sql | 1 + htdocs/langs/en_US/website.lang | 4 +- htdocs/websites/class/website.class.php | 24 +++++++---- htdocs/websites/index.php | 42 ++++++++++++++++++- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index a429ef5c7cf..adce0125c1c 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -115,11 +115,12 @@ CREATE TABLE llx_website ref varchar(24) NOT NULL, description varchar(255), status integer, + fk_default_home integer, date_creation datetime, date_modification datetime, tms timestamp ) ENGINE=innodb; - +ALTER TABLE llx_website ADD COLUMN fk_default_home integer; ALTER TABLE llx_website ADD UNIQUE INDEX uk_website_ref (ref, entity); CREATE TABLE llx_website_page diff --git a/htdocs/install/mysql/tables/llx_website.sql b/htdocs/install/mysql/tables/llx_website.sql index fd8ed4cc39c..8dd257ffa69 100644 --- a/htdocs/install/mysql/tables/llx_website.sql +++ b/htdocs/install/mysql/tables/llx_website.sql @@ -24,6 +24,7 @@ CREATE TABLE llx_website ref varchar(24) NOT NULL, description varchar(255), status integer, + fk_default_home integer, date_creation datetime, date_modification datetime, tms timestamp diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index a0b2106ac63..d94ac13cd05 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -17,4 +17,6 @@ AddPage=Add page Page=Page PreviewOfSiteNotYetAvailable=Preview of your website %s not yet available. You must first add a page. PageDeleted=Page %s of website %s deleted -ViewInNewTab=View in new tab \ No newline at end of file +ViewSiteInNewTab=View site in new tab +ViewPageInNewTab=View page in new tab +SetAsHomePage=Set as Home page \ No newline at end of file diff --git a/htdocs/websites/class/website.class.php b/htdocs/websites/class/website.class.php index 4b6d4e04245..a82a0833aba 100644 --- a/htdocs/websites/class/website.class.php +++ b/htdocs/websites/class/website.class.php @@ -63,7 +63,7 @@ class Website extends CommonObject public $date_creation = ''; public $date_modification = ''; public $tms = ''; - + public $fk_default_home; public $records; /** @@ -122,9 +122,9 @@ class Website extends CommonObject $sql.= 'ref,'; $sql.= 'description,'; $sql.= 'status,'; + $sql.= 'fk_default_home,'; $sql.= 'date_creation,'; $sql.= 'date_modification'; - $sql .= ') VALUES ('; @@ -132,6 +132,7 @@ class Website extends CommonObject $sql .= ' '.(! isset($this->ref)?'NULL':"'".$this->db->escape($this->ref)."'").','; $sql .= ' '.(! isset($this->description)?'NULL':"'".$this->db->escape($this->description)."'").','; $sql .= ' '.(! isset($this->status)?'NULL':$this->status).','; + $sql .= ' '.(! isset($this->fk_default_home)?'NULL':$this->fk_default_home).','; $sql .= ' '.(! isset($this->date_creation) || dol_strlen($this->date_creation)==0?'NULL':"'".$this->db->idate($this->date_creation)."'").','; $sql .= ' '.(! isset($this->date_modification) || dol_strlen($this->date_modification)==0?'NULL':"'".$this->db->idate($this->date_modification)."'"); @@ -191,6 +192,7 @@ class Website extends CommonObject $sql .= " t.ref,"; $sql .= " t.description,"; $sql .= " t.status,"; + $sql .= " t.fk_default_home,"; $sql .= " t.date_creation,"; $sql .= " t.date_modification,"; $sql .= " t.tms"; @@ -213,6 +215,7 @@ class Website extends CommonObject $this->ref = $obj->ref; $this->description = $obj->description; $this->status = $obj->status; + $this->fk_default_home = $obj->fk_default_home; $this->date_creation = $this->db->jdate($obj->date_creation); $this->date_modification = $this->db->jdate($obj->date_modification); $this->tms = $this->db->jdate($obj->tms); @@ -257,6 +260,7 @@ class Website extends CommonObject $sql .= " t.ref,"; $sql .= " t.description,"; $sql .= " t.status,"; + $sql .= " t.fk_default_home,"; $sql .= " t.date_creation,"; $sql .= " t.date_modification,"; $sql .= " t.tms"; @@ -295,6 +299,7 @@ class Website extends CommonObject $line->ref = $obj->ref; $line->description = $obj->description; $line->status = $obj->status; + $line->fk_default_home = $obj->fk_default_home; $line->date_creation = $this->db->jdate($obj->date_creation); $line->date_modification = $this->db->jdate($obj->date_modification); $line->tms = $this->db->jdate($obj->tms); @@ -353,6 +358,7 @@ class Website extends CommonObject $sql .= ' ref = '.(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").','; $sql .= ' description = '.(isset($this->description)?"'".$this->db->escape($this->description)."'":"null").','; $sql .= ' status = '.(isset($this->status)?$this->status:"null").','; + $sql .= ' fk_default_home = '.(($this->fk_default_home > 0)?$this->fk_default_home:"null").','; $sql .= ' date_creation = '.(! isset($this->date_creation) || dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').','; $sql .= ' date_modification = '.(! isset($this->date_modification) || dol_strlen($this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').','; $sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'"); @@ -595,13 +601,14 @@ class Website extends CommonObject { $this->id = 0; - $this->entity = ''; - $this->ref = ''; - $this->description = ''; + $this->entity = 1; + $this->ref = 'myspecimenwebsite'; + $this->description = 'A specimen website'; $this->status = ''; - $this->date_creation = ''; - $this->date_modification = ''; - $this->tms = ''; + $this->fk_default_home = null; + $this->date_creation = dol_now(); + $this->date_modification = dol_now(); + $this->tms = dol_now(); } @@ -625,6 +632,7 @@ class WebsiteLine public $ref; public $description; public $status; + public $fk_default_home; public $date_creation = ''; public $date_modification = ''; public $tms = ''; diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php index 98efd23f45b..1b7532bd0da 100644 --- a/htdocs/websites/index.php +++ b/htdocs/websites/index.php @@ -90,6 +90,7 @@ if (GETPOST('create')) { $action='create'; } if (GETPOST('editmedia')) { $action='editmedia'; } if (GETPOST('editcss')) { $action='editcss'; } if (GETPOST('editmenu')) { $action='editmenu'; } +if (GETPOST('setashome')) { $action='setashome'; } if (GETPOST('editmeta')) { $action='editmeta'; } if (GETPOST('editcontent')) { $action='editcontent'; } @@ -278,6 +279,32 @@ if ($action == 'updatecss') $action='preview'; } +// Update page +if ($action == 'setashome') +{ + $db->begin(); + $object->fetch(0, $website); + + $object->fk_default_home = $pageid; + $res = $object->update($user); + if (! $res > 0) + { + $error++; + setEventMessages($objectpage->error, $objectpage->errors, 'errors'); + } + + if (! $error) + { + $db->commit(); + setEventMessages($langs->trans("Saved"), null, 'mesgs'); + $action='preview'; + } + else + { + $db->rollback(); + } +} + // Update page if ($action == 'updatemeta') { @@ -393,6 +420,10 @@ if ($action == 'editmenu') { print ''; } +if ($action == 'setashome') +{ + print ''; +} if ($action == 'editmeta') { print ''; @@ -444,7 +475,7 @@ if (count($object->records) > 0) if ($website) { - print ''.$langs->trans("ViewInNewTab").''; + print ''.$langs->trans("ViewSiteInNewTab").''; } print '
    '; @@ -496,6 +527,7 @@ if (count($object->records) > 0) if ($pageid > 0 && $pageid == $key) $out.=' selected'; // To preselect a value $out.='>'; $out.=$valpage->title; + if ($object->fk_default_home && $key == $object->fk_default_home) $out.=' ('.$langs->trans("HomePage").')'; $out.=''; } } @@ -505,6 +537,12 @@ if (count($object->records) > 0) print ''; print ''; //print $form->selectarray('page', $array); + + if ($website && $pageid > 0) + { + print ''.$langs->trans("ViewPageInNewTab").''; + } + print ''; print '
    '; print '
    '; @@ -518,6 +556,8 @@ if (count($object->records) > 0) if ($pageid > 0) { + if ($object->fk_default_home > 0 && $pageid == $object->fk_default_home) print ''; + else print ''; print ''; print ''; //print ''.dol_escape_htmltag($langs->trans("EditPageMeta")).''; From 77494254687af3a5ba799d061c87034ae5307ed3 Mon Sep 17 00:00:00 2001 From: aspangaro Date: Mon, 2 May 2016 20:02:45 +0200 Subject: [PATCH 586/834] Modify right to access on chart of accounts --- htdocs/core/modules/modAccounting.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/modules/modAccounting.class.php b/htdocs/core/modules/modAccounting.class.php index 04fccce1285..f20cf663b2c 100644 --- a/htdocs/core/modules/modAccounting.class.php +++ b/htdocs/core/modules/modAccounting.class.php @@ -273,7 +273,7 @@ class modAccounting extends DolibarrModules $this->rights[$r][0] = 50440; $this->rights[$r][1] = 'Manage chart of accounts'; $this->rights[$r][2] = 'r'; - $this->rights[$r][3] = 1; + $this->rights[$r][3] = 0; $this->rights[$r][4] = 'chartofaccount'; $this->rights[$r][5] = ''; $r++; From e0f600f226c669575d8cd8f5e3427de33e1441ec Mon Sep 17 00:00:00 2001 From: aspangaro Date: Mon, 2 May 2016 20:21:37 +0200 Subject: [PATCH 587/834] FIX: Accountancy - Little fix et add page to assign massively the category --- htdocs/accountancy/admin/account.php | 1 + htdocs/accountancy/admin/categories.php | 157 ++++++++++++++++++ .../class/accountancycategory.class.php | 8 +- htdocs/langs/en_US/accountancy.lang | 2 + 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 htdocs/accountancy/admin/categories.php diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index 94ce450ff30..5bdaa3c5828 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -147,6 +147,7 @@ if ($result) { print '
    '; print '' . $langs->trans("Addanaccount") . ''; + print '' . $langs->trans("ApplyMassCategories") . ''; // print '' . $langs->trans("ImportAccount") . ''; // print '' . $langs->trans("CheckProductAccountancyCode") . ''; print '

    '; diff --git a/htdocs/accountancy/admin/categories.php b/htdocs/accountancy/admin/categories.php new file mode 100644 index 00000000000..f0013632b53 --- /dev/null +++ b/htdocs/accountancy/admin/categories.php @@ -0,0 +1,157 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/accountancy/admin/categories.php + * \ingroup Advanced accountancy + * \brief Page to assign mass categories to accounts + */ +require '../../main.inc.php'; + +// Class +require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountancycategory.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formaccounting.class.php'; + +$error = 0; + +// Langs +$langs->load("bills"); +$langs->load("accountancy"); + +$mesg = ''; +$action = GETPOST('action'); +$cat_id = GETPOST('account_category'); +$selectcpt = GETPOST('cpt_bk'); +$cpt_id = GETPOST('cptid'); + +if($cat_id == 0){ + $cat_id = null; +} + +$id = GETPOST('id', 'int'); +$rowid = GETPOST('rowid', 'int'); +$cancel = GETPOST('cancel'); + +// Security check +if (! $user->admin) + accessforbidden(); + +$AccCat = new AccountancyCategory($db); + +// si ajout de comptes +if(!empty($selectcpt)){ + $cpts = array(); + $i = 0; + foreach ($selectcpt as $selectedOption){ + $cpts[$i] = "'".$selectedOption."'"; + $i++; + } + + if($AccCat->updateAccAcc($cat_id, $cpts)){ + setEventMessages($langs->trans('Saved'), null, 'mesgs'); + }else{ + setEventMessages($langs->trans('errors'), null, 'errors'); + } + + +} +if ($action == 'delete') { + if($cpt_id){ + if($AccCat->deleteCptCat($cpt_id)){ + setEventMessages($langs->trans('Deleted'), null, 'mesgs'); + }else{ + setEventMessages($langs->trans('errors'), null, 'errors'); + } + } +} + +/* + * View + */ +llxheader('', $langs->trans('AccountAccounting')); + +$formaccounting = new FormAccounting($db); +$form = new Form($db); + + print load_fiche_titre($langs->trans('Categories')); + + print '
    ' . "\n"; + print ''; + print ''; + + dol_fiche_head(); + + print '
    ' . $langs->trans("Ref") . ''; - print $object->rowid; + print $object->ref; print ''; print $linkback; print '
    '; - print $form->editfieldkey("Date", 'date_start', $object->date_start, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); + print $form->editfieldkey("DateStart", 'date_start', $object->date_start, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); print ''; - print $form->editfieldval("Date", 'date_start', $object->date_start, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); + print $form->editfieldval("DateStart", 'date_start', $object->date_start, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); print '
    '; - print $form->editfieldkey("Date", 'date_end', $object->date_end, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); + print $form->editfieldkey("DateEnd", 'date_end', $object->date_end, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); print ''; - print $form->editfieldval("Date", 'date_end', $object->date_end, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); + print $form->editfieldval("DateEnd", 'date_end', $object->date_end, $object, $conf->global->MAIN_EDIT_ALSO_INLINE, 'datepicker'); print '
    '; + // Category + print ''; + print ''; + + + if(!empty($cat_id)){ + $obj = $AccCat->getCptBK($cat_id); + print ''; + print ''; + } + + print '
    ' . $langs->trans("AccountingCategory") . ''; + $formaccounting->select_accounting_category($cat_id, 'account_category', 1); + print ''; + print '
    ' . $langs->trans("AddCompteFromBK") . ''; + if(!empty($obj)){ + print ' - '; + } + print '
    '; + + dol_fiche_end(); + + print ''; + +if ($action == 'display' || $action == 'delete') { + + print ''; + + print ''; + + if(!empty($cat_id)){ + $obj = $AccCat->display($cat_id); + $j=1; + if(!empty($obj)){ + foreach ( $obj as $cpt ) { + $var = ! $var; + print ""; + print ''; + print ''; + print $form->formconfirm($_SERVER["PHP_SELF"]."?account_category=$cat_id&cptid=".$cpt->rowid, $langs->trans("DeleteCptCategory"), $langs->trans("ConfirmDeleteCptCategory"), "delete", '', 0, "action-delete".$j); + print ''; + print "\n"; + $j++; + } + } + } + + print "
    '.$langs->trans("Numerocompte").''.$langs->trans("Description").'Action
    ' . length_accountg($cpt->account_number) . '' . $cpt->label . '
    "; +} + +llxFooter(); + +$db->close(); \ No newline at end of file diff --git a/htdocs/accountancy/class/accountancycategory.class.php b/htdocs/accountancy/class/accountancycategory.class.php index 92ea01660ac..ef880aa8113 100644 --- a/htdocs/accountancy/class/accountancycategory.class.php +++ b/htdocs/accountancy/class/accountancycategory.class.php @@ -19,7 +19,7 @@ /** * \file htdocs/accountancy/class/accountancycategory.class.php * \ingroup Advanced accountancy - * \brief File of class to manage categories of an accounting account_category + * \brief File of class to manage categories of an accounting category_type */ // Class @@ -243,7 +243,7 @@ class AccountancyCategory } else { - $sql = "SELECT c.rowid, c.code, c.label, c.account_category "; + $sql = "SELECT c.rowid, c.code, c.label, c.category_type "; $sql.= " FROM ".MAIN_DB_PREFIX."c_accounting_category as c, ".MAIN_DB_PREFIX."c_country as co"; $sql.= " WHERE c.active = 1 AND c.fk_country = co.rowid"; $sql.= " AND co.code = '".$mysoc->country_code."'"; @@ -343,7 +343,7 @@ class AccountancyCategory { $sql = "SELECT c.rowid, c.code, c.label, c.formula, c.position"; $sql.= " FROM ".MAIN_DB_PREFIX."c_accounting_category as c"; - $sql.= " WHERE c.active = 1 AND c.account_category = 1 "; + $sql.= " WHERE c.active = 1 AND c.category_type = 1 "; $sql.= " AND c.fk_country = ".$mysoc->country_id; $sql.= " ORDER BY c.position ASC"; } @@ -351,7 +351,7 @@ class AccountancyCategory { $sql = "SELECT c.rowid, c.code, c.label, c.formula, c.position"; $sql.= " FROM ".MAIN_DB_PREFIX."c_accounting_category as c, ".MAIN_DB_PREFIX."c_country as co"; - $sql.= " WHERE c.active = 1 AND c.account_category = 1 AND c.fk_country = co.rowid"; + $sql.= " WHERE c.active = 1 AND c.category_type = 1 AND c.fk_country = co.rowid"; $sql.= " AND co.code = '".$mysoc->country_code."'"; $sql.= " ORDER BY c.position ASC"; } diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 64c0d88df48..46375e49ff9 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -149,6 +149,8 @@ FicheVentilation=Breakdown card GeneralLedgerIsWritten=Operations are written in the general ledger MvtNotCorrectlyBalanced=Mouvement not correctly balanced. Credit = %s. Debit = %s +## Admin +ApplyMassCategories=Apply mass categories ## Export Exports=Exports From cefb5f6c6b1876b21915f4e3aa4dfa2528622a73 Mon Sep 17 00:00:00 2001 From: Alexis Algoud Date: Tue, 3 May 2016 14:20:42 +0200 Subject: [PATCH 588/834] NEW hook in expedition card --- htdocs/expedition/card.php | 132 ++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index c52c72ff509..b4b82be7726 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1736,72 +1736,80 @@ else if ($id || $ref) { print '
    '; - if ($object->statut == 0 && $num_prod > 0) + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been + // modified by hook + if (empty($reshook)) { - if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->creer)) - || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->shipping_advance->validate))) - { - print ''.$langs->trans("Validate").''; - } - else - { - print ''.$langs->trans("Validate").''; - } - } - // TODO add alternative status - // 0=draft, 1=validated, 2=billed, we miss a status "delivered" (only available on order) - if ($object->statut == 2 && $object->billed && $user->rights->expedition->creer) - { - print ''.$langs->trans("ReOpen").''; + if ($object->statut == 0 && $num_prod > 0) + { + if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->creer)) + || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->expedition->shipping_advance->validate))) + { + print ''.$langs->trans("Validate").''; + } + else + { + print ''.$langs->trans("Validate").''; + } + } + + // TODO add alternative status + // 0=draft, 1=validated, 2=billed, we miss a status "delivered" (only available on order) + if ($object->statut == 2 && $object->billed && $user->rights->expedition->creer) + { + print ''.$langs->trans("ReOpen").''; + } + + // Send + if ($object->statut > 0) + { + if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->expedition->shipping_advance->send) + { + print ''.$langs->trans('SendByMail').''; + } + else print ''.$langs->trans('SendByMail').''; + } + + // Create bill and Close shipment + if (! empty($conf->facture->enabled) && $object->statut > 0) + { + if ($user->rights->facture->creer) + { + print ''.$langs->trans("CreateBill").''; + } + } + + // This is just to generate a delivery receipt + //var_dump($object->linkedObjectsIds['delivery']); + if ($conf->livraison_bon->enabled && ($object->statut == 1 || $object->statut == 2) && $user->rights->expedition->livraison->creer && count($object->linkedObjectsIds['delivery']) == 0) + { + print ''.$langs->trans("CreateDeliveryOrder").''; + } + // Close + if (! empty($conf->facture->enabled) && $object->statut > 0) + { + if ($user->rights->expedition->creer && $object->statut > 0 && ! $object->billed) + { + $label="Close"; $paramaction='classifyclosed'; // = Transferred/Received + // Label here should be "Close" or "ClassifyBilled" if we decided to make bill on shipments instead of orders + if (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT)) // TODO Quand l'option est on, il faut avoir le bouton en plus et non en remplacement du Close. + { + $label="ClassifyBilled"; + $paramaction='classifybilled'; + } + print ''.$langs->trans($label).''; + } + } + + if ($user->rights->expedition->supprimer) + { + print ''.$langs->trans("Delete").''; + } + } - // Send - if ($object->statut > 0) - { - if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->expedition->shipping_advance->send) - { - print ''.$langs->trans('SendByMail').''; - } - else print ''.$langs->trans('SendByMail').''; - } - - // Create bill and Close shipment - if (! empty($conf->facture->enabled) && $object->statut > 0) - { - if ($user->rights->facture->creer) - { - print ''.$langs->trans("CreateBill").''; - } - } - - // This is just to generate a delivery receipt - //var_dump($object->linkedObjectsIds['delivery']); - if ($conf->livraison_bon->enabled && ($object->statut == 1 || $object->statut == 2) && $user->rights->expedition->livraison->creer && count($object->linkedObjectsIds['delivery']) == 0) - { - print ''.$langs->trans("CreateDeliveryOrder").''; - } - // Close - if (! empty($conf->facture->enabled) && $object->statut > 0) - { - if ($user->rights->expedition->creer && $object->statut > 0 && ! $object->billed) - { - $label="Close"; $paramaction='classifyclosed'; // = Transferred/Received - // Label here should be "Close" or "ClassifyBilled" if we decided to make bill on shipments instead of orders - if (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT)) // TODO Quand l'option est on, il faut avoir le bouton en plus et non en remplacement du Close. - { - $label="ClassifyBilled"; - $paramaction='classifybilled'; - } - print ''.$langs->trans($label).''; - } - } - - if ($user->rights->expedition->supprimer) - { - print ''.$langs->trans("Delete").''; - } - print '
    '; } From 878ba07fcd0575956066b13a3998ef1f9381853c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 12:24:15 +0200 Subject: [PATCH 589/834] NEW The note on time spent can be entered when using the view per day. --- htdocs/core/lib/project.lib.php | 5 +++++ htdocs/projet/activity/perday.php | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php index 479e46258be..7cb0e35e463 100644 --- a/htdocs/core/lib/project.lib.php +++ b/htdocs/core/lib/project.lib.php @@ -666,6 +666,11 @@ function projectLinesPerDay(&$inc, $parent, $lines, &$level, &$projectsrole, &$t else if ($disabledtask) print $form->textwithpicto('',$langs->trans("TaskIsNotAffectedToYou")); print ''; + print ''; + print ''; + print ''; + print "\n"; } diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php index 7e23fe123d0..ada7e0bb7d4 100644 --- a/htdocs/projet/activity/perday.php +++ b/htdocs/projet/activity/perday.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2015 Laurent Destailleur + * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin * Copyright (C) 2010 François Legastelois * @@ -192,9 +192,10 @@ if ($action == 'addtime' && $user->rights->projet->creer) foreach($timespent_duration as $key => $val) { $object->fetch($key); - $object->progress = GETPOST($key . 'progress', 'int'); + $object->progress = GETPOST($key.'progress', 'int'); $object->timespent_duration = $val; $object->timespent_fk_user = $user->id; + $object->timespent_note = GETPOST($key.'note'); if (GETPOST($key."hour") != '' && GETPOST($key."hour") >= 0) // If hour was entered { $object->timespent_date = dol_mktime(GETPOST($key."hour"),GETPOST($key."min"),0,$monthofday,$dayofday,$yearofday); @@ -385,6 +386,7 @@ if ($usertoprocess->id == $user->id) print ''.$langs->trans("T else print ''.$langs->trans("TimeSpentByUser").''; print ''.$langs->trans("HourStart").''; print ''.$langs->trans("Duration").''; +print ''.$langs->trans("Note").''; print "\n"; // By default, we can edit only tasks we are assigned to From 7998cefdd216cf91ffe820515207dedc64ad56c1 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 13:15:00 +0200 Subject: [PATCH 590/834] Fix mark a deprecated function as deprecated --- htdocs/product/class/product.class.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index e5a70708c8d..2b109af5e68 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2948,9 +2948,9 @@ class Product extends CommonObject /** * Return all direct parent products fo current product - * + * * @return array prod - * @see getFather + * @deprecated See getFather */ function getParent() { @@ -3036,7 +3036,9 @@ class Product extends CommonObject */ function get_sousproduits_arbo() { - $parent = $this->getParent(); + //$parent = $this->getParent(); + $parent[$this->label]=array(0 => $this->id); + foreach($parent as $key => $value) // key=label, value[0]=id { foreach($this->getChildsArbo($value[0]) as $keyChild => $valueChild) From 911e862a8e88a703ea54ca2b7588f7d426b87c2e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 13:49:05 +0200 Subject: [PATCH 591/834] Fix missing translation and add a TODO to avoid infinite loop. --- htdocs/langs/en_US/products.lang | 1 + htdocs/product/class/product.class.php | 14 ++++++++++---- htdocs/product/composition/card.php | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 9298bdc9669..291f04f1c7e 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -130,6 +130,7 @@ AssociatedProductsAbility=Activate the package feature AssociatedProducts=Package product AssociatedProductsNumber=Number of products composing this package product ParentProductsNumber=Number of parent packaging product +ParentProducts=Parent products IfZeroItIsNotAVirtualProduct=If 0, this product is not a package product IfZeroItIsNotUsedByVirtualProduct=If 0, this product is not used by any package product EditAssociate=Associate diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 2b109af5e68..3cc72ac9b97 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2982,9 +2982,10 @@ class Product extends CommonObject * * @param int $id Id of product to search childs of * @param int $firstlevelonly Return only direct child + * @param int $level Level of recursing call (start to 1) * @return array Prod */ - function getChildsArbo($id, $firstlevelonly=0) + function getChildsArbo($id, $firstlevelonly=0, $level=1) { $sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type, pa.incdec"; $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; @@ -2993,13 +2994,18 @@ class Product extends CommonObject $sql.= " AND pa.fk_product_pere = ".$id; $sql.= " AND pa.fk_product_fils != ".$id; // This should not happens, it is to avoid infinite loop if it happens - dol_syslog(get_class($this).'::getChildsArbo', LOG_DEBUG); + dol_syslog(get_class($this).'::getChildsArbo id='.$id.' level='.$level, LOG_DEBUG); + + // Protection against infinite loop + if ($level > 30) return array(); + $res = $this->db->query($sql); if ($res) { $prods = array(); while ($rec = $this->db->fetch_array($res)) { + // TODO Add check to not add ne record if already added $prods[$rec['rowid']]= array( 0=>$rec['id'], 1=>$rec['qty'], @@ -3011,7 +3017,7 @@ class Product extends CommonObject //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']); if (empty($firstlevelonly)) { - $listofchilds=$this->getChildsArbo($rec['id']); + $listofchilds=$this->getChildsArbo($rec['id'], 0, $level + 1); foreach($listofchilds as $keyChild => $valueChild) { $prods[$rec['rowid']]['childs'][$keyChild] = $valueChild; @@ -3041,7 +3047,7 @@ class Product extends CommonObject foreach($parent as $key => $value) // key=label, value[0]=id { - foreach($this->getChildsArbo($value[0]) as $keyChild => $valueChild) + foreach($this->getChildsArbo($value[0]) as $keyChild => $valueChild) // Warning. getChildsArbo can gell getChildsArbo recursively. { $parent[$key][$keyChild] = $valueChild; } diff --git a/htdocs/product/composition/card.php b/htdocs/product/composition/card.php index f4b3f575eba..ee99226f2e4 100644 --- a/htdocs/product/composition/card.php +++ b/htdocs/product/composition/card.php @@ -265,7 +265,7 @@ if ($id > 0 || ! empty($ref)) print load_fiche_titre($langs->trans("ProductParentList"),'','').'
    '; print ''; print ''; - print ''; + print ''; print ''; print ''; print ''; From 568a43214dc0134e5ce607941bf9ce2a9d45afcc Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 13:51:47 +0200 Subject: [PATCH 592/834] Add code comment --- htdocs/product/class/product.class.php | 32 +------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 6919d722ceb..784ce9791cc 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2956,6 +2956,7 @@ class Product extends CommonObject $prods = array (); while ($record = $this->db->fetch_array($res)) { + // $record['id'] = $record['rowid'] = id of father $prods[$record['id']]['id'] = $record['rowid']; $prods[$record['id']]['ref'] = $record['ref']; $prods[$record['id']]['label'] = $record['label']; @@ -2974,37 +2975,6 @@ class Product extends CommonObject } - /** - * Return all direct parent products fo current product - * - * @return array prod - * @see getFather - */ - function getParent() - { - $sql = "SELECT p.rowid, p.label as label, p.ref as ref, pa.fk_product_pere as id, p.fk_product_type, pa.qty"; - $sql.= " FROM ".MAIN_DB_PREFIX."product_association as pa,"; - $sql.= " ".MAIN_DB_PREFIX."product as p"; - $sql.= " WHERE p.rowid = pa.fk_product_pere"; - $sql.= " AND p.rowid = ".$this->id; - - $res = $this->db->query($sql); - if ($res) - { - $prods = array (); - while ($record = $this->db->fetch_array($res)) - { - $prods[$this->db->escape($record['label'])] = array(0=>$record['id']); - } - return $prods; - } - else - { - dol_print_error($this->db); - return -1; - } - } - /** * Return childs of product $id * From 2f3a2303d44175582ecc82b2ab85bbe8570892e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Wed, 4 May 2016 16:49:15 +0200 Subject: [PATCH 593/834] put colspan in td instead tr --- htdocs/compta/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/compta/index.php b/htdocs/compta/index.php index 0f513412a93..f0a8c82df1b 100644 --- a/htdocs/compta/index.php +++ b/htdocs/compta/index.php @@ -199,7 +199,7 @@ if (! empty($conf->facture->enabled) && $user->rights->facture->lire) } else { - print ''; + print ''; } print "
    '.$langs->trans('ParentProduct').''.$langs->trans('ParentProducts').''.$langs->trans('Label').''.$langs->trans('Qty').'
    '.$langs->trans("NoInvoice").'
    '.$langs->trans("NoInvoice").'

    "; $db->free($resql); @@ -275,7 +275,7 @@ if (! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->facture- } else { - print ''.$langs->trans("NoInvoice").''; + print ''.$langs->trans("NoInvoice").''; } print "
    "; $db->free($resql); From 72695d1dee3dcebc45baa62596d15fd19872fe17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Wed, 4 May 2016 17:02:58 +0200 Subject: [PATCH 594/834] put colspan in td instead tr --- htdocs/comm/index.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/comm/index.php b/htdocs/comm/index.php index f1bf108f14c..59aa71b5595 100644 --- a/htdocs/comm/index.php +++ b/htdocs/comm/index.php @@ -197,7 +197,7 @@ if (! empty($conf->propal->enabled) && $user->rights->propal->lire) else { $var=!$var; - print ''.$langs->trans("NoProposal").''; + print ''.$langs->trans("NoProposal").''; } print "
    "; @@ -277,7 +277,7 @@ if (! empty($conf->supplier_proposal->enabled) && $user->rights->supplier_propos else { $var=!$var; - print ''.$langs->trans("NoProposal").''; + print ''.$langs->trans("NoProposal").''; } print "
    "; @@ -357,7 +357,7 @@ if (! empty($conf->commande->enabled) && $user->rights->commande->lire) else { $var=!$var; - print ''.$langs->trans("NoOrder").''; + print ''.$langs->trans("NoOrder").''; } print "
    "; @@ -438,7 +438,7 @@ if (! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->commande else { $var=!$var; - print ''.$langs->trans("NoSupplierOrder").''; + print ''.$langs->trans("NoSupplierOrder").''; } print "
    "; From 5c81feab7e2e1d24897e8eee4905c152215a3dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Wed, 4 May 2016 17:10:12 +0200 Subject: [PATCH 595/834] Add "no order" --- htdocs/commande/index.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/htdocs/commande/index.php b/htdocs/commande/index.php index 0aa828dc77a..fa1b2064ed2 100644 --- a/htdocs/commande/index.php +++ b/htdocs/commande/index.php @@ -216,6 +216,11 @@ if (! empty($conf->commande->enabled)) $i++; } } + else + { + $var=!$var; + print ''.$langs->trans("NoOrder").''; + } print "
    "; } } From 8b41970b2efa522b40143b530f9f45d1b2c5d146 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 20:19:53 +0200 Subject: [PATCH 596/834] Prepare upgrade of doliwamp --- build/exe/doliwamp/doliwamp.iss | 36 ++++++++++----------- build/makepack-dolibarr.pl | 55 +++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/build/exe/doliwamp/doliwamp.iss b/build/exe/doliwamp/doliwamp.iss index a837450f077..086968a66aa 100644 --- a/build/exe/doliwamp/doliwamp.iss +++ b/build/exe/doliwamp/doliwamp.iss @@ -32,7 +32,7 @@ AppPublisherURL=http://www.nltechno.com AppSupportURL=http://www.dolibarr.org AppUpdatesURL=http://www.dolibarr.org AppComments=DoliWamp includes Dolibarr, Apache, PHP and Mysql softwares. -AppCopyright=Copyright (C) 2008-2015 Laurent Destailleur, NLTechno +AppCopyright=Copyright (C) 2008-2016 Laurent Destailleur, NLTechno DefaultDirName=c:\dolibarr DefaultGroupName=Dolibarr ;LicenseFile=COPYING @@ -80,7 +80,7 @@ Name: "desktopicon"; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm: Name: "{app}\logs" Name: "{app}\tmp" Name: "{app}\dolibarr_documents" -Name: "{app}\bin\apache\apache2.2.11\logs" +Name: "{app}\bin\apache\apache\logs" [Files] ; Stop/start @@ -101,10 +101,10 @@ Source: "build\exe\doliwamp\UsedPort.exe"; DestDir: "{app}\"; Flags: ignoreversi ; Put here path of Wampserver applications ; Value OK: apache 2.2.6, php 5.2.5 (5.2.11, 5.3.0 and 5.3.1 fails if php_exif, php_pgsql, php_zip is on), mysql 5.0.45 or 5.1.36 ; Value OK: apache 2.2.11, php 5.3.0 (if no php_exif, php_pgsql, php_zip), mysql 5.0.45 or 5.1.36 -Source: "C:\Program Files\Wamp\apps\phpmyadmin3.2.0.1\*.*"; DestDir: "{app}\apps\phpmyadmin3.2.0.1"; Flags: ignoreversion recursesubdirs; Excludes: "config.inc.php,wampserver.conf,*.log,*_log,darkblue_orange" -Source: "C:\Program Files\Wamp\bin\apache\apache2.2.11\*.*"; DestDir: "{app}\bin\apache\apache2.2.11"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,httpd.conf,wampserver.conf,*.log,*_log" -Source: "C:\Program Files\Wamp\bin\php\php5.3.0\*.*"; DestDir: "{app}\bin\php\php5.3.0"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,phpForApache.ini,wampserver.conf,*.log,*_log" -Source: "C:\Program Files\Wamp\bin\mysql\mysql5.0.45\*.*"; DestDir: "{app}\bin\mysql\mysql5.0.45"; Flags: ignoreversion recursesubdirs; Excludes: "my.ini,data\*,wampserver.conf,*.log,*_log,MySQLInstanceConfig.exe" +Source: "C:\Program Files\Wamp\apps\phpmyadmin4.1.14\*.*"; DestDir: "{app}\apps\phpmyadmin4.1.14"; Flags: ignoreversion recursesubdirs; Excludes: "config.inc.php,wampserver.conf,*.log,*_log,darkblue_orange" +Source: "C:\Program Files\Wamp\bin\apache\apache2.4.9\*.*"; DestDir: "{app}\bin\apache\apache2.4.9"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,httpd.conf,wampserver.conf,*.log,*_log" +Source: "C:\Program Files\Wamp\bin\php\php5.5.12\*.*"; DestDir: "{app}\bin\php\php5.5.12"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,phpForApache.ini,wampserver.conf,*.log,*_log" +Source: "C:\Program Files\Wamp\bin\mysql\mysql5.6.17\*.*"; DestDir: "{app}\bin\mysql\mysql5.6.17"; Flags: ignoreversion recursesubdirs; Excludes: "my.ini,data\*,wampserver.conf,*.log,*_log,MySQLInstanceConfig.exe" ; Mysql data files (does not overwrite if exists) Source: "build\exe\doliwamp\mysql\*.*"; DestDir: "{app}\bin\mysql\data\mysql"; Flags: onlyifdoesntexist ignoreversion recursesubdirs; Excludes: ".gitignore,.project,CVS\*,Thumbs.db" ; Dolibarr @@ -116,10 +116,10 @@ Source: "*.*"; DestDir: "{app}\www\dolibarr"; Flags: ignoreversion; Excludes: ". ; Config files Source: "build\exe\doliwamp\phpmyadmin.conf.install"; DestDir: "{app}\alias"; Flags: ignoreversion; Source: "build\exe\doliwamp\dolibarr.conf.install"; DestDir: "{app}\alias"; Flags: ignoreversion; -Source: "build\exe\doliwamp\config.inc.php.install"; DestDir: "{app}\apps\phpmyadmin3.2.0.1"; Flags: ignoreversion; -Source: "build\exe\doliwamp\httpd.conf.install"; DestDir: "{app}\bin\apache\apache2.2.11\conf"; Flags: ignoreversion; -Source: "build\exe\doliwamp\my.ini.install"; DestDir: "{app}\bin\mysql\mysql5.0.45"; Flags: ignoreversion; -Source: "build\exe\doliwamp\php.ini.install"; DestDir: "{app}\bin\php\php5.3.0"; Flags: ignoreversion; +Source: "build\exe\doliwamp\config.inc.php.install"; DestDir: "{app}\apps\phpmyadmin4.1.14"; Flags: ignoreversion; +Source: "build\exe\doliwamp\httpd.conf.install"; DestDir: "{app}\bin\apache\apache2.4.9\conf"; Flags: ignoreversion; +Source: "build\exe\doliwamp\my.ini.install"; DestDir: "{app}\bin\mysql\mysql5.5.12"; Flags: ignoreversion; +Source: "build\exe\doliwamp\php.ini.install"; DestDir: "{app}\bin\php\php5.6.17"; Flags: ignoreversion; Source: "build\exe\doliwamp\index.php.install"; DestDir: "{app}\www"; Flags: ignoreversion; Source: "build\exe\doliwamp\install.forced.php.install"; DestDir: "{app}\www\dolibarr\htdocs\install"; Flags: ignoreversion; Source: "build\exe\doliwamp\openssl.conf"; DestDir: "{app}"; Flags: ignoreversion; @@ -195,10 +195,10 @@ end; procedure InitializeWizard(); begin //version des applis, a modifier pour chaque version de WampServer 2 - apacheVersion := '2.2.11'; - phpVersion := '5.3.0' ; - mysqlVersion := '5.0.45'; - phpmyadminVersion := '3.2.0.1'; + apacheVersion := '2.4.9'; + phpVersion := '5.5.12' ; + mysqlVersion := '5.6.17'; + phpmyadminVersion := '4.1.14'; smtpServer := 'localhost'; apachePort := '80'; @@ -329,10 +329,10 @@ begin winPath := ExpandConstant('{win}'); pathWithSlashes := path; StringChange (pathWithSlashes, '\','/'); - datadirold := pathWithSlashes+'/bin/mysql/mysql5.0.45/data'; + datadirold := pathWithSlashes+'/bin/mysql/mysql5.6.17/data'; datadirnew := pathWithSlashes+'/bin/mysql/data'; - exedirold := pathWithSlashes+'/bin/mysql/mysql5.0.45'; - exedirnew := pathWithSlashes+'/bin/mysql/mysql5.0.45'; + exedirold := pathWithSlashes+'/bin/mysql/mysql5.6.17'; + exedirnew := pathWithSlashes+'/bin/mysql/mysql5.6.17'; // If we have a new database version, we should only copy old my.ini file into new directory // and change only all basedir= strings to use new version. Like this, data dir is still correct. @@ -1002,7 +1002,7 @@ Filename: "{app}\rundoliwamp.bat"; Description: {cm:LaunchNow}; Flags: shellexec [UninstallDelete] Type: files; Name: "{app}\*.*" -Type: files; Name: "{app}\bin\mysql\mysql5.0.45\*.*" +Type: files; Name: "{app}\bin\mysql\mysql5.6.17\*.*" Type: filesandordirs; Name: "{app}\alias" Type: filesandordirs; Name: "{app}\apps" Type: filesandordirs; Name: "{app}\bin\apache" diff --git a/build/makepack-dolibarr.pl b/build/makepack-dolibarr.pl index 8a70ea73f2c..45c8e0a6f42 100755 --- a/build/makepack-dolibarr.pl +++ b/build/makepack-dolibarr.pl @@ -595,9 +595,12 @@ if ($nboftargetok) { if ($target eq 'TGZ') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/standard'); - if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } - + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/standard'); + if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } + } + print "Remove target $FILENAMETGZ.tgz...\n"; unlink("$NEWDESTI/$FILENAMETGZ.tgz"); @@ -624,8 +627,11 @@ if ($nboftargetok) { if ($target eq 'XZ') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/standard'); - if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/standard'); + if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } + } print "Remove target $FILENAMEXZ.xz...\n"; unlink("$NEWDESTI/$FILENAMEXZ.xz"); @@ -658,8 +664,11 @@ if ($nboftargetok) { if ($target eq 'ZIP') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/standard'); - if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/standard'); + if (-d $DESTI.'/standard') { $NEWDESTI=$DESTI.'/standard'; } + } print "Remove target $FILENAMEZIP.zip...\n"; unlink("$NEWDESTI/$FILENAMEZIP.zip"); @@ -696,8 +705,11 @@ if ($nboftargetok) { if ($target =~ /FEDO/i) { $subdir="package_rpm_redhat-fedora"; } if ($target =~ /MAND/i) { $subdir="package_rpm_mandriva"; } if ($target =~ /OPEN/i) { $subdir="package_rpm_opensuse"; } - mkdir($DESTI.'/'.$subdir); - if (-d $DESTI.'/'.$subdir) { $NEWDESTI=$DESTI.'/'.$subdir; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/'.$subdir); + if (-d $DESTI.'/'.$subdir) { $NEWDESTI=$DESTI.'/'.$subdir; } + } if ($RPMDIR eq "") { $RPMDIR=$ENV{'HOME'}."/rpmbuild"; } @@ -779,8 +791,11 @@ if ($nboftargetok) { if ($target eq 'DEB') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/package_debian-ubuntu'); - if (-d $DESTI.'/package_debian-ubuntu') { $NEWDESTI=$DESTI.'/package_debian-ubuntu'; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/package_debian-ubuntu'); + if (-d $DESTI.'/package_debian-ubuntu') { $NEWDESTI=$DESTI.'/package_debian-ubuntu'; } + } $olddir=getcwd(); @@ -979,8 +994,11 @@ if ($nboftargetok) { if ($target eq 'APS') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/package_aps'); - if (-d $DESTI.'/package_aps') { $NEWDESTI=$DESTI.'/package_aps'; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/package_aps'); + if (-d $DESTI.'/package_aps') { $NEWDESTI=$DESTI.'/package_aps'; } + } $newbuild = $BUILD; $newbuild =~ s/(dev|alpha)/0/gi; # dev @@ -1062,8 +1080,11 @@ if ($nboftargetok) { if ($target eq 'EXEDOLIWAMP') { $NEWDESTI=$DESTI; - mkdir($DESTI.'/package_windows'); - if (-d $DESTI.'/package_windows') { $NEWDESTI=$DESTI.'/package_windows'; } + if ($NEWPUBLISH =~ /stable/) + { + mkdir($DESTI.'/package_windows'); + if (-d $DESTI.'/package_windows') { $NEWDESTI=$DESTI.'/package_windows'; } + } print "Remove target $NEWDESTI/$FILENAMEEXEDOLIWAMP.exe...\n"; unlink "$NEWDESTI/$FILENAMEEXEDOLIWAMP.exe"; @@ -1195,8 +1216,8 @@ if ($nboftargetok) { $command="rsync -s -e 'ssh' \"$file\" \"".$destFolder."\""; print "$command\n"; - my $ret=`$command 2>&1`; - print "$ret\n"; + my $ret2=`$command 2>&1`; + print "$ret2\n"; } } } From 1af8ca458d90d8728a75b68b51613e39a3a1473b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 29 May 2010 10:22:00 +0000 Subject: [PATCH 597/834] Better image Conflicts: build/exe/doliwamp/doliwamp.bmp --- build/exe/doliwamp/doliwamp.bmp | Bin 52642 -> 52574 bytes build/exe/doliwamp/doliwamp.iss | 2 +- build/exe/doliwamp/my.ini.install | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/exe/doliwamp/doliwamp.bmp b/build/exe/doliwamp/doliwamp.bmp index 87191204d8c3c761212b3b902091a976bc9a495e..52b1320d1113e36e744c5ebb2f1aaa5b397e5b0f 100644 GIT binary patch literal 52574 zcmeHvdvsgXneU!;FTFDicV;bSx>jjJZ!+{@*4Tj?r>x5)^<&x!LSkIs+IOa@JxmK7 zM~#V*Wk?i=>>>rQkz2=h9m@g5F$%JCWXB1yfEYazOoC+;#gHHy3k9%jH)4yZ$p{S#VkL`@273>`&OA za(dPXW4V7Wejri+Gv57Omi?XYu&3_7hixhT346Bm+w4a_EM=QYo@V?0=^pm0e=cJW z{mW0;&$j)99jGj0+MoS|S*l9dw(V-R>ydwC?GKl;j-P2*wdP^g@Q9j~)$U}6AN?n0 zseOn&{rCgyajk(pwyT!?@DVfn`R*F_tuMSF4Ft zKeoW6reCqnHY4k5e}X;O`W)NaVrD%ZI@Z&vWrq)KW2ZXwth1$?b?$Fr-TU{kw*Bqw zm-{=J?fEBJ_jAv(C(S+Vsip~b`~@Q$IrJ3Uf3S-MZ9CX5%Td|l2n`^5`~*^Av)cHsF}*{@!(F-vzZ;yB2je(p_XY!9*XJ&o*{!-&In zi0wJ@B0Jh^W)@oyJ9y{}dq?^eGrcgv9vSjZa~`)aNB>cF;DnX+^}od2C(O(?aEzTea+-AyIN0Ec z9yT<1m^ohRX9F)CW5d z>G^r~uOI)0&3_zc%gH$wPbOIW<1~vVudt=`$LwGKZGl}$FR_L6C+y#r7ul8neU-Nf zSCvaaKtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3 zKtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3 zKtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3 zKtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3KtVu3 zKtVu3KtVu3KtbUDYXnRW{ZOr~sM1zdRH-Za98Wg(JB~a%WO{7Znf(u*aTw~ILytZ) zWHX=WZyoB}-*m`fH1;9f;p{eT*;1vhQq|x4_f=JDRsHw#wOVz4Yej`tU6p6ukXJ!r z-@ikpt|~D+bjJ_CTCw~4->*<>i|aRRpuldY)75ii*y(V-?i)Sx{44J~?|i6dRPs4& z&fSk#U$5vJ9d(V4`lxUYt^M&Gx8$t8`R1I}Ii;?C{oj@E-+%8jtN-K%>zBXqxbDHf zvg*Ek$I!YjnjgLQ(Vjc*{e`r*%yIAi&p3yyzOpYGOS?5^z*cHd_r3U&2P@jN!w=ru z{ivbBrMhdKwpG`6{}=9m$k1l`=P!Kg(W+M6i}!!?8EtFD=!uV>R<~-p-5%!;?|)Fe zHD8^ledNbaJpTB%{?nKK=SE{icKuzqeE-LPQLn1dmTE>x#uIn^_(v6`nznj_pHhl%+w6O{_toF~;IOm*b@Qz@ zgF`#?h34*?Ej0LJATyGU!`lU zFmM?s&Ybg2&^6~VoTCV~diu`6+wDUMPF%D(tX8Yd zWbL+DZB~n^)w=(e-L2hLQ-2%$wl-@&tZr++gK!-7A^LN^QCPS~edjLXM>i}lEc`fu z+`dt8%KnKARj{vw@VL(L&iir-eSV%cM`KW}&R3aowArfCe47(>V~88DKkXC*$B=U+ zc*yA>9tTl4S-dGT4D~<#t^Gayt@V#OUvGW<{+IjjTYsOc&01C8YHI5TU?!8PwclYf zwdqXtR+rUQ-)Pmg8VpWe)~2d9ZN0X&wO^PHt>IZ~tM0JYV5sVs1%xF(Wbt3dPd;%A zdM2D~9_APpFFS3H2Oo6wcWWPYxVn$*AMM`u?Omg#s=U>!@5zX(UGjbC<~4bP{pJ|fVs|5N1O_GDh{`>mi%2m+ioo5cm&Y6D6( z2eCTM&_)v+@NqFfuytE(-QdlLGfXlrCDV)P91fcQy|BJU;-znaZBWd`*{p-aY*Dyxr}&|>jFm;%CrCx z1@IhE76cqPb@Cm@=`+ta1df zxFYeg);BcNR2J0~<<@K}DzB;0>$lTW)39BS#AFFF(Olp72paM5#L@0#Za73fC?r!+ zxSMMxXc_y{l3$BU(CL*WUauQEj@J|w=4R!VuP-Pp*xs~P-}G=(15A>Uk|h&mf;#A)4VPhRMt&RO$|-k8*18{^zFFd zsVLUSk|9X}l&E{6MbC(zL0ja9mrO<-xIR|yY==s=x9@1`92^)t zrGf&4B9Vnaf->;CuVo^~t}eU%DKIxm_Bav3@W@xxR~arg z?@?sM5xOh$$)(Qgb;F*9h9?^uAm@`X4v~yuQ;SL6BE=$!NHms+#O83%%fG}N$oNHl zLAxgsu(-_zV>lQwn-Y-#ar*6QqtRy9v_v4Is#sehbdrRk=8+=@gZEqnn0PW|!F;Vn zf}oV`fjM!_&&|z$*mY6`V)%e$Z!~-3snqX#5D@TNS`tCG`Lx9z2`wCO#}k2tR4OIe z>TI@?Hlv$xRLxB4W>D1>l{Lv_JOw$a@bFt+IBq8#m#;4W zk%xl$Pv-lC>`#_ww;GGn9a9^6xJ4~~$G|KV#nv6MpHMumtkAkTr~naFNARW7H{NV!8nWx6IAxP*MNh@$Z! zlZ4Uh>-1S!>o;Ys&n?)Y@8B{5l}`vw+*gDRuXuQUL7B!=iHs`1rc%ECLKw*?eOqe z2HQ__8OO}M9&@kxpt)D-l>$P_j5&k1=IO;hd@Z9TLY;~121;^ekQGs`-130pdLU)d zGPr^tp_I%Z8B~J!C68N3#wDq&%)HIKu58;jqj672KpD#j3BDM;^M|ixw3*Pyn1NCv zA@PtrbaOv>MYqbmD?Bng?}li6NXZQTYjR;RxqwnwSp4;CBl&?j&jNZEMDw1jD~}BlS_X+ zM@CGp!X!4tLA~Doy_$le^;zo+atrj^yLegqo1urzX7iqAv(Yd0`kEypTnCN3)IS@J zysTAWw2v4Q$;0RL5|@cenEwdkG=+0TAce?`sfdGzCXw3Js}1c%P3W$h=xJ&g;B_WE zhtj3zaz+WSv+>qv(8sG(*2Ob3bzyX4({p5o=cgm&n2t=tLj(%K6^>3%N21|qgu*f* z({r2+(Lf*3K`f#PpVqC_KqpuEj3?3Y4#zdyGi}!`; zk#+xKT*n(5YRyp?(^RkJX8beiBX1uCjxuaQD&F|ON&2>k5q=sEiOEAb#1ulUCuJ_h zn~<)ijNNS0ji%1nZ7$cZK zAT#PaA@#=6%a~FS`AU-J7T}{PL>xb%kL}I-nmU`?+xO9IX5VpMuSd3(8O!jjGk?8o zTUm2i*|v2D%GQ0`%!Nez7}m9@&7Tp+beuPZF&en0QdFdTigP7~l?O=a51pu8j;2v< zs0Jm|aWaI5G8gAf$c`PA_V~N;YB1#ho%p1sz{+0`TG^Q{h zDXIi8Gx$USEN^ZINZ;g%S`tWzw3v=k5?n-H_@d4I#-<; zEtlJ9F`M0HNiv(IT8|`2o>~u_lDW3_XIqRevt%X$$VSj87?*jatOpJb4AAdj*TB(% zfmid;{4KLv_p-0v!yfp2McrGrXB`^WF?950x30sxLLbA@F*90aQpRZ7bD~Fz#eAsC zv9S|9wPUeqkH;TFE7{|Tg+T^_XpEp}s8rKXP*G>}JM=qt*654$dVS4KJ)h4kzYtxX zOux7H^TIeLz3J(dqP>`67@JAPY{M3{XbyP=K-=v3sK*>78K={s6Vkd`XgyU7zpvIG z4f}d(Ln*365E1o_H-QH3$Z@;$dMpMCs2HugD%JUw=D zOzKUVo11%$+fdvAmGrEdH*?gckT7YFug9F8p7hm*W55eCQY)E?`Fy}J>G$~KDgQLd zNJ9;JMT{x*<>&&-+p!+iRoN-GkL=d5|Nc%d`<(hX?HRk0439};W7ECUV}zs1X((Aj zYXVd_DCs!K7>oG{#{f(Z73y*zAEc$SvRiw%36}~ z>VLgw>`xz{vd$#?x$J5T?8??*VvMrhtxhBsFj7dPUZZZHJ4PQJGn-7|=p@Gx{&*sd zETHCtEh##1>VXy#z*Qcb4PXU;u0bq64!%WgkKDff$0X!$i&q1xAyY{z#ur(lFfnL^ z7#JQxMdL9)$ryz&G?qjtwN5a3+XH!{>3MYVqV3tabH@(+aPO?#xpN=K@pHlU`5YEL#Ss#g<*5{ZptdWdDYi$%eraR zn!L{?;~zmfpi-4+X;emXka(ny)C^HAN`-g@<^*|U#5LB>WfaPbu_o8Y%1xUp^-YyK zH|^Y6v$Jwvw!zqFY&5|z7-hp`-2IfX5melu!ezn(4=izmgA)+S>F^RqX=fBVX(v79zvbQW=l~F=dWJvZ12QO0Yh*y+6UEnd3hV@R&68~ znH$O6xIxf1=8=PVh-m}oAOa^6pSV>U$x+hMs9EG`^Riw9q!5xfgz30wdmv0YNekiE zwV<^aOh@9B7kSRLV`pV~d1XyyO#{Z^?F04}UvPGEHW-?m4TNSVLqV_IZnM*6#?N-l zc8Gfb~M) zO_0wOcwdh-V`&cKye~)4uK=g+goZ+n#_&1r_k|0E5#VMYjAL|9Y21Z za+~blP;fRd6Z9vRaHai?9=8V%zrC`}eHr3)HV=25#_c5ow*hSmAi}H_O$(NHaS4+* zNx~UL`$%mMwLY=iH{RzrJ!PuXxJf4x97hbmY1@M~KClCF-0jv;MQDM}KgBDA$Lsgv zkqvUtecngUzaw(5@BF9O8__IM)lQ}+Jps2z!pm;S1Awa~m&fn%PR=3_Z6uVEpiHLM z*6v=CESg;F;nB;K6 z4I2fF9}y&qNwOQnW*d~ojOL;R)!tU3)))<2&1$u&r38C9e8@qK6R+3F+q1VUv{)p& z*+aV~3AryUUxOYyw;VHuu7TIQ{)wXpT72gxI{vO`#P6Rt4_y2~zu)cmdxODX0B&yp zlo7x0{CR|if}!AK5DdQar%&3?Pxzafy8M1{O`P{nz^+?48zF`jsc0k=P9(!9Sv2mk zbTmX5V();Gw0y6d(5t9+y=S!|{RRr$*1a_xQHj{r}c0tBEiKS z#QGy<)<~a^yvWU~FNQTVjDP8y9CV~dC0Npc)(uzQU~TrA_(VEcNX zH|X7*x6zGu=N|{=rM4;GEc7QB_Db%6X;E5=m_scy;b3U{JzcX8|Lwqei_UCO8yAp^ z!0cd0)4Q|bmo9Wn&W304;116=3Unwrl8yusk}dO@Kjh;Q5|A_=qgIJ-kr&9%anwJo zUw_*gjC~3U8ur!<1ZN|crcTaAA`xk;B@ju>MiPP9L^u)*$672v6af(|pj|pO6?^y8 zY*1HcvKTG4$UJCs$xGo#;=+ZuXn!%12q&=L7!f#9x-nW~UP^`uG!l%&qN((Iz>sFt zM8-k|Fo&PoZ+c10UG)t$ML-Z3?$p;bwU12ry`+S&+^Ev1hCciz{G@x_0S^y`heElB zxMec&k{jB~-!?2RgiA^i>10S}gplS9)sPP>aS*813@AgjTOg`=gNMxO$;E{QaHP|L z`%KV_On(L_vmJzm&$m&faYjUbQjwCkkPgABOb zZWkIKP~}`?Q{JXqVq zkWuOx^Ms`ybI22rdZe*X6o&}>C~sND2Gkn95eFEz54i0fG_G!!bZnSlbK6eB97V>w zz!VO2Djv7j>mnYYirjj=z7ePa!eM(!A%HAE^^k`642$8=dk3*ng8GB5&@+iS7pBuR zclFfP8u{8@G);4^Sj=qZb61+rpqTkGpFF>=(HE@0tsrYnZb5^-W?*ya)7fh=rpeD+ zo2SZCtyOJN73Xa%F5XgFTwGF7QlW-XQe3iCU81H-^HYtcTBFnH>UP%| zKoa=T%`sF1K*G>ar!zDHMH>1-z7~_kY91c8R2U>{qi!iOCU9I%0hWNr6Hd^w1aRcz zaDol1xgJkMjN#;U3W!HzT)DqS`&dx31LMac82YAxfll-{T?5Y#bk5?MoSlutA~Cde zWX8~L#v+jf+@Q}OwFsI*7%|!)3C+Sqj0tJdG7~0-@QgnZhK&AHn6K8;*d~?;zkUM4 zSekQ9M?DvNu=c~(>9LldjLmyM4o4H9VD=-AarI3#1-Z0;U5JxNH8?cY{xE(udu#Ob z$5g9}tHbEp(Vp^NEGdcc>B8bnouqb?2Q4jaQrJCe)A$^@sL!1Xd8q}a@ML;U>NSQI z0AgnRn21X@^GuTF3RokR%r-u%ByAw@!U#lJqqJja&gFW&UBA9yQ_-fP!lI%A6nsNX z6N&h%y}$ZR5ZZ5_l%jj|>e2`860-)x~Rmt|S>=p?T*=(T~a$OT0!<4p;WQ^s+74XVtJ z9lZT%Li1At@t}?kH3@ItU#u+-Q!h{H!t~g=icvGNv}iktvX%p=7)|4sDLcd*5BZ@6 zvqF;ve4cHt6KnL)s&8)`Qy zYzoKe!U0-Kp{+OCokJk))}_z~3PLOyC!M&aj~MwB0XWoXJul_Ggr$D|a%ArE+&GkS zWmh>o1t(WdSfLf=ByHsSNN7{Ulc%X6AbFFt_d(dwUTQ|qh%JL7n0P~o9R+%qyNR8q zi@=f3xuB0ln;Nz^=uuOeYS8J3N=m!Ces4_HHsH9H&VQL~tQvLjV0z>KW zLNc{Lt4XOeRB0ZOA_lHvbF@;2oWqMVnX!0yF0*r&zC@Ecyg_(y*m&Bs?+OZ~jk4xA z3X!PoCo%Ekfyf0H7qIa8Ov87xzOlZjpy=!Caqgp#CfR$@lJhzF$R$D)yNVh)tq%LtmAk4EM(Ihdb^Qi_x2i?P{2kmNv8SE%4bd1lIroBx~d)wPHPs6-hSFF~FhM8CG zaEm*JgW5^I+iW+xjb7xP$DUpccqYB}aq5x?jgV>F!fPk0G4|C_ae;dr=M+)Vv3E|L zF>s&ud3^IR@>9J=)#rsFn@QSZCmrBx0EM}?+<4nfH{HDID-GCM=)$5883XMD4fEcu zc_r%0BnBl8HEfiywz`l$>9^Pv& z5Z?5=)?X8L{NMpZqV-2oJc6345`s8|cm$4^tg3Iv!X+jG>M+S4=XFMpAw^Lk&0TA- z5Lttz$V>y2aI02tE}2E=Avb%0S>D`Wwb)kIh}l~_6Oc?x?w?B3B_VO%j(Q0}zyZk! zP1H}`KB9^cq$DJjwOqic?m~0~Ci3cxX{fE$*5)`ohTN zOVrQd)D`ao!_@uBy&?65nQoDv%);0VKtMK6OUw&(pogj_YA$uqvH6+M>})6$nT>>I z0^y(~G&wah8{u`F8`u;dnQwf2yk{M$SfhfpS>8MT2z=i$#Ye02GCgv4)1W&_;*{5c$xD z#BHic#4ROtrpx9M6V80sX-W>LjsAq$v{fT*Rqx(at@DJ6x9Zf@#kO4~EhQ!$PQQud zkfQvo_~}?rIvh$R!gGlPCg|Zn;$$ooOhsoRLO#la(E7Y@Z%!XG8;+U1JQiNBF{UUi z-%j%3zZ9r1yvkr`0T~lm+)qX@cV7r&2%k#fSWv{XfZC2SSXfAeQ(@F<)N9-@V{^QH zq}>13kCI;@0xa&Po*!hP(82>Gi*Mf?z{;M<=tc-y5Ze3DV_gsP7#dVSFbpPWJkd^* zuLZ7z;mf3eeDF0lcNr>5UjAIB5NM)!aC9)^;3OK*Q4qJe1BMvn$yClxZZx{rNP&@& z5zl$tAqoi7_T^Q~{L6Rjz@KL}q0CWnsGNZ%#%4=PVah0}3uTE6)K*@xaoCc30Rt~9 z@DLxtLIyce;PQXW1O-T6;m6!4l;VXU|4ECt1|q3UpoolOA{iTsa@VXcDqN5Cl-ml? zVH1k@6AhO9mfn3}?=+?qsB{ZxvXf*kpdU`brvIMcSUA@_IZcWX&MQ=|WQ!Ap!Xp=M zrc=#GAm@+gDPS8dYEFA07roD_ofuWG?yjmDh-g zXOh3qk7ZrmbPLXM;UvWx8ccNII2WF-4jf%*S{Uef|NUR5FI|p|Ljx0Ym#2brn9t6m zf4p@5($vU24T-oANFY<@lw?LqP%}u&#mVKOd=c20yznN6i$(H@H3gm>E-T@e?)*^P)RTfBb&>W ziG#nv1>rBi#Y>$>%nOM6T9WbY3-4YSzi|EnUFR>n_4Wu%_};$oT0AkIz*u}ff*%~* z#u>Os90W+r<6XcYcPD(5b7*I_-DZt{`csH@`v_G8BmSuJ)f>C1Bu_ZIZEV{~Krj;n51MwyLTsuU=hWuc?4b zqpfc;c2fhh_FDa1U1_J)p4yU!h z-qcDmnvQ(*(c@pb^Gn4!t5@gbXXj_<6j9Fp6&^WbSi5`t*YL+&91Tofgv<@BSZnA8}P=ug?DxxZx}{tj_tfyH>06 zSKm_AYHGBY@W}@f4UaI$vEsQ#wwko{B%|Tke|Y@G`g$y8o7(VM1B1zAFtu?vK6TJ0 z-*5)M``z)0nF-(Q4E~ff6N3?(iNHYyzhg5eo<7%Y`o`bXA&$(OZHBfsM2k3e6qf-( zlnRfxmCA@pi!Sou6ASRbB%9w5g=Ew|Qt=4(3$$81_~J>GRxMoQ(`Yr^hYf`)Btr}H zrg~W9qTg;)Bll1k`5R4{cqk4$YDA)=m_($A9n>oP>b3QnDg#+sgI4zoFd4`p{1m8x zAG;YEEy?)HC!ToXu{MLn+2_EA6dZm1c>3C&?{`?c9oC)~U+%W`Ic$z2FYiBrAZy=? zM-KP&gTrC#JN)vIBM!LmIfcVV_QS@jAnxz$>*kTbFu9MvHu?cy=*N0Kt@q<0^kovf zle6!4yzt@2v!iWJ*Qm?267a!HWEAQ2lZ;RNeI|-|sp}8%Gz17AG7wtAk|x(QQxs<*^3d-T9A7o@O1vJ{#fyyvSgEkMQ(W@cTZ zsdzd`e`~`5#|uG6w~foFyYtRFx9SjOtJBdeIZ)(74hMZqgg$%Wd|7;6WT@{u6F>p=;x9lLx zEtwB|_j{IOZkGmsMYbBRF&t=iIgu#gh>v*mN^pjaKF%Q+GW_u2+cH*5w}rkX1B()}n$i4O_>WU~Acbcbt;A)9K}p(3MrUcM zuA&~FD$y2~s;Zo}oC-}ca`SnS2%_$O7U5hR>zPk zTa~XZ&9e@fa!N}}f1pvdI@@wo6;&!tX+^2ZFl5tK#LWpLCGHiU z%tR#DibU{{n{&w`W=Pj0&eWMcn}b>kM?ZQY6fzo;ZhT|~RGR}2-P{d}JbiYDy$@wi zVSVJ{954_pGZG&)P2h%s7p{wi$3fxr9T$XyjR(s~5D_mx8Xg50L|NbY z7rym_QjNN_q%?c2N?od{C@C&2(QZ-aWoLgaJ3D)A_U+kg^Vi-^Kjh+e$_E!7hYd;Y z%^NlrZ`cgkH>>W$4Y7-{1rJ$q|Fo0~Od@MY>T0z{t*KTEQ6cFrLp5#9bHj+%f$gvi89u5a^oH^9AK!}UZM3S6< zcQc_2F*&*`IjtFMkB<;F7H}=1S{Qb+?Lj$u3(;Htg)mddsbGYd5$| zG@EcHyO10^Bp_bKagQA|0ZE!bRGiEZokTHUoSxt>7{x&&f-<7=Ym`A?m~oJX?%elv zPcz*!!%vglRPFo1bbsIXefz!7`@G+;XP)|TWfO?<+TQ_eL2WO7{`~=f2jK5T`(up& zEB-U;J<#c4%TS4!=In~ z5$yg|8a%%#1767dHI)4}4Yt1UYhbo$Aak=8vS0pBc>Bd)LdgqR@KV-`@Yc&(NPFcE zuB6-ugDD5= zs!d@2$O8LKEHoT?7xq^lf!2?A!SRn7sQKt1lpWp+)gOHV?BPbHpu_<80Z=cfIWH;>~$un`}7kySYw0YQzh`(v5&x7TL-pJ z>)__`_rZ3o1`gGngQ{bn!%5pm@abo@Q2m(=>OT7%&e#vZd-b2fhbIq1qy1B;uRjd+ z_G56=b{dY_?Xa)08XPA-fqf^=LEEV#aHjqgoI70ur%$qQ=5!rc9d&Tx^ht1@VWGLP z793~l!FH+%?l`K!cBTPp&on`KLkpZba}MmMTA;1rBph)x!}-Q0XgJ#l^^F&y{_Hup zbnXOPI)5C_pF0E2^PfZDq7_b`ZHKlCpTlXV6An7B!uuE6;mUCh*?Qr%|3zW2U!O2VQu%opPZnQb!YTHFq(K@NjQz6vn?4#F0RtLK&b8PzERilmW^BWq>k38K4YM1}FoR z0m=YnfHFWCpbSt3Ck3 z8K4YM1}FoR0m=YnfHFWCpbSt3Ck38K4YM1}FoR0m=YnfHFWCpbSt3Ck38K4YM1}FoR0m=YnfHFWCpbSt3Ch=0DYs^Xf zpU93N@*$)V-?(oqAbOxKc}fZ=hR_lQdm@RT!t*?8ZF^mFz2FT56b@0;)xxEsZKjg`l1$U)&hyG1Ptjo^({nPL$p$fbD!uSh|Rt7se zF~l2&Or}~>x9Ls@PM@i^wx?vMq2Cw6jToylT>7e2=vHk#QBYWE&}Hy0-m6S8LidpV z;PraEo-T5gL^gV6Bv{!YWV6)dCnInv4d)lcFI*h+_=yFJ7tW8_^zg!%g^L$IJh=Fw z1q&aK-|3%LysX=ejglMijg9)S`Szm(lOOYq_4E|o8oP_8q5l4!pqUSF0fE#lAK>|M zua`%iH0ddXyN(Ukd&7q=T)-!ciSFdm%?nZ6cbE}NL5769k{_B(*nV?MOg{|f)^DoM zEGa4J*;rDtId^l3DQIT0T-qJ~fr0HsOm5qjVr{D67W~1;=r@b`z6Bc-AtOR~Qt+8b zz!mNBnLIfQAKu;FO`2}ETfBODiB8m+%G7;k7D%RW)qOj@VDYO9j3ws2B9Gs z^IsLOL53j+h!I7?7{o+IYhsGPNY)g=1}NPoUrEUE*evH*(m7&ebS=iD)L(8{c4tGX zwtRK*Gn%DKqtA-H8_D!Hw)+wm>o1Ej!1=8KA{>7F{&qi6qkOS}75|nnQhtSN0i1MnrC`5g6?_AOx$D;pXbG;Tqqt0Cnj=O`RXLqa#gN1=Za4;} zfih|I95IryCrlzOYKJ-Z86gTrU&B?oNOFgYXphO-k=C~ECRcZ3tLHlBaM$T}??}?Y$jC9==s3T!$Y9Vj z#=Tiuu5_)1$+PA&4f-xa8B@5I?673v#PN}T&NnbejDipxdqPDN#~KBX(Z?P{An#0w zGNH@J6n7~OuV2MHBLNG?i0Grtg~Uiix{d|!QK3#?oNiMs7y%@my(I zJkcDufD!9fY;tn6x~?C6)Nf%8kr)GhQVeW|A9 z=b4C&p`}LPMsDM%%sJ}uif1|~(esto6a&Jq)=#zai2f|GcDTBo5#orjI3or=kD-O# zkiATMLzj{9(}X@veqPQ}@tiU;FKK){e0TWS$ha9>xZdWP;2-aMs9EE#aP|_V zG4O-l_S>VJ$IB0RLJRrX??NJ;z;0ZbH*T!SC}aj0OO@HK;~k|17JWf9*cUGl>)3mD zqplyh`gZeRxB2)LzsD6Sf~U?{XJc8`!`j$p)>5BfbFi$Hwe|&%bZ2lQLye)m-s!Zk zBVDenZnm!LY+v0KwlNAup+wvlj=h10(jgF0>n3Ep@t8h)%er)3^Xlw%YqhC`sX6O5 zq_183?V)&k1YuDOezV6zrlPrs8slVS%r$j2qELbFLsM>tRLS5GuR6Oqg)m8PacA8! zeO8vivMMe6Cq?P$c`Md!2sL_TrqaG}yKq#!FGP%XAtU~Gj~8TDFU@~7Y2x9jkD-+* zPowi*QaU{H5epf7Sr+}73!)icn4DL|BSuc}azh**d8j4;#5l9F)+xQP#msc}Q~N1F z%5`0VGrcgZSZ)v=)sJB)+@g5DpiESy^Zbap`K*(*M_ubo(t=cd1~EFlwxpF${>#rg zQ&%KhpGaO=*H>h#Tq3fLB!VkLEQyt9Bp6lNi~X#{U}dwKm3X2x_5pG1;eYO1|EMZC zv96;n+?5!UY%{OA3{WbewFjb&v_8KxoS>KWpMalVr~PhLz(B4QjA`~g3n#5wtZ+w0}L z#M^}{7lg#K0Rel%U0e#fIq7AQ{HXfQ@gqiaYh9Dw+dASjwwoQC!_nI4@H!awSb4n? zV~}SAiml4)bei2RixU$I2)t!Lms0#xdiLX;+1c7bIRg=6rJ!XkMty;kVGInTV>o9{ zz^Jz{jFBr~EJiIqwt2HQ$6{m(WQOI(L-&YAJrhGfaN4_Bv&Ch>geOf~#2CKN!to=N zIcgk#H*_4XiU|dJ$pMq%U9GYag}fp)J}}l@T7b1hFmjxOZDSqXeJVea6ghcS2_BhD z2#>Mcga_^!Bf5c<9%s0h^ZivBKglRgNm{k_-r6nc8iOV^)HxNoN@Mu$O9~TFv92Bd zla@bcTMX~2qLjsT;xVT}#zj_SO1U7D6EDO4GHJm&@(Ob4r~CGX^#1jRA}djyx~_xX z*-KTA4g8S=WsKX4q{xXcJ~YX1eWbKW516Hdt$Q#XFK4eQs?J$gRjO*X#6?e504%ZS(2F~GOC^mTN5+(XLIQqnnb{m7pm-M?Oy zA=b5vt61H9w))ByGka0aFo^Mafzjq@b8@s_XxF?o!_C^AiO%kT?t0hmcZ_jMDb>+D%jm^%wW{0n$*I}-U z+@Xz2d)FFyT#!==*0nf(U@2vDsL$LZ9*RwS(u9lLUAVmyrl<-#Y;0$(HO}@XYjbm} zv8~KkZ)-BwlkY>R1FVmXxs1bPg`p6U$Nl7p3H|Xgn8YCZrHN~j-Y?W7EQ^>q@f%#?+HCBN@b|6(Vc{RF`ch5Gla>&_ z94oa)@l`3ApgHpGpm0HU)Fvgekj{DdR-EVYq*N3SkE8b_luBoNCvlikl{naELP^RR z;V}pS5r#xIqJ^v~f}(({@D_8DU1oQeVa(*7yHf5kuGJy`&fgPVDy?`1CQXD-l2Jn% z9FmBGNPry&8Ei2jX&0MMC_JskO?Czl?AJvZGRB&`(71K4oFpt^OeU&?F;BFLZDxaL zB^Qplr%zh5RC7X;lCg4ediKh^l++BZc3DPV#kvil_C;K!@p1P#qAO&|y7n@H#qMPN zTu;B-!Ma$t!|fG%`n^1BHscWv&-M81E~C!Qx&i@)^>nZvJdpHazMSANGiLk5_@&5j zDpgwLsd1x!K=@h0!e!t7#V-^7DJ9{t1(Fv@M@&Cnu)93=0B_paiE#{KvkWsE*$p2Hqr95@7ajGtBgv0pKU#U0R@D;{!Na8 zK9jSW95;jnF}jQN`h5LX?POhl2KI>dAIe}#P4%3j1*#uHj5k)~FJ70RNtyE0Y3fMK zE637n&C(jPZH5f(E_EJZq4ABPp;P!(_}QKxcMG*)-WWuTq@CXjZ4M)FeNX zzI0Ra6y4k({LV+u>G1J!bv%CEd+E|83)LJTkN7=&BGQ#8;S!3H+`{9<<1`#c#Fb}| znyGlaDn_>OTxS@MHDn+}eB%)qhi`A)lC9rzLT9X4r_0ZY7<0!A#wxv0%am0d)7o-I zbXxJ`TLnU_+{qi@Br#*^_Bbi^*xLP8@2J<`D-5{ZeSW*ATa`Nru%0Hi+fvtfow3-O zxURrpga=~OTgHdT4+#8y;VXGHDY$dGCR$p?)~n9|CT+v$K1nHOW5!gHvO3EOG79y@ z=3R`4VbaU^HZkfs;of?c&&1|VW%aWx>590fQJKR7{a4A~)uH~YJ^kTvUtu`F*(?># z@cnpz?Z75_ugo>|)|vd~qyv9+P=vpanG6#*t1s0@oBDAzs_H_)oUjXvL)v5+&%(~n zU{+VTcsf);F9H#NG!|whL4*;lP_wk*VN{3H+3In)TAjVEI44EPiJ0&^d@iSh8~}P9 zEV+inFL2(-K~1z^zwTwN=895dsnt?3SxG`kMb~gmF@IUo;=DEK@kuXayrg+Na*!2s z`^`ho#4k=-#re+~%NVtf^oyFPHHTw(OY*nMw43yg8Fd9@ET4-YkGNbXdtB){SsG;T zEAE7i?J4=8EirNDr0u2f#QgbRh9O0avl(P2@x??ZiJ7@Mi1DGPUi(rAXyfA-E?i(t z711I_=01N7fscEI`~FRZIf(Irmi7mDzUisM1JB8HBF1luVAnu-;sL|=+XGLu|KkfM zWMBlN!D*L+U(a39I~1zA7f7=@u7IO&?4MV?^S_@yjn4MA*MlgL;&iWMG;)#|Uz#cK=(BjX`+; zKe2J&B5F)*@I!RTBH3bMGkA#0S$hJ(!k@JS0AdB~DDsL^W=?bX$@}0vJlf z>vva)p~S=$#0@P(L2cD{@s^gk2CtGmE^ZC+mnBgdkd3pu$1aW`4Z=L6OTOdw>+lNvQq{q1C#;E0A+wOKpCJ6PzERil!33Cf&T}XbZf5w diff --git a/build/exe/doliwamp/doliwamp.iss b/build/exe/doliwamp/doliwamp.iss index 086968a66aa..f771a2f19e0 100644 --- a/build/exe/doliwamp/doliwamp.iss +++ b/build/exe/doliwamp/doliwamp.iss @@ -80,7 +80,7 @@ Name: "desktopicon"; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm: Name: "{app}\logs" Name: "{app}\tmp" Name: "{app}\dolibarr_documents" -Name: "{app}\bin\apache\apache\logs" +Name: "{app}\bin\apache\apache2.4.9\logs" [Files] ; Stop/start diff --git a/build/exe/doliwamp/my.ini.install b/build/exe/doliwamp/my.ini.install index 40a764b6038..8528ce1a62e 100644 --- a/build/exe/doliwamp/my.ini.install +++ b/build/exe/doliwamp/my.ini.install @@ -69,7 +69,7 @@ port=WAMPMYSQLPORT #Path to installation directory. All paths are usually resolved relative to this. -basedir=WAMPROOT/bin/mysql/mysql5.0.45 +basedir=WAMPROOT/bin/mysql/mysql5.6.17 #log file log-error=WAMPROOT/logs/mysql.log @@ -226,7 +226,7 @@ port=WAMPMYSQLPORT #Path to installation directory. All paths are usually resolved relative to this. -basedir=WAMPROOT/bin/mysql/mysql5.0.45 +basedir=WAMPROOT/bin/mysql/mysql5.6.17 #log file log-error=WAMPROOT/logs/mysql.log From f7bd2d16dbe816f79c8d65a0513926c023ad81d4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 4 May 2016 20:48:49 +0200 Subject: [PATCH 598/834] Prepare upgrade of doliwamp --- build/exe/doliwamp/doliwamp.iss | 1 + build/exe/doliwamp/my.ini.install | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/exe/doliwamp/doliwamp.iss b/build/exe/doliwamp/doliwamp.iss index f771a2f19e0..8c6130437ac 100644 --- a/build/exe/doliwamp/doliwamp.iss +++ b/build/exe/doliwamp/doliwamp.iss @@ -663,6 +663,7 @@ begin //installDir et version de php StringChangeEx (srcContents, 'WAMPROOT', pathWithSlashes, True); StringChangeEx (srcContents, 'WAMPMYSQLPORT', myport, True); + StringChangeEx (srcContents, 'WAMPMYSQLVERSION', myport, True); SaveStringToFile(destFile,srcContents, False); end diff --git a/build/exe/doliwamp/my.ini.install b/build/exe/doliwamp/my.ini.install index 8528ce1a62e..c855cc27a66 100644 --- a/build/exe/doliwamp/my.ini.install +++ b/build/exe/doliwamp/my.ini.install @@ -69,7 +69,7 @@ port=WAMPMYSQLPORT #Path to installation directory. All paths are usually resolved relative to this. -basedir=WAMPROOT/bin/mysql/mysql5.6.17 +basedir=WAMPROOT/bin/mysql/mysqlWAMPMYSQLVERSION #log file log-error=WAMPROOT/logs/mysql.log From 5040fe46b0ce31adaf0dfb6507d480122f0c395d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 11:15:53 +0200 Subject: [PATCH 599/834] FIX Missing number total of modules --- htdocs/admin/modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index 3fbaddd0a3f..aa4c1b85cae 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -223,7 +223,7 @@ asort($orders); //var_dump($modules); $nbofactivatedmodules=count($conf->modules); -$moreinfo=$langs->trans("TotalNumberOfActivatedModules",($nbofactivatedmodules-1)); +$moreinfo=$langs->trans("TotalNumberOfActivatedModules",($nbofactivatedmodules-1), count($modules)); if ($nbofactivatedmodules <= 1) $moreinfo .= ' '.img_warning($langs->trans("YouMustEnableOneModule")); print load_fiche_titre($langs->trans("ModulesSetup"),$moreinfo,'title_setup'); From 461f62c200f9bd86a860966ffdece3da02c58e8b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 13:29:42 +0200 Subject: [PATCH 600/834] Use the new global search form --- htdocs/core/class/hookmanager.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/core/class/hookmanager.class.php b/htdocs/core/class/hookmanager.class.php index f92056893b6..484b6518f77 100644 --- a/htdocs/core/class/hookmanager.class.php +++ b/htdocs/core/class/hookmanager.class.php @@ -135,6 +135,7 @@ class HookManager $method, array( 'addMoreActionsButtons', + 'addSearchEntry', 'addStatisticLine', 'deleteFile', 'doActions', From c795803da1e49cdd3d00868094fe35a2f9dee0cb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 20:19:56 +0200 Subject: [PATCH 601/834] FIX ref_supplier was not set. We also refered to old var names. --- .../fourn/class/fournisseur.product.class.php | 4 ++- htdocs/fourn/commande/card.php | 31 +++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index 03be7e77d36..48896689dce 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -613,7 +613,9 @@ class ProductFournisseur extends Product if ($fourn_unitprice < $min || $min == -1) { $this->product_fourn_price_id = $record["product_fourn_price_id"]; - $this->fourn_ref = $record["ref_fourn"]; + $this->ref_supplier = $record["ref_fourn"]; + $this->ref_fourn = $record["ref_fourn"]; // deprecated + $this->fourn_ref = $record["ref_fourn"]; // deprecated $this->fourn_price = $fourn_price; $this->fourn_qty = $record["quantity"]; $this->fourn_remise_percent = $record["remise_percent"]; diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index b35ffa799da..4454c0ac28c 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -984,17 +984,16 @@ if (empty($reshook)) // If creation from another object of another module (Example: origin=propal, originid=1) if (! empty($origin) && ! empty($originid)) { - if ($origin == 'order' || $origin == 'commande') { + if ($origin == 'order' || $origin == 'commande') + { $element = $subelement = 'commande'; } - elseif ($origin =='supplier_proposal'){ + else + { $element = 'supplier_proposal'; $subelement = 'supplier_proposal'; - } - else { - $element = 'comm/askpricesupplier'; - $subelement = 'askpricesupplier'; - } + } + $object->origin = $origin; $object->origin_id = $originid; @@ -1056,11 +1055,17 @@ if (empty($reshook)) } $result = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty); - if ($result>0) { - $productsupplier->fetch($productsupplier->id); - $soc=new societe($db); - $soc->fetch($socid); - $tva_tx=($origin=="commande")?get_default_tva($soc,$mysoc,$lines[$i]->fk_product,$idprod):$lines[$i]->tva_tx; + if ($result>0) + { + $tva_tx = $lines[$i]->tva_tx; + + if ($origin=="commande") + { + $soc=new societe($db); + $soc->fetch($socid); + $tva_tx=get_default_tva($soc, $mysoc, $lines[$i]->fk_product, $productsupplier->product_fourn_price_id); + } + $result = $object->addline( $desc, $lines[$i]->subprice, @@ -1070,7 +1075,7 @@ if (empty($reshook)) $lines[$i]->localtax2_tx, $lines[$i]->fk_product, $productsupplier->product_fourn_price_id, - $productsupplier->ref_fourn, + $productsupplier->ref_supplier, $lines[$i]->remise_percent, 'HT', 0, From f901453a6b1070b441097b2506478a9438c6eb81 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 21:38:29 +0200 Subject: [PATCH 602/834] FIX addMoreActionsButtons hook must be inside the div class=tabaction. --- htdocs/comm/card.php | 148 +++++++++++++++++---------------- htdocs/fourn/card.php | 16 ++-- htdocs/fourn/commande/card.php | 20 +++-- htdocs/projet/tasks/task.php | 61 +++++++------- 4 files changed, 125 insertions(+), 120 deletions(-) diff --git a/htdocs/comm/card.php b/htdocs/comm/card.php index 17098310769..38447c17c5e 100644 --- a/htdocs/comm/card.php +++ b/htdocs/comm/card.php @@ -961,82 +961,84 @@ if ($id > 0) * Barre d'actions */ + print '
    '; + $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been - - print '
    '; - - if (! empty($conf->propal->enabled) && $user->rights->propal->creer && $object->status==1) - { - $langs->load("propal"); - print ''; - } - - if (! empty($conf->commande->enabled) && $user->rights->commande->creer && $object->status==1) - { - $langs->load("orders"); - print ''; - } - - if ($user->rights->contrat->creer && $object->status==1) - { - $langs->load("contracts"); - print ''; - } - - if (! empty($conf->ficheinter->enabled) && $user->rights->ficheinter->creer && $object->status==1) - { - $langs->load("fichinter"); - print ''; - } - - // Add invoice - if ($user->societe_id == 0) - { - if (! empty($conf->deplacement->enabled) && $object->status==1) - { - $langs->load("trips"); - print ''; - } - - if (! empty($conf->facture->enabled)) - { - if ($user->rights->facture->creer && $object->status==1) - { - $langs->load("bills"); - $langs->load("orders"); - - if (! empty($conf->commande->enabled)) - { - if (! empty($orders2invoice) && $orders2invoice > 0) print ''; - else print ''; - } - - if ($object->client != 0) print ''; - else print ''; - - } - else - { - print ''; - } - } - } - - // Add action - if (! empty($conf->agenda->enabled) && ! empty($conf->global->MAIN_REPEATTASKONEACHTAB)) - { - if ($user->rights->agenda->myactions->create) - { - print ''; - } - else - { - print ''; - } - } - + if (empty($reshook)) + { + if (! empty($conf->propal->enabled) && $user->rights->propal->creer && $object->status==1) + { + $langs->load("propal"); + print ''; + } + + if (! empty($conf->commande->enabled) && $user->rights->commande->creer && $object->status==1) + { + $langs->load("orders"); + print ''; + } + + if ($user->rights->contrat->creer && $object->status==1) + { + $langs->load("contracts"); + print ''; + } + + if (! empty($conf->ficheinter->enabled) && $user->rights->ficheinter->creer && $object->status==1) + { + $langs->load("fichinter"); + print ''; + } + + // Add invoice + if ($user->societe_id == 0) + { + if (! empty($conf->deplacement->enabled) && $object->status==1) + { + $langs->load("trips"); + print ''; + } + + if (! empty($conf->facture->enabled)) + { + if ($user->rights->facture->creer && $object->status==1) + { + $langs->load("bills"); + $langs->load("orders"); + + if (! empty($conf->commande->enabled)) + { + if (! empty($orders2invoice) && $orders2invoice > 0) print ''; + else print ''; + } + + if ($object->client != 0) print ''; + else print ''; + + } + else + { + print ''; + } + } + } + + // Add action + if (! empty($conf->agenda->enabled) && ! empty($conf->global->MAIN_REPEATTASKONEACHTAB)) + { + if ($user->rights->agenda->myactions->create) + { + print ''; + } + else + { + print ''; + } + } + } + print '
    '; if (! empty($conf->global->MAIN_REPEATCONTACTONEACHTAB)) diff --git a/htdocs/fourn/card.php b/htdocs/fourn/card.php index 255d7f2cda0..b64a8ee77e4 100644 --- a/htdocs/fourn/card.php +++ b/htdocs/fourn/card.php @@ -518,15 +518,14 @@ if ($object->id > 0) /* * Barre d'actions */ + print '
    '; + $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been // modified by hook if (empty($reshook)) { - - print '
    '; - - if ($user->rights->fournisseur->commande->creer) + if ($user->rights->fournisseur->commande->creer) { $langs->load("orders"); print ''.$langs->trans("AddOrder").''; @@ -562,9 +561,11 @@ if ($object->id > 0) print ''.$langs->trans("AddAction").''; } } - - print '
    '; - print '
    '; + } + + print '
    '; + + print '
    '; if (! empty($conf->global->MAIN_REPEATCONTACTONEACHTAB)) { @@ -589,7 +590,6 @@ if ($object->id > 0) // List of done actions show_actions_done($conf,$langs,$db,$object); } - } } else { diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index 4454c0ac28c..8d18914d62c 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -2784,15 +2784,17 @@ elseif (! empty($object->id)) /** * Boutons actions */ - $parameters = array(); - $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been - // modified by hook - if (empty($reshook)) + + if ($user->societe_id == 0 && $action != 'editline' && $action != 'delete') { - if ($user->societe_id == 0 && $action != 'editline' && $action != 'delete') - { - print '
    '; + print '
    '; + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been + // modified by hook + if (empty($reshook)) + { + // Validate if ($object->statut == 0 && $num > 0) { @@ -2974,9 +2976,11 @@ elseif (! empty($object->id)) print ''.$langs->trans("Delete").''; } - print "
    "; } + + print "
    "; } + print "
    "; diff --git a/htdocs/projet/tasks/task.php b/htdocs/projet/tasks/task.php index a65ca9a8f0d..8c1d1fc40cb 100644 --- a/htdocs/projet/tasks/task.php +++ b/htdocs/projet/tasks/task.php @@ -487,16 +487,17 @@ if ($id > 0 || ! empty($ref)) if ($action != 'edit') { - $parameters = array(); - $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been - // modified by hook - if (empty($reshook)) - { - /* - * Actions - */ - print '
    '; - + /* + * Actions + */ + + print '
    '; + + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been + // modified by hook + if (empty($reshook)) + { // Modify if ($user->rights->projet->creer) { @@ -518,27 +519,25 @@ if ($id > 0 || ! empty($ref)) } print '
    '; - - print '
    '; - print ''; // ancre - - /* - * Documents generes - */ - $filename=dol_sanitizeFileName($projectstatic->ref). "/". dol_sanitizeFileName($object->ref); - $filedir=$conf->projet->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref). "/" .dol_sanitizeFileName($object->ref); - $urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id; - $genallowed=($user->rights->projet->lire); - $delallowed=($user->rights->projet->creer); - - $var=true; - - $somethingshown=$formfile->show_documents('project_task',$filename,$filedir,$urlsource,$genallowed,$delallowed,$object->modelpdf); - - - - print '
    '; - } + } + + print '
    '; + print ''; // ancre + + /* + * Documents generes + */ + $filename=dol_sanitizeFileName($projectstatic->ref). "/". dol_sanitizeFileName($object->ref); + $filedir=$conf->projet->dir_output . "/" . dol_sanitizeFileName($projectstatic->ref). "/" .dol_sanitizeFileName($object->ref); + $urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id; + $genallowed=($user->rights->projet->lire); + $delallowed=($user->rights->projet->creer); + + $var=true; + + $somethingshown=$formfile->show_documents('project_task',$filename,$filedir,$urlsource,$genallowed,$delallowed,$object->modelpdf); + + print '
    '; } } } From ca37a90a1b27ceac32d8c348d580857665532bb9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 22:26:42 +0200 Subject: [PATCH 603/834] Better explanation --- htdocs/langs/en_US/admin.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index a04600ae6ed..47efe0eee0a 100755 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -68,7 +68,7 @@ SearchFilter=Search filters options NumberOfKeyToSearch=Nbr of characters to trigger search: %s ViewFullDateActions=Show full dates events in the third sheet NotAvailableWhenAjaxDisabled=Not available when Ajax disabled -AllowToSelectProjectFromOtherCompany=In document, allow to link it to project from another company +AllowToSelectProjectFromOtherCompany=On document of a thirdparty, can choose a project linked to another thirdparty JavascriptDisabled=JavaScript disabled UsePopupCalendar=Use popup for dates input UsePreviewTabs=Use preview tabs From 8065e0079505d5ae7bb856b4c04c716f1ea29b00 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 22:30:08 +0200 Subject: [PATCH 604/834] FIX #5142 --- htdocs/main.inc.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index bea4a20afcd..b17ce86ad17 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1357,6 +1357,9 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a global $dolibarr_main_authentication, $dolibarr_main_demo; global $hookmanager,$menumanager; + $searchform=''; + $bookmarks=''; + // Instantiate hooks of thirdparty module $hookmanager->initHooks(array('toprightmenu')); From 0c6aaaaa3cbd159b82fc14aece10844ff9f0c8f4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 23:05:17 +0200 Subject: [PATCH 605/834] FIX #5168 --- htdocs/core/lib/functions2.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 887b5f300cf..15c1fb498ee 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -1966,7 +1966,7 @@ function getElementProperties($element_type) $module='ficheinter'; $subelement='fichinter'; } - if ($element_type == 'dolresource') { + if ($element_type == 'dolresource' || $element_type == 'resource') { $classpath = 'resource/class'; $module='resource'; $subelement='dolresource'; From 06d0c261ad6e1b2a55b04d5f2a1e551fd6c6b98c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 23:19:21 +0200 Subject: [PATCH 606/834] Add TODO comment --- htdocs/fourn/class/fournisseur.commande.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index 6f20c509fd8..cb7d0ce6e6f 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -1415,6 +1415,7 @@ class CommandeFournisseur extends CommonOrder $subprice = price2num($pu,'MU'); + // TODO We should use here $this->line=new CommandeFournisseurLigne($this->db); and $this->line->insert(); to work loke other object (proposal, order, invoice) $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet"; $sql.= " (fk_commande, label, description, date_start, date_end,"; $sql.= " fk_product, product_type,"; From 2960fb6df77fd88fbda0a0396bface56bfd6669c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 23:28:22 +0200 Subject: [PATCH 607/834] FIX Option not tested everywhere --- htdocs/core/tpl/admin_extrafields_add.tpl.php | 2 ++ htdocs/core/tpl/admin_extrafields_edit.tpl.php | 6 ++++-- htdocs/core/tpl/admin_extrafields_view.tpl.php | 2 +- htdocs/langs/en_US/members.lang | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/htdocs/core/tpl/admin_extrafields_add.tpl.php b/htdocs/core/tpl/admin_extrafields_add.tpl.php index db3e17bc123..25b86cdc0bf 100644 --- a/htdocs/core/tpl/admin_extrafields_add.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_add.tpl.php @@ -118,7 +118,9 @@ trans("AlwaysEditable"); ?>> +global->MAIN_CAN_HIDE_EXTRAFIELDS)) { ?> trans("Hidden"); ?>> + global->MAIN_FEATURES_LEVEL >= 2) { ?> trans("ByDefaultInList"); ?> diff --git a/htdocs/core/tpl/admin_extrafields_edit.tpl.php b/htdocs/core/tpl/admin_extrafields_edit.tpl.php index e17a9073067..64c78c43c2e 100644 --- a/htdocs/core/tpl/admin_extrafields_edit.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_edit.tpl.php @@ -85,7 +85,7 @@ $alwayseditable=$extrafields->attribute_alwayseditable[$attrname]; $param=$extrafields->attribute_param[$attrname]; $perms=$extrafields->attribute_perms[$attrname]; $list=$extrafields->attribute_list[$attrname]; -$ishidden=$extrafields->attribute_hidden[$attrname]; +//$ishidden=$extrafields->attribute_hidden[$attrname]; if((($type == 'select') || ($type == 'checkbox') || ($type == 'radio')) && is_array($param)) { @@ -167,7 +167,9 @@ else trans("AlwaysEditable"); ?>> -trans("Hidden"); ?>> +global->MAIN_CAN_HIDE_EXTRAFIELDS)) { ?> + trans("Hidden"); ?>> + global->MAIN_FEATURES_LEVEL >= 2) { ?> trans("ByDefaultInList"); ?> diff --git a/htdocs/core/tpl/admin_extrafields_view.tpl.php b/htdocs/core/tpl/admin_extrafields_view.tpl.php index e131b345e1d..6acc81f009f 100644 --- a/htdocs/core/tpl/admin_extrafields_view.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_view.tpl.php @@ -61,7 +61,7 @@ foreach($extrafields->attribute_type as $key => $value) print ''.yn($extrafields->attribute_unique[$key])."\n"; print ''.yn($extrafields->attribute_required[$key])."\n"; print ''.yn($extrafields->attribute_alwayseditable[$key])."\n"; - print ''.yn($extrafields->attribute_hidden[$key])."\n"; // Add hidden option on not working feature. Why hide if user can't see it. + if (! empty($conf->global->MAIN_CAN_HIDE_EXTRAFIELDS)) print ''.yn($extrafields->attribute_hidden[$key])."\n"; // Add hidden option on not working feature. Why hide if user can't see it. print ''.img_edit().''; print "  ".img_delete()."\n"; print ""; diff --git a/htdocs/langs/en_US/members.lang b/htdocs/langs/en_US/members.lang index 1844a22c227..06dfa25166c 100644 --- a/htdocs/langs/en_US/members.lang +++ b/htdocs/langs/en_US/members.lang @@ -202,5 +202,5 @@ MembersByNature=This screen show you statistics on members by nature. MembersByRegion=This screen show you statistics on members by region. VATToUseForSubscriptions=VAT rate to use for subscriptions NoVatOnSubscription=No TVA for subscriptions -MEMBER_PAYONLINE_SENDEMAIL=Email to warn when Dolibarr receive a confirmation of a validated payment for subscription +MEMBER_PAYONLINE_SENDEMAIL=Email to use for email warning when Dolibarr receive a confirmation of a validated payment for a subscription (Example: paymentdone@example.com) ADHERENT_PRODUCT_ID_FOR_SUBSCRIPTIONS=Product used for subscription line into invoice: %s \ No newline at end of file From f78bc16076e0e03d9ba10ca27d6a0b2eee94d8cf Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 23:39:58 +0200 Subject: [PATCH 608/834] FIX Option 'onlypicto' was showing also the ref --- htdocs/adherents/class/adherent.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/adherents/class/adherent.class.php b/htdocs/adherents/class/adherent.class.php index 77437667c33..93b013cd29f 100644 --- a/htdocs/adherents/class/adherent.class.php +++ b/htdocs/adherents/class/adherent.class.php @@ -1577,7 +1577,7 @@ class Adherent extends CommonObject if ($withpicto) $result.=($link.img_object('', $picto, 'class="classfortooltip"').$linkend); if ($withpicto && $withpicto != 2) $result.=' '; - $result.=$link.($maxlen?dol_trunc($this->ref,$maxlen):$this->ref).$linkend; + $result.=$link.(($withpicto != 2) ? ($maxlen?dol_trunc($this->ref,$maxlen):$this->ref) : '').$linkend; return $result; } From cdbbe8d87b2daf20d9729d9cb42a538284b0822b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 5 May 2016 23:44:44 +0200 Subject: [PATCH 609/834] Minor fixes --- htdocs/comm/mailing/cibles.php | 2 +- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 15 +++++++++++++++ .../mysql/tables/llx_advtargetemailing.sql | 2 +- htdocs/langs/en_US/mails.lang | 6 +++--- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index 0c133fac6df..1282aab5558 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -540,7 +540,7 @@ if ($object->fetch($id) >= 0) print ''; } - //Sreach Icon + // Search Icon print ''; if ($obj->statut == 0) { diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index bf81785d3e3..895f2869b48 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -423,3 +423,18 @@ ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); ALTER TABLE llx_element_resources ADD COLUMN duree real; -- total duration of using ressource + +CREATE TABLE llx_advtargetemailing +( + rowid integer NOT NULL auto_increment PRIMARY KEY, + name varchar(200) NOT NULL, + entity integer NOT NULL DEFAULT 1, + fk_mailing integer NOT NULL, + filtervalue text, + fk_user_author integer NOT NULL, + datec datetime NOT NULL, + fk_user_mod integer NOT NULL, + tms timestamp NOT NULL +)ENGINE=InnoDB; + +ALTER TABLE llx_advtargetemailing ADD UNIQUE INDEX uk_advtargetemailing_name (name); diff --git a/htdocs/install/mysql/tables/llx_advtargetemailing.sql b/htdocs/install/mysql/tables/llx_advtargetemailing.sql index 2e4ad958755..d22503bb548 100644 --- a/htdocs/install/mysql/tables/llx_advtargetemailing.sql +++ b/htdocs/install/mysql/tables/llx_advtargetemailing.sql @@ -17,7 +17,7 @@ -- Table of "Plan de comptes" for accountancy expert module -- ============================================================================ -CREATE TABLE IF NOT EXISTS llx_advtargetemailing +CREATE TABLE llx_advtargetemailing ( rowid integer NOT NULL auto_increment PRIMARY KEY, name varchar(200) NOT NULL, diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 16c1d089854..ff87db130b5 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -147,8 +147,8 @@ MailSendSetupIs2=You must first go, with an admin account, into menu %sHome - Se MailSendSetupIs3=If you have any questions on how to setup your SMTP server, you can ask to %s. YouCanAlsoUseSupervisorKeyword=You can also add the keyword __SUPERVISOREMAIL__ to have email being sent to the supervisor of user (works only if an email is defined for this supervisor) NbOfTargetedContacts=Current number of targeted contact emails -MailAdvTargetRecipients=Advance recipients targeting -AdvTgtTitle=Fill the input ro preselect the customer (or contact) destinaries +MailAdvTargetRecipients=Recipients (advanced selection) +AdvTgtTitle=Fill input fields to preselect the thirdparties or contacts/addresses to target AdvTgtSearchTextHelp=Use %% as magic caracters. For exemple to find all item like jean, joe, jim, you can input j%%, you can also use ; as separator for value, and use ! for except this value. For exemple jean;joe;jim%%;!jimo;!jima% will target all jean, joe, start with jim but not jimo and not everythnig taht start by jima AdvTgtSearchIntHelp=Use interval to select int or float value AdvTgtMinVal=Minimum value @@ -163,7 +163,7 @@ AddAll=Add all RemoveAll=Remove all ItemsCount=Item(s) AdvTgtNameTemplate=Filter name -AdvTgtAddContact=Add email according filter criteria +AdvTgtAddContact=Add emails according to criterias AdvTgtLoadFilter=Load filter AdvTgtDeleteFilter=Delete filter AdvTgtSaveFilter=Save filter From 5a75f346ff578c47060732b6aa3b8062af2163c3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 6 May 2016 00:02:22 +0200 Subject: [PATCH 610/834] Debug advanced selector --- htdocs/comm/mailing/advtargetemailing.php | 208 +++--------------- .../html.formadvtargetemailing.class.php | 6 +- htdocs/core/lib/emailing.lib.php | 28 +-- .../plugins}/multiselect/MIT-LICENSE.txt | 0 .../multiselect/css/ui.multiselect.css | 0 .../plugins}/multiselect/js/ui.multiselect.js | 0 6 files changed, 52 insertions(+), 190 deletions(-) rename htdocs/includes/{ => jquery/plugins}/multiselect/MIT-LICENSE.txt (100%) rename htdocs/includes/{ => jquery/plugins}/multiselect/css/ui.multiselect.css (100%) rename htdocs/includes/{ => jquery/plugins}/multiselect/js/ui.multiselect.js (100%) diff --git a/htdocs/comm/mailing/advtargetemailing.php b/htdocs/comm/mailing/advtargetemailing.php index 4076217cf88..be8a63225fe 100644 --- a/htdocs/comm/mailing/advtargetemailing.php +++ b/htdocs/comm/mailing/advtargetemailing.php @@ -1,20 +1,27 @@ -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ +/* Copyright (C) 2014 Florian Henry + * Copyright (C) 2016 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/comm/mailing/advtargetemailing.php + * \ingroup mailing + * \brief Page to define emailing targets + */ + require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT . '/comm/mailing/class/mailing.class.php'; @@ -84,8 +91,9 @@ if ($result < 0) { } } + /* - * Action + * Actions */ if ($action == 'loadfilter') { @@ -387,15 +395,16 @@ if ($_POST["button_removefilter"]) { $search_email = ''; } + /* * View */ + $extrajs = array ( - '/includes/multiselect/js/ui.multiselect.js' + '/includes/jquery/plugins/multiselect/js/ui.multiselect.js' ); $extracss = array ( - '/includes/multiselect/css/ui.multiselect.css', - '/advtargetemailing/css/advtargetemailing.css' + '/includes/jquery/plugins/multiselect/css/ui.multiselect.css', ); llxHeader('', $langs->trans("MailAdvTargetRecipients"), '', '', '', '', $extrajs, $extracss); @@ -958,162 +967,11 @@ if ($object->fetch($id) >= 0) { print ''; print '
    '; } - if (empty($conf->mailchimp->enabled) || (! empty($conf->mailchimp->enabled) && $object->statut != 3)) { - // List of selected targets - print "\n\n"; - print '
    '; - print ''; - print ''; - print ''; - print ''; + - $sql = "SELECT mc.rowid, mc.lastname, mc.firstname, mc.email, mc.other, mc.statut, mc.date_envoi, mc.source_url, mc.source_id, mc.source_type"; - $sql .= " FROM " . MAIN_DB_PREFIX . "mailing_cibles as mc"; - $sql .= " WHERE mc.fk_mailing=" . $object->id; - if ($search_nom) - $sql .= " AND mc.lastname LIKE '%" . $db->escape($search_nom) . "%'"; - if ($search_prenom) - $sql .= " AND mc.firstname LIKE '%" . $db->escape($search_prenom) . "%'"; - if ($search_email) - $sql .= " AND mc.email LIKE '%" . $db->escape($search_email) . "%'"; - $sql .= $db->order($sortfield, $sortorder); - $sql .= $db->plimit($conf->liste_limit + 1, $offset); - - dol_syslog('advtargetemailing.php:: sql=' . $sql); - $resql = $db->query($sql); - if ($resql) { - $num = $db->num_rows($resql); - - $parm = "&id=" . $object->id; - if ($search_nom) - $parm .= "&search_nom=" . urlencode($search_nom); - if ($search_prenom) - $parm .= "&search_prenom=" . urlencode($search_prenom); - if ($search_email) - $parm .= "&search_email=" . urlencode($search_email); - - print_barre_liste($langs->trans("MailSelectedRecipients"), $page, $_SERVER["PHP_SELF"], $parm, $sortfield, $sortorder, "", $num, $object->nbemail, ''); - - if ($page) - $parm .= "&page=" . $page; - print ''; - print ''; - print_liste_field_titre($langs->trans("EMail"), $_SERVER["PHP_SELF"], "mc.email", $parm, "", "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Lastname"), $_SERVER["PHP_SELF"], "mc.lastname", $parm, "", "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Firstname"), $_SERVER["PHP_SELF"], "mc.firstname", $parm, "", "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("OtherInformations"), $_SERVER["PHP_SELF"], "", $parm, "", "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Source"), $_SERVER["PHP_SELF"], "", $parm, "", 'align="center"', $sortfield, $sortorder); - - // Date sendinf - if ($object->statut < 2) { - print ''; - } else { - print_liste_field_titre($langs->trans("DateSending"), $_SERVER["PHP_SELF"], "mc.date_envoi", $parm, '', 'align="center"', $sortfield, $sortorder); - } - - // Statut - print_liste_field_titre($langs->trans("Status"), $_SERVER["PHP_SELF"], "mc.statut", $parm, '', 'align="right"', $sortfield, $sortorder); - - print ''; - - // Ligne des champs de filtres - print ''; - // EMail - print ''; - // Name - print ''; - // Firstname - print ''; - // Other - print ''; - // SendDate - print ''; - // Source - print ''; - print ''; - - $var = true; - $i = 0; - - if ($num) { - while ( $i < min($num, $conf->liste_limit) ) { - $obj = $db->fetch_object($resql); - $var = ! $var; - - print ""; - print ''; - print ''; - print ''; - print ''; - print ''; - - // Statut pour l'email destinataire (Attentioon != statut du mailing) - if ($obj->statut == 0) { - print ''; - print ''; - } else { - print ''; - print ''; - } - print ''; - - $i ++; - } - } else { - print ''; - } - print "
     
    '; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ' '; - print ''; - print ' '; - print ''; - print ''; - print '  '; - print ''; - print '
    ' . $obj->email . '' . $obj->lastname . '' . $obj->firstname . '' . $obj->other . ''; - if (empty($obj->source_id) || empty($obj->source_type)) { - print $obj->source_url; // For backward compatibility - } else { - - if ($obj->source_type == 'thirdparty') { - include_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; - $m = new Societe($db); - $m->fetch($obj->source_id); - print $m->getNomUrl(1); - } elseif ($obj->source_type == 'contact') { - include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; - $m = new Contact($db); - $m->fetch($obj->source_id); - print $m->getNomUrl(1); - } - } - print ' ' . $langs->trans("MailingStatusNotSent"); - if ($user->rights->mailing->creer) { - print '' . img_delete($langs->trans("RemoveRecipient")); - } - print '' . $obj->date_envoi . ''; - if ($obj->statut == - 1) - print $langs->trans("MailingStatusError") . ' ' . img_error(); - if ($obj->statut == 1) - print $langs->trans("MailingStatusSent") . ' ' . img_picto($langs->trans("MailingStatusSent"), 'statut4'); - if ($obj->statut == 2) - print $langs->trans("MailingStatusRead") . ' ' . img_picto($langs->trans("MailingStatusRead"), 'statut6'); - if ($obj->statut == 3) - print $langs->trans("MailingStatusNotContact") . ' ' . img_picto($langs->trans("MailingStatusNotContact"), 'statut8'); - print '
    ' . $langs->trans("NoTargetYet") . '

    "; - - $db->free($resql); - } else { - setEventMessage($db->lasterror(), 'errors'); - } - - print '
    '; + if (empty($conf->mailchimp->enabled) || (! empty($conf->mailchimp->enabled) && $object->statut != 3)) + { + // List of recipients (TODO Move code of page cibles.php into a .tpl.php file and make an include here to avoid duplicate content) } } diff --git a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php index 9c340e1e33b..ac6b5727c58 100644 --- a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php +++ b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php @@ -333,7 +333,8 @@ class FormAdvTargetEmailing extends Form function advMultiselectarray($htmlname, $options_array = array(), $selected_array = array(), $showempty = 0) { global $conf, $langs; - $return = ''; - $return .= ''; + if ($showempty) $return .= ''; diff --git a/htdocs/core/lib/emailing.lib.php b/htdocs/core/lib/emailing.lib.php index 257f2829704..443b9b6156a 100644 --- a/htdocs/core/lib/emailing.lib.php +++ b/htdocs/core/lib/emailing.lib.php @@ -39,20 +39,22 @@ function emailing_prepare_head(Mailing $object) $head[$h][2] = 'card'; $h++; - if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! $user->rights->mailing->mailing_advance->recipient) { - return $head; + if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->rights->mailing->mailing_advance->recipient)) + { + $head[$h][0] = DOL_URL_ROOT."/comm/mailing/cibles.php?id=".$object->id; + $head[$h][1] = $langs->trans("MailRecipients"); + $head[$h][2] = 'targets'; + $h++; + + if (! empty($conf->global->EMAILING_USE_ADVANCED_SELECTOR)) // Still need debug + { + $head[$h][0] = DOL_URL_ROOT."/comm/mailing/advtargetemailing.php?id=".$object->id; + $head[$h][1] = $langs->trans("MailAdvTargetRecipients"); + $head[$h][2] = 'advtargets'; + $h++; + } } - - $head[$h][0] = DOL_URL_ROOT."/comm/mailing/cibles.php?id=".$object->id; - $head[$h][1] = $langs->trans("MailRecipients"); - $head[$h][2] = 'targets'; - $h++; - - $head[$h][0] = DOL_URL_ROOT."/comm/mailing/advtargetemailing.php?id=".$object->id; - $head[$h][1] = $langs->trans("MailAdvTargetRecipients"); - $head[$h][2] = 'advtargets'; - $h++; - + $head[$h][0] = DOL_URL_ROOT."/comm/mailing/info.php?id=".$object->id; $head[$h][1] = $langs->trans("Info"); $head[$h][2] = 'info'; diff --git a/htdocs/includes/multiselect/MIT-LICENSE.txt b/htdocs/includes/jquery/plugins/multiselect/MIT-LICENSE.txt similarity index 100% rename from htdocs/includes/multiselect/MIT-LICENSE.txt rename to htdocs/includes/jquery/plugins/multiselect/MIT-LICENSE.txt diff --git a/htdocs/includes/multiselect/css/ui.multiselect.css b/htdocs/includes/jquery/plugins/multiselect/css/ui.multiselect.css similarity index 100% rename from htdocs/includes/multiselect/css/ui.multiselect.css rename to htdocs/includes/jquery/plugins/multiselect/css/ui.multiselect.css diff --git a/htdocs/includes/multiselect/js/ui.multiselect.js b/htdocs/includes/jquery/plugins/multiselect/js/ui.multiselect.js similarity index 100% rename from htdocs/includes/multiselect/js/ui.multiselect.js rename to htdocs/includes/jquery/plugins/multiselect/js/ui.multiselect.js From 0d0336037582fcd37872589135b4be7e30e6d151 Mon Sep 17 00:00:00 2001 From: phf Date: Fri, 6 May 2016 09:21:21 +0200 Subject: [PATCH 611/834] Fix no confirm window if we delete buying price --- htdocs/langs/en_US/products.lang | 3 +++ htdocs/product/fournisseurs.php | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index b481a7e1f60..96bf0da9bfa 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -325,3 +325,6 @@ ProductVolume=Volume for 1 product WeightUnits=Weight unit VolumeUnits=Volume unit SizeUnits=Size unit +DeleteProductBuyPrice=Delete buying price +ConfirmDeleteProductBuyPrice=Are you sure you want to delete this buying price? + diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index ce6815911e1..6a76055d3fc 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -115,7 +115,7 @@ if (empty($reshook)) } } - if ($action == 'remove_pf') + if ($action == 'confirm_remove_pf') { if ($rowid) // id of product supplier price to remove { @@ -285,6 +285,12 @@ if ($id > 0 || $ref) if ($result) { + if ($action == 'ask_remove_pf') { + $form = new Form($db); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $id . '&rowid=' . $rowid, $langs->trans('DeleteProductBuyPrice'), $langs->trans('ConfirmDeleteProductBuyPrice'), 'confirm_remove_pf', '', 0, 1); + echo $formconfirm; + } + if ($action <> 'edit' && $action <> 're-edit') { $head=product_prepare_head($object); @@ -680,7 +686,7 @@ if ($id > 0 || $ref) if ($user->rights->produit->creer || $user->rights->service->creer) { print ''.img_edit().""; - print ''.img_picto($langs->trans("Remove"),'disable.png').''; + print ''.img_picto($langs->trans("Remove"),'disable.png').''; } print ''; From a05963ee2b2813475e90b178a9fa8b4b6bd3f1b9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 6 May 2016 11:22:58 +0200 Subject: [PATCH 612/834] Better position for clear button --- htdocs/comm/mailing/cibles.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index 1282aab5558..0df0ad2568b 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -1,6 +1,6 @@ - * Copyright (C) 2005-2015 Laurent Destailleur + * Copyright (C) 2005-2016 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin * Copyright (C) 2014 Florian Henry * @@ -398,12 +398,12 @@ if ($object->fetch($id) >= 0) print ''; print ''; + $cleartext=''; if ($allowaddtarget) { - $cleartext='
    '.$langs->trans("ToClearAllRecipientsClickHere").': '.''; + $cleartext=$langs->trans("ToClearAllRecipientsClickHere").' '.''; } - - print_barre_liste($langs->trans("MailSelectedRecipients").$cleartext,$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,"",$num,$nbtotalofrecords,'',0,'','',$limit); - + print_barre_liste($langs->trans("MailSelectedRecipients"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,$cleartext,$num,$nbtotalofrecords,'',0,'','',$limit); + print ''; print "\n\n"; From 91d300289fefd2868ecc84a0aa0063d140f1fb11 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 6 May 2016 12:15:44 +0200 Subject: [PATCH 613/834] Minor fix in presentation --- htdocs/comm/mailing/card.php | 21 +++++++++++++-------- htdocs/comm/mailing/cibles.php | 4 ++-- htdocs/langs/en_US/mails.lang | 2 ++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index 9a7d0349f41..1a54340e7ce 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -948,7 +948,7 @@ else } } - print '

    '; + print '
    '; } // Affichage formulaire de TEST @@ -1052,20 +1052,25 @@ else print ''; - // Ref - print ''; + $linkback = ''.$langs->trans("BackToList").''; + + print ''; + print ''; + // Topic - print ''; + print ''; // From - print ''; + print ''; // To - print ''; + print ''; // Status - print ''; + print ''; // Nb of distinct emails - print ''; if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) { - print ''; + print ''; } if (! empty($arrayfields['d.ref']['checked'])) print_liste_field_titre($arrayfields['d.ref']['label'],$_SERVER["PHP_SELF"],'d.rowid','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['d.firstname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.firstname']['checked']) && empty($arrayfields['d.lastname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.lastname']['checked'])) print_liste_field_titre($arrayfields['d.lastname']['label'],$_SERVER["PHP_SELF"],'d.lastname','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.company']['checked'])) print_liste_field_titre($arrayfields['d.company']['label'],$_SERVER["PHP_SELF"],'d.company','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.login']['checked'])) print_liste_field_titre($arrayfields['d.login']['label'],$_SERVER["PHP_SELF"],'d.login','',$param,'',$sortfield,$sortorder); @@ -359,39 +360,89 @@ if ($resql) // Line for filters fields print ''; + if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) + { + print ''; + } + // Ref - if (! empty($arrayfields['c.ref']['checked'])) + if (! empty($arrayfields['d.ref']['checked'])) { print ''; } + + if (! empty($arrayfields['d.firstname']['checked']) || ! empty($arrayfields['d.lastname']['checked']) || ! empty($arrayfields['d.company']['checked'])) + { + print ''; + } - print ''; + if (! empty($arrayfields['d.login']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.morphy']['checked'])) + { + print ''; + } - print ''; + if (! empty($arrayfields['t.libelle']['checked'])) + { + print ''; + } - print ''; + if (! empty($arrayfields['d.address']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.zip']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.town']['checked'])) + { + print ''; + } - print ''; - - print ''; - - print ''; + if (! empty($arrayfields['d.email']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.datefin']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.datec']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.tms']['checked'])) + { + print ''; + } $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Status - print ''; + if (! empty($arrayfields['d.statut']['checked'])) + { + print ''; + } // Action column print '\n"; + if (! empty($arrayfields['d.ref']['checked'])) + { + print "\n"; + } // Lastname - print "\n"; + if (! empty($arrayfields['d.firstname']['checked']) || ! empty($arrayfields['d.lastname']['checked']) || ! empty($arrayfields['d.company']['checked'])) + { + print "\n"; + } // Login - print "\n"; + if (! empty($arrayfields['d.login']['checked'])) + { + print "\n"; + } + + // Moral/Physique + if (! empty($arrayfields['d.morphy']['checked'])) + { + print "\n"; + } // Type - $membertypestatic->id=$objp->type_id; - $membertypestatic->libelle=$objp->type; - print ''; - - // Moral/Physique - print "\n"; + if (! empty($arrayfields['t.libelle']['checked'])) + { + $membertypestatic->id=$objp->type_id; + $membertypestatic->libelle=$objp->type; + print ''; + } + + // Address + if (! empty($arrayfields['d.address']['checked'])) + { + print "\n"; + } + + // Zip + if (! empty($arrayfields['d.zip']['checked'])) + { + print "\n"; + } + + // Town + if (! empty($arrayfields['d.town']['checked'])) + { + print "\n"; + } // EMail print "\n"; @@ -462,15 +546,10 @@ if ($resql) $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; - // Statut - print '"; - // End of subscription date if ($datefin) { - print ''; } + + // Date create + if (! empty($arrayfields['d.datec']['checked'])) + { + print "\n"; + } + + // Date modif + if (! empty($arrayfields['d.tms']['checked'])) + { + print "\n"; + } + + // Statut + print '"; // Actions print '\n"; @@ -432,7 +432,7 @@ if ($resql) print ''; // Status/Percent - print ''; + print ''; print ''; diff --git a/htdocs/core/class/fiscalyear.class.php b/htdocs/core/class/fiscalyear.class.php index 7d4dff8de44..ef990d8edf3 100644 --- a/htdocs/core/class/fiscalyear.class.php +++ b/htdocs/core/class/fiscalyear.class.php @@ -39,6 +39,7 @@ class Fiscalyear extends CommonObject var $label; var $date_start; var $date_end; + var $datec; var $statut; // 0=open, 1=closed var $entity; From 47103fdac79ceb122740a3ceaf862ec691d15858 Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 6 May 2016 22:11:05 +0200 Subject: [PATCH 620/834] Fix Remove unused variables --- htdocs/comm/propal/class/propal.class.php | 28 +++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 3d43d6b00f2..56c3839dcb6 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -780,7 +780,7 @@ class Propal extends CommonObject */ function create($user, $notrigger=0) { - global $langs,$conf,$mysoc,$hookmanager; + global $conf,$hookmanager; $error=0; $now=dol_now(); @@ -1080,7 +1080,7 @@ class Propal extends CommonObject */ function createFromClone($socid=0) { - global $db, $user,$langs,$conf,$hookmanager; + global $user,$conf,$hookmanager; dol_include_once('/projet/class/project.class.php'); @@ -1214,7 +1214,6 @@ class Propal extends CommonObject */ function fetch($rowid,$ref='') { - global $conf; $sql = "SELECT p.rowid, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc"; $sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht"; @@ -1508,7 +1507,7 @@ class Propal extends CommonObject { require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - global $conf,$langs; + global $conf; $error=0; $now=dol_now(); @@ -1868,7 +1867,6 @@ class Propal extends CommonObject */ function reopen($user, $statut, $note='', $notrigger=0) { - global $langs,$conf; $this->statut = $statut; $error=0; @@ -1979,7 +1977,7 @@ class Propal extends CommonObject $outputlangs->setDefaultLang($newlang); } //$ret=$object->fetch($id); // Reload to get new records - $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + $this->generateDocument($modelpdf, $outputlangs); } // Call trigger @@ -2082,7 +2080,7 @@ class Propal extends CommonObject */ function liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC') { - global $conf,$user; + global $user; $ga = array(); @@ -2247,7 +2245,7 @@ class Propal extends CommonObject */ function delete($user, $notrigger=0) { - global $conf,$langs; + global $conf; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $error=0; @@ -2610,7 +2608,7 @@ class Propal extends CommonObject */ function initAsSpecimen() { - global $user,$langs,$conf; + global $langs; // Charge tableau des produits prodids $prodids = array(); @@ -2696,7 +2694,7 @@ class Propal extends CommonObject */ function load_state_board() { - global $conf, $user; + global $user; $this->nb=array(); $clause = "WHERE"; @@ -2741,7 +2739,7 @@ class Propal extends CommonObject */ function getNextNumRef($soc) { - global $conf, $db, $langs; + global $conf,$langs; $langs->load("propal"); if (! empty($conf->global->PROPALE_ADDON)) @@ -2942,7 +2940,7 @@ class Propal extends CommonObject */ public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0) { - global $conf,$user,$langs; + global $conf,$langs; $langs->load("propale"); @@ -3196,7 +3194,7 @@ class PropaleLigne extends CommonObjectLine */ function insert($notrigger=0) { - global $conf,$langs,$user; + global $conf,$user; $error=0; @@ -3332,7 +3330,7 @@ class PropaleLigne extends CommonObjectLine */ function delete() { - global $conf,$langs,$user; + global $conf,$user; $error=0; $this->db->begin(); @@ -3383,7 +3381,7 @@ class PropaleLigne extends CommonObjectLine */ function update($notrigger=0) { - global $conf,$langs,$user; + global $conf,$user; $error=0; From 9e7e23a8ab17a18580495c71b8f28ba73cf9a368 Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 6 May 2016 22:30:34 +0200 Subject: [PATCH 621/834] Fix Remove unused variables --- htdocs/commande/class/commande.class.php | 30 ++++++++++-------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index a0babc4d972..b148fe428e8 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2012 Laurent Destailleur * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2006 Andre Cianfarani - * Copyright (C) 2010-2015 Juanjo Menent + * Copyright (C) 2010-2016 Juanjo Menent * Copyright (C) 2011 Jean Heimburger * Copyright (C) 2012-2014 Christophe Battarel * Copyright (C) 2012 Cedric Salvador @@ -204,7 +204,7 @@ class Commande extends CommonOrder */ function getNextNumRef($soc) { - global $db, $langs, $conf; + global $langs, $conf; $langs->load("order"); if (! empty($conf->global->COMMANDE_ADDON)) @@ -239,7 +239,7 @@ class Commande extends CommonOrder } else { - dol_print_error($db,get_class($this)."::getNextNumRef ".$obj->error); + dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); return ""; } } @@ -500,7 +500,6 @@ class Commande extends CommonOrder */ function set_reopen($user) { - global $conf,$langs; $error=0; if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED) @@ -560,7 +559,7 @@ class Commande extends CommonOrder */ function cloture($user) { - global $conf, $langs; + global $conf; $error=0; @@ -697,7 +696,7 @@ class Commande extends CommonOrder */ function create($user, $notrigger=0) { - global $conf,$langs,$mysoc,$hookmanager; + global $conf,$langs; $error=0; // Clean parameters @@ -971,7 +970,7 @@ class Commande extends CommonOrder */ function createFromClone($socid=0) { - global $conf,$user,$langs,$hookmanager; + global $user,$hookmanager; $error=0; @@ -1061,7 +1060,7 @@ class Commande extends CommonOrder */ function createFromProposal($object) { - global $db, $conf,$user,$langs,$hookmanager; + global $conf,$user,$hookmanager; dol_include_once('/core/class/extrafields.class.php'); @@ -1134,7 +1133,7 @@ class Commande extends CommonOrder // get extrafields from original line $object->fetch_optionals($object->id); - $e = new ExtraFields($db); + $e = new ExtraFields($this->db); $element_extrafields = $e->fetch_name_optionals_label($this->element); foreach($object->array_options as $options_key => $value) { @@ -1495,7 +1494,6 @@ class Commande extends CommonOrder */ function fetch($id, $ref='', $ref_ext='', $ref_int='') { - global $conf; // Check parameters if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1; @@ -1854,7 +1852,7 @@ class Commande extends CommonOrder * @param int $fk_product Filter on a product * @return int <0 if KO, Nb of lines found if OK */ - function loadExpeditions($filtre_statut=-1, $fk_product=0) + function loadExpeditions($filtre_statut=-1) { $this->expeditions = array(); @@ -1983,7 +1981,6 @@ class Commande extends CommonOrder */ function deleteline($lineid) { - global $user; if ($this->statut == self::STATUS_DRAFT) { @@ -2262,7 +2259,7 @@ class Commande extends CommonOrder */ function liste_array($shortlist=0, $draft=0, $excluser='', $socid=0, $limit=0, $offset=0, $sortfield='c.date_commande', $sortorder='DESC') { - global $conf,$user; + global $user; $ga = array(); @@ -2430,7 +2427,7 @@ class Commande extends CommonOrder */ function classifyBilled(User $user) { - global $conf, $user, $langs; + global $user; $error = 0; $this->db->begin(); @@ -2698,7 +2695,6 @@ class Commande extends CommonOrder */ function update($user=null, $notrigger=0) { - global $conf, $langs; $error=0; // Clean parameters @@ -3205,7 +3201,7 @@ class Commande extends CommonOrder */ function initAsSpecimen() { - global $user,$langs,$conf; + global $langs; dol_syslog(get_class($this)."::initAsSpecimen"); @@ -3287,7 +3283,7 @@ class Commande extends CommonOrder */ function load_state_board() { - global $conf, $user; + global $user; $this->nb=array(); $clause = "WHERE"; From 5f2807041749127b858d30b970db4a9d7b648ff7 Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 6 May 2016 22:43:50 +0200 Subject: [PATCH 622/834] Fix Remove unused variables --- htdocs/expedition/class/expedition.class.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 18b3d4d7ebc..35ef726be20 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -3,7 +3,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2007 Franky Van Liedekerke * Copyright (C) 2006-2012 Laurent Destailleur - * Copyright (C) 2011-2015 Juanjo Menent + * Copyright (C) 2011-2016 Juanjo Menent * Copyright (C) 2013 Florian Henry * Copyright (C) 2014 Cedric GROSS * Copyright (C) 2014-2015 Marcos García @@ -90,6 +90,7 @@ class Expedition extends CommonObject var $date_creation; var $date_valid; + var $meths; var $listmeths; // List of carriers /** @@ -119,7 +120,7 @@ class Expedition extends CommonObject */ function getNextNumRef($soc) { - global $db, $langs, $conf; + global $langs, $conf; $langs->load("sendings"); if (!empty($conf->global->EXPEDITION_ADDON_NUMBER)) @@ -156,7 +157,7 @@ class Expedition extends CommonObject } else { - dol_print_error($db,get_class($this)."::getNextNumRef ".$obj->error); + dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); return ""; } } @@ -176,7 +177,7 @@ class Expedition extends CommonObject */ function create($user, $notrigger=0) { - global $conf, $langs, $hookmanager; + global $conf, $hookmanager; $now=dol_now(); @@ -978,7 +979,7 @@ class Expedition extends CommonObject */ function update($user=null, $notrigger=0) { - global $conf, $langs; + global $conf; $error=0; // Clean parameters @@ -1532,7 +1533,7 @@ class Expedition extends CommonObject */ function initAsSpecimen() { - global $user,$langs,$conf; + global $langs; $now=dol_now(); @@ -1644,7 +1645,7 @@ class Expedition extends CommonObject function fetch_delivery_methods() { global $langs; - $meths = array(); + $this->meths = array(); $sql = "SELECT em.rowid, em.code, em.libelle"; $sql.= " FROM ".MAIN_DB_PREFIX."c_shipment_mode as em"; @@ -1799,7 +1800,6 @@ class Expedition extends CommonObject */ function setClosed() { - global $conf; $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=2'; $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0'; @@ -1825,7 +1825,6 @@ class Expedition extends CommonObject */ function set_billed() { - global $conf; $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=2, billed=1'; // TODO Update only billed $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0'; @@ -1851,7 +1850,6 @@ class Expedition extends CommonObject */ function reOpen() { - global $conf; $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=1'; $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0'; @@ -1882,7 +1880,7 @@ class Expedition extends CommonObject */ public function generateDocument($modele, $outputlangs,$hidedetails=0, $hidedesc=0, $hideref=0) { - global $conf,$user,$langs; + global $conf,$langs; $langs->load("sendings"); From fd6dbf5d1a288cd9791c38d0debf653243109ace Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 6 May 2016 22:59:03 +0200 Subject: [PATCH 623/834] Fix Remove unused variables --- htdocs/compta/facture/class/facture.class.php | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 8fee9cc5758..bea200f2656 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -7,7 +7,7 @@ * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2006 Andre Cianfarani * Copyright (C) 2007 Franky Van Liedekerke - * Copyright (C) 2010-2014 Juanjo Menent + * Copyright (C) 2010-2016 Juanjo Menent * Copyright (C) 2012-2014 Christophe Battarel * Copyright (C) 2012-2015 Marcos García * Copyright (C) 2012 Cédric Salvador @@ -734,7 +734,7 @@ class Facture extends CommonInvoice */ function createFromClone($socid=0) { - global $conf,$user,$langs,$hookmanager; + global $user,$hookmanager; $error=0; @@ -849,7 +849,7 @@ class Facture extends CommonInvoice */ function createFromOrder($object) { - global $conf,$user,$langs,$hookmanager; + global $user,$hookmanager; $error=0; @@ -1289,7 +1289,6 @@ class Facture extends CommonInvoice */ function update($user=null, $notrigger=0) { - global $conf, $langs; $error=0; // Clean parameters @@ -1682,7 +1681,6 @@ class Facture extends CommonInvoice */ function set_paid($user,$close_code='',$close_note='') { - global $conf,$langs; $error=0; if ($this->paye != 1) @@ -1740,7 +1738,6 @@ class Facture extends CommonInvoice */ function set_unpaid($user) { - global $conf,$langs; $error=0; $this->db->begin(); @@ -1790,9 +1787,6 @@ class Facture extends CommonInvoice */ function set_canceled($user,$close_code='',$close_note='') { - global $conf,$langs; - - $error=0; dol_syslog(get_class($this)."::set_canceled rowid=".$this->id, LOG_DEBUG); @@ -2463,6 +2457,7 @@ class Facture extends CommonInvoice */ function updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $txtva, $txlocaltax1=0, $txlocaltax2=0, $price_base_type='HT', $info_bits=0, $type= self::TYPE_STANDARD, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0, $label='', $special_code=0, $array_options=0, $situation_percent=0, $fk_unit = null) { + global $conf,$user; // Deprecation warning if ($label) { dol_syslog(__METHOD__ . ": using line label is deprecated", LOG_WARNING); @@ -2667,7 +2662,7 @@ class Facture extends CommonInvoice */ function update_percent($line, $percent) { - global $mysoc; + global $mysoc,$user; include_once(DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'); @@ -2693,7 +2688,6 @@ class Facture extends CommonInvoice */ function deleteline($rowid) { - global $langs, $conf; dol_syslog(get_class($this)."::deleteline rowid=".$rowid, LOG_DEBUG); @@ -2931,7 +2925,7 @@ class Facture extends CommonInvoice */ function getNextNumRef($soc,$mode='next') { - global $conf, $db, $langs; + global $conf, $langs; $langs->load("bills"); // Clean parameters (if not defined or using deprecated value) @@ -2993,7 +2987,7 @@ class Facture extends CommonInvoice * set up mask. */ if ($mode != 'last' && !$numref) { - dol_print_error($db,"Facture::getNextNumRef ".$obj->error); + dol_print_error($this->db,"Facture::getNextNumRef ".$obj->error); return ""; } @@ -3307,7 +3301,6 @@ class Facture extends CommonInvoice */ function demande_prelevement($fuser, $amount=0) { - global $langs; $error=0; @@ -3527,7 +3520,7 @@ class Facture extends CommonInvoice */ function initAsSpecimen($option='') { - global $user,$langs,$conf; + global $langs; $now=dol_now(); $arraynow=dol_getdate($now); @@ -3712,7 +3705,7 @@ class Facture extends CommonInvoice */ public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0) { - global $conf,$user,$langs; + global $conf,$langs; $langs->load("bills"); @@ -3812,7 +3805,6 @@ class Facture extends CommonInvoice */ function setFinal() { - global $conf, $langs, $user; $this->db->begin(); @@ -4276,7 +4268,7 @@ class FactureLigne extends CommonInvoiceLine */ function update($user='',$notrigger=0) { - global $user,$langs,$conf; + global $user,$conf; $error=0; @@ -4402,9 +4394,7 @@ class FactureLigne extends CommonInvoiceLine */ function delete() { - global $conf,$langs,$user; - - $error=0; + global $user; $this->db->begin(); From bba986e975b630a78fcd47c5cd9f5c12f03ffb45 Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 6 May 2016 23:35:56 +0200 Subject: [PATCH 624/834] Fix Remove unused variables --- htdocs/commande/class/commande.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index b148fe428e8..a22e937da87 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -1849,7 +1849,6 @@ class Commande extends CommonOrder * Note: For a dedicated shipment, the fetch_lines load the qty_asked and qty_shipped. This function return qty_shipped cuulated for order * * @param int $filtre_statut Filter on status - * @param int $fk_product Filter on a product * @return int <0 if KO, Nb of lines found if OK */ function loadExpeditions($filtre_statut=-1) From 25dfa0c5b95375800cbd1a43c43b805f5ed27a03 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 16:38:32 +0200 Subject: [PATCH 625/834] Work on website --- htdocs/core/lib/functions.lib.php | 2 +- htdocs/langs/en_US/website.lang | 7 ++- htdocs/public/websites/index.php | 19 ++++++-- htdocs/websites/class/websitepage.class.php | 22 ++++----- htdocs/websites/index.php | 49 +++++++++++++-------- 5 files changed, 64 insertions(+), 35 deletions(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 0634d4c6746..d4f1a38fbbb 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -2775,7 +2775,7 @@ function dol_print_error($db='',$error='',$errors=null) foreach($errors as $msg) { - $msg=$langs->trans($msg); + if (empty($msg)) continue; if ($_SERVER['DOCUMENT_ROOT']) // Mode web { $out.="".$langs->trans("Message").": ".$msg."
    \n" ; diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index d94ac13cd05..9ce7cfb09a3 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -16,7 +16,10 @@ Website=Web site AddPage=Add page Page=Page PreviewOfSiteNotYetAvailable=Preview of your website %s not yet available. You must first add a page. -PageDeleted=Page %s of website %s deleted +RequestedPageHasNoContentYet=Requested page with id %s has not content yet or cache file .tpl.php was removed. Edit content of page to solve this. +PageDeleted=Page '%s' of website %s deleted +PageAdded=Page '%s' added ViewSiteInNewTab=View site in new tab ViewPageInNewTab=View page in new tab -SetAsHomePage=Set as Home page \ No newline at end of file +SetAsHomePage=Set as Home page +RealURL=Real URL \ No newline at end of file diff --git a/htdocs/public/websites/index.php b/htdocs/public/websites/index.php index b54a69e90f9..4625bc1573a 100644 --- a/htdocs/public/websites/index.php +++ b/htdocs/public/websites/index.php @@ -48,8 +48,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $error=0; $website=GETPOST('website', 'alpha'); -$page=GETPOST('page', 'alpha'); -$pageid=GETPOST('pageid', 'alpha'); +$pageid=GETPOST('page', 'alpha')?GETPOST('page', 'alpha'):GETPOST('pageid', 'alpha'); $accessallowed = 1; $type=''; @@ -81,13 +80,23 @@ if (empty($pageid)) $pageid=$firstrep->id; } } - +if (empty($pageid)) +{ + $langs->load("website"); + print $langs->trans("PreviewOfSiteNotYetAvailable"); + exit; +} // Security: Delete string ../ into $original_file global $dolibarr_main_data_root; if ($pageid == 'css') { + header('Content-type: text/css'); + // Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access. + //if (empty($dolibarr_nocache)) header('Cache-Control: max-age=3600, public, must-revalidate'); + //else + header('Cache-Control: no-cache'); $original_file=$dolibarr_main_data_root.'/websites/'.$website.'/styles.css'; } else @@ -127,7 +136,9 @@ $original_file_osencoded=dol_osencode($original_file); // New file name encoded // This test if file exists should be useless. We keep it to find bug more easily if (! file_exists($original_file_osencoded)) { - dol_print_error(0,$langs->trans("ErrorFileDoesNotExists",$original_file)); + $langs->load("website"); + print $langs->trans("RequestedPageHasNoContentYet", $pageid); + //dol_print_error(0,$langs->trans("ErrorFileDoesNotExists",$original_file)); exit; } diff --git a/htdocs/websites/class/websitepage.class.php b/htdocs/websites/class/websitepage.class.php index aa0063f9a2b..65e56e030a1 100644 --- a/htdocs/websites/class/websitepage.class.php +++ b/htdocs/websites/class/websitepage.class.php @@ -253,15 +253,16 @@ class WebsitePage extends CommonObject /** * Load object in memory from the database * - * @param string $sortorder Sort Order - * @param string $sortfield Sort field - * @param int $limit limit - * @param int $offset Offset - * @param array $filter Filter array - * @param string $filtermode Filter mode (AND or OR) - * @return array|int int <0 if KO, array of pages if OK + * @param string $websiteid Web site + * @param string $sortorder Sort Order + * @param string $sortfield Sort field + * @param int $limit limit + * @param int $offset Offset + * @param array $filter Filter array + * @param string $filtermode Filter mode (AND or OR) + * @return array|int int <0 if KO, array of pages if OK */ - public function fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter = array(), $filtermode='AND') + public function fetchAll($websiteid, $sortorder='', $sortfield='', $limit=0, $offset=0, array $filter = array(), $filtermode='AND') { dol_syslog(__METHOD__, LOG_DEBUG); @@ -280,14 +281,15 @@ class WebsitePage extends CommonObject $sql .= " t.date_modification,"; $sql .= " t.tms"; $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t'; + $sql .= ' WHERE t.fk_website = '.$websiteid; // Manage filter $sqlwhere = array(); if (count($filter) > 0) { foreach ($filter as $key => $value) { if ($key=='t.rowid' || $key=='t.fk_website') { - $sqlwhere [] = $key . '='. $value; + $sqlwhere[] = $key . '='. $value; } else { - $sqlwhere [] = $key . ' LIKE \'%' . $this->db->escape($value) . '%\''; + $sqlwhere[] = $key . ' LIKE \'%' . $this->db->escape($value) . '%\''; } } } diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php index 73581307c83..ae29dab196b 100644 --- a/htdocs/websites/index.php +++ b/htdocs/websites/index.php @@ -126,6 +126,11 @@ $pathofwebsite=$dolibarr_main_data_root.'/websites/'.$website; $filecss=$pathofwebsite.'/styles.css'; $filetpl=$pathofwebsite.'/page'.$pageid.'.tpl.php'; +// Define $urlwithroot +$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); +$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file +//$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current + /* @@ -162,7 +167,7 @@ if ($action == 'add') if (! $error) { $db->commit(); - setEventMessages($langs->trans("PageAdded"), null, 'mesgs'); + setEventMessages($langs->trans("PageAdded", $objectpage->pageurl), null, 'mesgs'); $action=''; } else @@ -475,6 +480,9 @@ if (count($object->records) > 0) if ($website) { + print ' - '.$langs->trans("RealURL").' '; + $realurl=$urlwithroot.'/public/websites/index.php?website='.$website; + print ' '; print ''.$langs->trans("ViewSiteInNewTab").''; } print ''; @@ -508,8 +516,10 @@ if (count($object->records) > 0) { print ''; - $array=$objectpage->fetchAll('','',0,0,array('t.fk_website'=>$object->id)); - + $array=$objectpage->fetchAll($object->id); + if (! is_array($array) && $array < 0) dol_print_error('', $objectpage->error, $objectpage->errors); + $atleastonepage=(is_array($array) && count($array) > 0); + print '
    '; print '
    '; print $langs->trans("Page").': '; @@ -517,30 +527,33 @@ if (count($object->records) > 0) print '
    '; $out=''; $out.=''; print $out; - print ''; - print ''; + print ''; + print ''; //print $form->selectarray('page', $array); if ($website && $pageid > 0) { - print ''.$langs->trans("ViewPageInNewTab").''; + print ' - '.$langs->trans("RealURL").' '; + $realurl=$urlwithroot.'/public/websites/index.php?website='.$website.'&page='.$pageid; + print ' '; + print ''.$langs->trans("ViewPageInNewTab").''; } print '
    '; From 82745d32dff785178552f51cd76b96d18ae2cb40 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 17:12:31 +0200 Subject: [PATCH 626/834] NEW Survey system has now a status like other objects. You can close or reopen a survey. --- .../install/mysql/migration/3.9.0-4.0.0.sql | 1 + .../mysql/tables/llx_opensurvey_sondage.sql | 1 + htdocs/langs/en_US/opensurvey.lang | 2 +- htdocs/opensurvey/card.php | 230 +++++++++++------- .../class/opensurveysondage.class.php | 95 +++++++- htdocs/opensurvey/list.php | 13 +- htdocs/opensurvey/results.php | 2 +- htdocs/public/opensurvey/studs.php | 2 +- 8 files changed, 244 insertions(+), 102 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 895f2869b48..8e0092a73c0 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -35,6 +35,7 @@ ALTER TABLE llx_product_customer_price ADD COLUMN localtax2_type varchar(10) NO ALTER TABLE llx_product_customer_price_log ADD COLUMN localtax1_type varchar(10) NOT NULL DEFAULT '0' after localtax1_tx; ALTER TABLE llx_product_customer_price_log ADD COLUMN localtax2_type varchar(10) NOT NULL DEFAULT '0' after localtax2_tx; +ALTER TABLE llx_opensurvey_sondage ADD COLUMN status integer DEFAULT 1 after date_fin; ALTER TABLE llx_expedition ADD COLUMN billed smallint DEFAULT 0; diff --git a/htdocs/install/mysql/tables/llx_opensurvey_sondage.sql b/htdocs/install/mysql/tables/llx_opensurvey_sondage.sql index b728702db79..5ea2183758a 100644 --- a/htdocs/install/mysql/tables/llx_opensurvey_sondage.sql +++ b/htdocs/install/mysql/tables/llx_opensurvey_sondage.sql @@ -24,6 +24,7 @@ CREATE TABLE llx_opensurvey_sondage ( fk_user_creat integer NOT NULL, titre TEXT NOT NULL, date_fin DATETIME NOT NULL, + status integer DEFAULT 1, format VARCHAR(2) NOT NULL, -- 'A' = Text choice (choices are saved into sujet field), 'D' = Date choice (choices are saved into sujet field), 'F' = Form survey mailsonde tinyint NOT NULL DEFAULT 0, allow_comments tinyint NOT NULL DEFAULT 1, diff --git a/htdocs/langs/en_US/opensurvey.lang b/htdocs/langs/en_US/opensurvey.lang index 4ae116d4c33..bc90d1488e3 100644 --- a/htdocs/langs/en_US/opensurvey.lang +++ b/htdocs/langs/en_US/opensurvey.lang @@ -62,5 +62,5 @@ ErrorOpenSurveyOneChoice=Enter at least one choice ErrorOpenSurveyDateFormat=Date must have the format YYYY-MM-DD ErrorInsertingComment=There was an error while inserting your comment MoreChoices=Enter more choices for the voters -SurveyExpiredInfo=The voting time of this poll has expired. +SurveyExpiredInfo=The poll has been closed or voting delay has expired. EmailSomeoneVoted=%s has filled a line.\nYou can find your poll at the link: \n%s diff --git a/htdocs/opensurvey/card.php b/htdocs/opensurvey/card.php index c419a804210..ce941a680c7 100644 --- a/htdocs/opensurvey/card.php +++ b/htdocs/opensurvey/card.php @@ -35,6 +35,8 @@ if (!$user->rights->opensurvey->read) accessforbidden(); // Initialisation des variables $action=GETPOST('action'); +$cancel=GETPOST('cancel'); + $numsondage = ''; if (GETPOST('id')) { @@ -58,98 +60,119 @@ $expiredate=dol_mktime(0, 0, 0, GETPOST('expiremonth'), GETPOST('expireday'), GE * Actions */ +$parameters = array('id' => $numsondage); +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); -// Delete -if ($action == 'delete_confirm') +if (empty($reshook)) { - // Security check - if (!$user->rights->opensurvey->write) accessforbidden(); + if ($cancel) $action=''; + + // Delete + if ($action == 'delete_confirm') + { + // Security check + if (!$user->rights->opensurvey->write) accessforbidden(); + + $result=$object->delete($user,'',$numsondage); + + header('Location: '.dol_buildpath('/opensurvey/list.php',1)); + exit(); + } + + // Close + if ($action == 'close') + { + $object->status = Opensurveysondage::STATUS_CLOSED; + $object->update(); + } + + // Reopend + if ($action == 'reopen') + { + $object->status = Opensurveysondage::STATUS_VALIDATED; + $object->update(); + } + + // Update + if ($action == 'update') + { + // Security check + if (!$user->rights->opensurvey->write) accessforbidden(); + + $error=0; + + if (! GETPOST('nouveautitre')) + { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Title")), null, 'errors'); + $error++; + $action = 'edit'; + } + + if (! $error) + { + $object->titre = GETPOST('nouveautitre'); + $object->commentaires = GETPOST('nouveauxcommentaires'); + $object->mail_admin = GETPOST('nouvelleadresse'); + $object->date_fin = $expiredate; + $object->allow_comments = GETPOST('cancomment') == 'on' ? true : false; + $object->allow_spy = GETPOST('canseeothersvote') == 'on' ? true : false; + $object->mailsonde = GETPOST('mailsonde') == 'on' ? true : false; + + $res=$object->update($user); + if ($res < 0) + { + setEventMessages($object->error, $object->errors, 'errors'); + $action='edit'; + } + } + } - $result=$object->delete($user,'',$numsondage); - - header('Location: '.dol_buildpath('/opensurvey/list.php',1)); - exit(); -} - -// Update -if ($action == 'update') -{ - // Security check - if (!$user->rights->opensurvey->write) accessforbidden(); - - $error=0; - - if (! GETPOST('nouveautitre')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Title")), null, 'errors'); - $error++; - $action = 'edit'; - } - - if (! $error) - { - $object->titre = GETPOST('nouveautitre'); - $object->commentaires = GETPOST('nouveauxcommentaires'); - $object->mail_admin = GETPOST('nouvelleadresse'); - $object->date_fin = $expiredate; - $object->allow_comments = GETPOST('cancomment') == 'on' ? true : false; - $object->allow_spy = GETPOST('canseeothersvote') == 'on' ? true : false; - $object->mailsonde = GETPOST('mailsonde') == 'on' ? true : false; - - $res=$object->update($user); - if ($res < 0) - { - setEventMessages($object->error, $object->errors, 'errors'); - $action='edit'; - } - } -} - - -// Add comment -if (GETPOST('ajoutcomment')) -{ - $error=0; - - if (! GETPOST('comment')) - { - $error++; - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Comment")), null, 'errors'); - } - if (! GETPOST('commentuser')) - { - $error++; - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("User")), null, 'errors'); - } - - if (! $error) - { - $comment = GETPOST("comment"); - $comment_user = GETPOST('commentuser'); - - $resql = $object->addComment($comment, $comment_user); - - if (! $resql) - { - setEventMessages($langs->trans('ErrorInsertingComment'), null, 'errors'); - } - } -} - -// Delete comment -$idcomment=GETPOST('deletecomment','int'); -if ($idcomment) -{ - // Security check - if (!$user->rights->opensurvey->write) accessforbidden(); - - $resql = $object->deleteComment($idcomment); -} - -if ($action == 'edit') { - - // Security check - if (!$user->rights->opensurvey->write) accessforbidden(); + // Add comment + if (GETPOST('ajoutcomment')) + { + $error=0; + + if (! GETPOST('comment')) + { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Comment")), null, 'errors'); + } + if (! GETPOST('commentuser')) + { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("User")), null, 'errors'); + } + + if (! $error) + { + $comment = GETPOST("comment"); + $comment_user = GETPOST('commentuser'); + + $resql = $object->addComment($comment, $comment_user); + + if (! $resql) + { + setEventMessages($langs->trans('ErrorInsertingComment'), null, 'errors'); + } + } + } + + // Delete comment + $idcomment=GETPOST('deletecomment','int'); + if ($idcomment) + { + // Security check + if (!$user->rights->opensurvey->write) accessforbidden(); + + $resql = $object->deleteComment($idcomment); + } + + if ($action == 'edit') { + + // Security check + if (!$user->rights->opensurvey->write) accessforbidden(); + } } @@ -196,7 +219,7 @@ print '
    '.$langs->trans("Ref").''.$object->id.'
    '.$langs->trans("Ref").''; + print $form->showrefnav($object,'id', $linkback); + print '
    '.$langs->trans("MailTitle").''.$object->titre.'
    '.$langs->trans("MailTitle").''.$object->titre.'
    '.$langs->trans("MailFrom").''.dol_print_email($object->email_from,0,0,0,0,1).'
    '.$langs->trans("MailFrom").''.dol_print_email($object->email_from,0,0,0,0,1).'
    '.$langs->trans("MailErrorsTo").''.dol_print_email($object->email_errorsto,0,0,0,0,1).'
    '.$langs->trans("MailErrorsTo").''.dol_print_email($object->email_errorsto,0,0,0,0,1).'
    '.$langs->trans("Status").''.$object->getLibStatut(4).'
    '.$langs->trans("Status").''.$object->getLibStatut(4).'
    '; + print '
    '; print $langs->trans("TotalNbOfDistinctRecipients"); print ''; $nbemail = ($object->nbemail?$object->nbemail:img_warning('').' '.$langs->trans("NoTargetYet").''); diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index 0df0ad2568b..2ce7d5c110d 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -227,7 +227,7 @@ if ($object->fetch($id) >= 0) // Show email selectors if ($allowaddtarget && $user->rights->mailing->creer) { - print load_fiche_titre($langs->trans("ToAddRecipientsChooseHere"),($user->admin?info_admin($langs->trans("YouCanAddYourOwnPredefindedListHere"),1):''),''); + print load_fiche_titre($langs->trans("ToAddRecipientsChooseHere"), ($user->admin?info_admin($langs->trans("YouCanAddYourOwnPredefindedListHere"),1):''), 'title_generic'); print ''; print ''; @@ -402,7 +402,7 @@ if ($object->fetch($id) >= 0) if ($allowaddtarget) { $cleartext=$langs->trans("ToClearAllRecipientsClickHere").' '.''; } - print_barre_liste($langs->trans("MailSelectedRecipients"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,$cleartext,$num,$nbtotalofrecords,'',0,'','',$limit); + print_barre_liste($langs->trans("MailSelectedRecipients"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,$cleartext,$num,$nbtotalofrecords,'title_generic',0,'','',$limit); print ''; diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index ff87db130b5..01ff6c7b486 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -169,3 +169,5 @@ AdvTgtDeleteFilter=Delete filter AdvTgtSaveFilter=Save filter AdvTgtCreateFilter=Create filter AdvTgtOrCreateNewFilter=Name of new filter +NoContactWithCategoryFound=No contact/address with a category found +NoContactLinkedToThirdpartieWithCategoryFound=No contact/address with a category found \ No newline at end of file From 3093105cd0ed955773cc38f9fd6ce6288c4ddcac Mon Sep 17 00:00:00 2001 From: abb Date: Fri, 6 May 2016 11:20:07 +0100 Subject: [PATCH 614/834] fix:erroneous form tag --- htdocs/core/tpl/contacts.tpl.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/htdocs/core/tpl/contacts.tpl.php b/htdocs/core/tpl/contacts.tpl.php index b3eaeeec503..9b0291747ce 100644 --- a/htdocs/core/tpl/contacts.tpl.php +++ b/htdocs/core/tpl/contacts.tpl.php @@ -139,14 +139,12 @@ if ($permission) { } ?> -
    -
    trans("Nature"); ?>
    -
    trans("ThirdParty"); ?>
    -
    trans("Users").'/'.$langs->trans("Contacts"); ?>
    -
    trans("ContactType"); ?>
    -
    trans("Status"); ?>
    -
     
    - +
    trans("Source"); ?>
    +
    trans("Company"); ?>
    +
    trans("Contacts"); ?>
    +
    trans("ContactType"); ?>
    +
    trans("Status"); ?>
    +
     
    From 89684ce0f4fe8525f0d6a3e6ea4b1d66f75d52cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Fri, 6 May 2016 16:19:02 +0200 Subject: [PATCH 615/834] put colspan in td instead tr --- htdocs/contrat/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/contrat/index.php b/htdocs/contrat/index.php index f962a9e82ae..18a60a3429d 100644 --- a/htdocs/contrat/index.php +++ b/htdocs/contrat/index.php @@ -273,7 +273,7 @@ if (! empty($conf->contrat->enabled) && $user->rights->contrat->lire) } else { - print '
    '; + print ''; } print "
    '.$langs->trans("NoContracts").'
    '.$langs->trans("NoContracts").'

    "; $db->free($resql); From d5a18ab584645aecc638fe2489a70d1bb32e0883 Mon Sep 17 00:00:00 2001 From: philippe grand Date: Fri, 6 May 2016 17:07:14 +0200 Subject: [PATCH 616/834] add column numbering name and fix a lot of issues within members list --- htdocs/adherents/list.php | 180 ++++++++++++++++++++++++++-------- htdocs/langs/en_US/other.lang | 1 + 2 files changed, 139 insertions(+), 42 deletions(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 1cc09394023..43aeee8ebdf 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -3,6 +3,7 @@ * Copyright (C) 2002-2003 Jean-Louis Bergamo * Copyright (C) 2004-2014 Laurent Destailleur * Copyright (C) 2013-2015 Raphaël Doursenaud + * Copyright (C) 2016 Philippe Grand * * 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 @@ -320,10 +321,10 @@ if ($resql) print '
     '.$langs->trans("NumberingShort").'
     '; print ''; print ''; + print ''; - print ''; + print ' '; - print ''; + $listetype=$membertypestatic->liste_array(); + print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); + print ''; - print '   '; - $listetype=$membertypestatic->liste_array(); - print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); - print ' '; - print ''; + print '     '; @@ -431,29 +482,62 @@ if ($resql) } // Ref - print ""; - print $memberstatic->getNomUrl(1); - print ""; + print $memberstatic->getNomUrl(1); + print "rowid\">"; - print ((! empty($objp->lastname) || ! empty($objp->firstname)) ? dol_trunc($memberstatic->getFullName($langs)) : ''); - print (((! empty($objp->lastname) || ! empty($objp->firstname)) && ! empty($companyname)) ? ' / ' : ''); - print (! empty($companyname) ? dol_trunc($companyname, 32) : ''); - print "rowid\">"; + print ((! empty($objp->lastname) || ! empty($objp->firstname)) ? dol_trunc($memberstatic->getFullName($langs)) : ''); + print (((! empty($objp->lastname) || ! empty($objp->firstname)) && ! empty($companyname)) ? ' / ' : ''); + print (! empty($companyname) ? dol_trunc($companyname, 32) : ''); + print "".$objp->login."".$objp->login."".$memberstatic->getmorphylib($objp->morphy)."'; - print $membertypestatic->getNomUrl(1,32); - print '".$memberstatic->getmorphylib($objp->morphy)."'; + print $membertypestatic->getNomUrl(1,32); + print '".$memberstatic->address."".$memberstatic->zip."".$memberstatic->town."".dol_print_email($objp->email,0,0,1)."'; - print $memberstatic->LibStatut($objp->statut,$objp->cotisation,$datefin,2); - print "'; + print ''; print dol_print_date($datefin,'day'); if ($memberstatic->hasDelay()) { print " ".img_warning($langs->trans("SubscriptionLate")); @@ -491,6 +570,23 @@ if ($resql) } print '".$memberstatic->datec."".$memberstatic->datem."'; + print $memberstatic->LibStatut($objp->statut,$objp->cotisation,$datefin,2); + print "'; diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 3000eecf33b..6187293aefd 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -1,6 +1,7 @@ # Dolibarr language file - Source file is en_US - other SecurityCode=Security code Calendar=Calendar +NumberingShort=N° Tools=Tools ToolsDesc=This area is dedicated to group miscellaneous tools not available into other menu entries.

    Those tools can be reached from menu on the side. Birthday=Birthday From 9508d8a5c04f0244333f08540e786be56a7b542c Mon Sep 17 00:00:00 2001 From: Scrutinizer Auto-Fixer Date: Fri, 6 May 2016 19:10:59 +0000 Subject: [PATCH 617/834] Scrutinizer Auto-Fixes This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com --- .../comm/mailing/class/html.formadvtargetemailing.class.php | 4 ++-- htdocs/core/class/html.form.class.php | 6 +++--- htdocs/societe/class/api_thirdparty.class.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php index ac6b5727c58..9e9cea1c391 100644 --- a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php +++ b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php @@ -455,8 +455,8 @@ class FormAdvTargetEmailing extends Form * selectAdvtargetemailingTemplate * * @param string $htmlname control name - * @param number $selected defaut selected - * @param number $showempty empty lines + * @param integer $selected defaut selected + * @param integer $showempty empty lines * * @return string HTML combo */ diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index c65fcb1bdc5..74cd27bc460 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -5250,7 +5250,7 @@ class Form * * @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') - * @return int <0 if KO, >0 if OK + * @return string <0 if KO, >0 if OK */ function showLinkToObjectBlock($object, $restrictlinksto=array()) { @@ -5468,7 +5468,7 @@ class Form * @param int $option 0 return yes/no, 1 return 1/0 * @param bool $disabled true or false * @param int $useempty 1=Add empty line - * @return mixed See option + * @return string See option */ function selectyesno($htmlname,$value='',$option=0,$disabled=false,$useempty='') { @@ -5810,7 +5810,7 @@ class Form * @param string $include Array list of groups id to include * @param int $enableonly Array list of groups id to be enabled. All other must be disabled * @param int $force_entity 0 or Id of environment to force - * @return void + * @return string * @see select_dolusers */ function select_dolgroups($selected='', $htmlname='groupid', $show_empty=0, $exclude='', $disabled=0, $include='', $enableonly='', $force_entity=0) diff --git a/htdocs/societe/class/api_thirdparty.class.php b/htdocs/societe/class/api_thirdparty.class.php index 9c65d08ac43..507d95639e6 100644 --- a/htdocs/societe/class/api_thirdparty.class.php +++ b/htdocs/societe/class/api_thirdparty.class.php @@ -370,7 +370,7 @@ class ThirdpartyApi extends DolibarrApi * Delete thirdparty * * @param int $id Thirparty ID - * @return type + * @return integer * * @url DELETE thirdparty/{id} * @url DELETE customer/{id} From bf09e4a0b947850e80c27c69e008b3175cafcb8a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 6 May 2016 21:28:06 +0200 Subject: [PATCH 618/834] PHPdoc --- .../mailing/class/advtargetemailing.class.php | 13 ++-- .../html.formadvtargetemailing.class.php | 5 +- htdocs/websites/class/website.class.php | 67 ++++++++++++++----- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/htdocs/comm/mailing/class/advtargetemailing.class.php b/htdocs/comm/mailing/class/advtargetemailing.class.php index 599173a1869..62cf5f2436f 100644 --- a/htdocs/comm/mailing/class/advtargetemailing.class.php +++ b/htdocs/comm/mailing/class/advtargetemailing.class.php @@ -17,15 +17,14 @@ */ /** - * \file class/advtargetemailing.class.php - * \ingroup advtargetemailing + * \file comm/mailing/class/advtargetemailing.class.php + * \ingroup mailing * \brief This file is an example CRUD class file (Create/Read/Update/Delete) - */ /** - * Put your class' description here + * Class to manage advanced emailing target selector */ class AdvanceTargetingMailing extends CommonObject { @@ -293,7 +292,7 @@ class AdvanceTargetingMailing extends CommonObject * @param int $notrigger 0=launch triggers after, 1=disable triggers * @return int <0 if KO, >0 if OK */ - function update($user=0, $notrigger=0) + function update($user, $notrigger=0) { global $conf, $langs; $error=0; @@ -737,9 +736,9 @@ class AdvanceTargetingMailing extends CommonObject * * @param string $column_to_test column to test * @param string $criteria Use %% as magic caracters. For exemple to find all item like jean, joe, jim, you can input j%%, you can also use ; as separator for value, - * and use ! for except this value. + * and use ! for except this value. * For exemple jean;joe;jim%%;!jimo;!jima%> will target all jean, joe, start with jim but not jimo and not everythnig taht start by jima - * @return int <0 if KO, >0 if OK + * @return string Sql to use for the where condition */ public function transformToSQL($column_to_test,$criteria) { $return_sql_criteria = '('; diff --git a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php index ac6b5727c58..8c2adbe08f8 100644 --- a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php +++ b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php @@ -17,8 +17,9 @@ */ /** - * \file advtargetemailing/class/html.formadvtragetemaling.class.php - * \brief Fichier de la classe des fonctions predefinie de composants html advtargetemaling + * \file comm/mailing/class/html.formadvtragetemaling.class.php + * \ingroup mailing + * \brief Fichier de la classe des fonctions predefinie de composants html advtargetemaling */ /** diff --git a/htdocs/websites/class/website.class.php b/htdocs/websites/class/website.class.php index a82a0833aba..e87e830bb77 100644 --- a/htdocs/websites/class/website.class.php +++ b/htdocs/websites/class/website.class.php @@ -54,16 +54,37 @@ class Website extends CommonObject public $lines = array(); /** + * @var int */ - public $entity; + /** + * @var string + */ public $ref; + /** + * @var string + */ public $description; + /** + * @var int + */ public $status; - public $date_creation = ''; - public $date_modification = ''; + /** + * @var mixed + */ + public $date_creation; + /** + * @var mixed + */ + public $date_modification; + /** + * @var mixed + */ public $tms = ''; - public $fk_default_home; + /** + * @var integer + */ + public $fk_default_home; public $records; /** @@ -625,20 +646,36 @@ class WebsiteLine */ public $id; /** - * @var mixed Sample line property 1 + * @var int */ - public $entity; - public $ref; - public $description; - public $status; - public $fk_default_home; - public $date_creation = ''; - public $date_modification = ''; - public $tms = ''; - /** - * @var mixed Sample line property 2 + * @var string */ + public $ref; + /** + * @var string + */ + public $description; + /** + * @var int + */ + public $status; + /** + * @var int + */ + public $fk_default_home; + /** + * @var mixed + */ + public $date_creation; + /** + * @var mixed + */ + public $date_modification; + /** + * @var mixed + */ + public $tms = ''; } From d48438346728a97cfaf8f01587d790511b464d67 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 6 May 2016 22:06:23 +0200 Subject: [PATCH 619/834] Minor errors --- htdocs/comm/action/listactions.php | 4 ++-- htdocs/core/class/fiscalyear.class.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/comm/action/listactions.php b/htdocs/comm/action/listactions.php index cfb3bc92333..31290318742 100644 --- a/htdocs/comm/action/listactions.php +++ b/htdocs/comm/action/listactions.php @@ -309,7 +309,7 @@ if ($resql) print_liste_field_titre($langs->trans("ThirdParty"),$_SERVER["PHP_SELF"],"s.nom",$param,"","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("Contact"),$_SERVER["PHP_SELF"],"a.fk_contact",$param,"","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("ActionsOwnedBy"),$_SERVER["PHP_SELF"],"",$param,"","",$sortfield,$sortorder); - print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"a.percent",$param,"",'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"a.percent",$param,"",'align="center"',$sortfield,$sortorder); print_liste_field_titre(""); print "
    '.$actionstatic->LibStatut($obj->percent,6).''.$actionstatic->LibStatut($obj->percent,6).'
    '; $linkback = ''.$langs->trans("BackToList").''; // Ref -print ''; +print ''; print ''; @@ -219,6 +242,12 @@ if ($action == 'edit') else print dol_htmlentities($object->titre); print ''; +// Status +print ''; + // Description print ''."\n"; @@ -107,13 +110,14 @@ print ''; print ''; $arraystatus=array(''=>' ','expired'=>$langs->trans("Expired"),'opened'=>$langs->trans("Opened")); print ''; +print ''; print ''; print ''."\n"; -$sql = "SELECT p.id_sondage, p.fk_user_creat, p.format, p.date_fin, p.titre, p.nom_admin,"; +$sql = "SELECT p.id_sondage, p.fk_user_creat, p.format, p.date_fin, p.status, p.titre, p.nom_admin,"; $sql.= " u.login, u.firstname, u.lastname"; $sql.= " FROM ".MAIN_DB_PREFIX."opensurvey_sondage as p"; $sql.= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."user u ON u.rowid = p.fk_user_creat"; @@ -150,6 +154,9 @@ while ($i < min($num,$limit)) } else dol_print_error($db); + $opensurvey_static->id=$obj->id_sondage; + $opensurvey_static->status=$obj->status; + $var=!$var; print ''; print ''; print''."\n"; - + print ''; + print''."\n"; + print''."\n"; print ''."\n"; diff --git a/htdocs/opensurvey/results.php b/htdocs/opensurvey/results.php index 5c14a20de23..93ccd93729b 100644 --- a/htdocs/opensurvey/results.php +++ b/htdocs/opensurvey/results.php @@ -432,7 +432,7 @@ print '
    '.$langs->trans('Ref').'
    '.$langs->trans('Ref').''; print $form->showrefnav($object, 'id', $linkback, 1, 'id_sondage', 'id_sondage'); print '
    '; +print $langs->trans("Status") .''; +print $object->getLibStatut(4); +print '
    '.$langs->trans("Description") .''; if ($action == 'edit') @@ -326,7 +355,11 @@ dol_fiche_end(); if ($action == 'edit') { - print '
    '; + print '
    '; + print ''; + print '   '; + print ''; + print '
    '; } print ''."\n"; @@ -340,9 +373,20 @@ print '
    '; if ($action != 'edit' && $user->rights->opensurvey->write) { - //Modify button - print ''.$langs->trans("Modify") . ''; + //Modify button + print ''.$langs->trans("Modify") . ''; + if ($object->status == Opensurveysondage::STATUS_VALIDATED) + { + //Close button + print ''.$langs->trans("Close") . ''; + } + if ($object->status == Opensurveysondage::STATUS_CLOSED) + { + //Opened button + print ''.$langs->trans("ReOpen") . ''; + } + //Delete button print ''.$langs->trans('Delete').''; } diff --git a/htdocs/opensurvey/class/opensurveysondage.class.php b/htdocs/opensurvey/class/opensurveysondage.class.php index c3be8fc4793..aaf5028fd8f 100644 --- a/htdocs/opensurvey/class/opensurveysondage.class.php +++ b/htdocs/opensurvey/class/opensurveysondage.class.php @@ -56,6 +56,7 @@ class Opensurveysondage extends CommonObject var $titre; var $date_fin=''; + var $status=1; var $format; var $mailsonde; @@ -73,6 +74,22 @@ class Opensurveysondage extends CommonObject */ public $allow_spy; + + /** + * Draft status (not used) + */ + const STATUS_DRAFT = 0; + /** + * Validated/Opened status + */ + const STATUS_VALIDATED = 1; + /** + * Closed + */ + const STATUS_CLOSED = 2; + + + /** * Constructor * @@ -115,6 +132,7 @@ class Opensurveysondage extends CommonObject $sql.= "fk_user_creat,"; $sql.= "titre,"; $sql.= "date_fin,"; + $sql.= "status,"; $sql.= "format,"; $sql.= "mailsonde,"; $sql.= "allow_comments,"; @@ -127,6 +145,7 @@ class Opensurveysondage extends CommonObject $sql.= " ".$user->id.","; $sql.= " '".$this->db->escape($this->titre)."',"; $sql.= " '".$this->db->idate($this->date_fin)."',"; + $sql.= " ".$this->status.","; $sql.= " '".$this->db->escape($this->format)."',"; $sql.= " ".$this->db->escape($this->mailsonde).","; $sql.= " ".$this->db->escape($this->allow_comments).","; @@ -190,6 +209,7 @@ class Opensurveysondage extends CommonObject $sql.= " t.fk_user_creat,"; $sql.= " t.titre,"; $sql.= " t.date_fin,"; + $sql.= " t.status,"; $sql.= " t.format,"; $sql.= " t.mailsonde,"; $sql.= " t.allow_comments,"; @@ -217,6 +237,7 @@ class Opensurveysondage extends CommonObject $this->nom_admin = $obj->nom_admin; $this->titre = $obj->titre; $this->date_fin = $this->db->jdate($obj->date_fin); + $this->status = $obj->status; $this->format = $obj->format; $this->mailsonde = $obj->mailsonde; $this->allow_comments = $obj->allow_comments; @@ -274,6 +295,7 @@ class Opensurveysondage extends CommonObject $sql.= " nom_admin=".(isset($this->nom_admin)?"'".$this->db->escape($this->nom_admin)."'":"null").","; $sql.= " titre=".(isset($this->titre)?"'".$this->db->escape($this->titre)."'":"null").","; $sql.= " date_fin=".(dol_strlen($this->date_fin)!=0 ? "'".$this->db->idate($this->date_fin)."'" : 'null').","; + $sql.= " status=".(isset($this->status)?"'".$this->db->escape($this->status)."'":"null").","; $sql.= " format=".(isset($this->format)?"'".$this->db->escape($this->format)."'":"null").","; $sql.= " mailsonde=".(isset($this->mailsonde)?$this->db->escape($this->mailsonde):"null").","; $sql.= " allow_comments=".$this->db->escape($this->allow_comments).","; @@ -428,12 +450,13 @@ class Opensurveysondage extends CommonObject $this->id=0; $this->id_sondage=''; - $this->commentaires=''; + $this->commentaires='Comment of the specimen survey'; $this->mail_admin=''; $this->nom_admin=''; - $this->titre=''; - $this->date_fin=''; - $this->format=''; + $this->titre='This is a specimen survey'; + $this->date_fin=dol_now()+3600*24*10; + $this->status=1; + $this->format='classic'; $this->mailsonde=''; } @@ -518,10 +541,74 @@ class Opensurveysondage extends CommonObject $this->mail_admin = trim($this->mail_admin); $this->nom_admin = trim($this->nom_admin); $this->titre = trim($this->titre); + $this->status = trim($this->status); $this->format = trim($this->format); $this->mailsonde = ($this->mailsonde ? 1 : 0); $this->allow_comments = ($this->allow_comments ? 1 : 0); $this->allow_spy = ($this->allow_spy ? 1 : 0); $this->sujet = trim($this->sujet); } + + + /** + * Return status label of Order + * + * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto + * @return string Libelle + */ + function getLibStatut($mode) + { + return $this->LibStatut($this->status,$mode); + } + + /** + * Return label of status + * + * @param int $status Id statut + * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto + * @return string Label of status + */ + function LibStatut($status,$mode) + { + global $langs, $conf; + + //print 'x'.$status.'-'.$billed; + if ($mode == 0) + { + if ($status==self::STATUS_DRAFT) return $langs->trans('Draft'); + if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened').$billedtext; + if ($status==self::STATUS_CLOSED) return $langs->trans('Closed'); + } + elseif ($mode == 1) + { + if ($status==self::STATUS_DRAFT) return $langs->trans('Draft'); + if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened').$billedtext; + if ($status==self::STATUS_CLOSED) return $langs->trans('Closed'); + } + elseif ($mode == 2) + { + if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened'),'statut1').' '.$langs->trans('Opened').$billedtext; + if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6').' '.$langs->trans('Closed'); + } + elseif ($mode == 3) + { + if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0'); + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened').$billedtext,'statut1'); + if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6'); + } + elseif ($mode == 4) + { + if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened').$billedtext,'statut1').' '.$langs->trans('Opened').$billedtext; + if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6').' '.$langs->trans('Closed'); + } + elseif ($mode == 5) + { + if ($status==self::STATUS_DRAFT) return ''.$langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut0'); + if ($status==self::STATUS_VALIDATED) return ''.$langs->trans('Opened').$billedtext.' '.img_picto($langs->trans('Opened').$billedtext,'statut1'); + if ($status==self::STATUS_CLOSED) return ''.$langs->trans('Closed').' '.img_picto($langs->trans('Closed'),'statut6'); + } + } + } diff --git a/htdocs/opensurvey/list.php b/htdocs/opensurvey/list.php index b440b5ee43e..aed5610bee0 100644 --- a/htdocs/opensurvey/list.php +++ b/htdocs/opensurvey/list.php @@ -25,6 +25,7 @@ require_once('../main.inc.php'); require_once(DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"); require_once(DOL_DOCUMENT_ROOT."/core/lib/files.lib.php"); +require_once(DOL_DOCUMENT_ROOT."/opensurvey/class/opensurveysondage.class.php"); // Security check if (!$user->rights->opensurvey->read) accessforbidden(); @@ -68,6 +69,7 @@ if (GETPOST('button_removefilter')) */ $form=new Form($db); +$opensurvey_static = new Opensurveysondage($db); $now = dol_now(); @@ -96,6 +98,7 @@ print_liste_field_titre($langs->trans("Type")); print_liste_field_titre($langs->trans("Author"), $_SERVER["PHP_SELF"], "u.".$fieldtosortuser,$param,"","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("NbOfVoters")); print_liste_field_titre($langs->trans("ExpireDate"), $_SERVER["PHP_SELF"], "p.date_fin",$param,"",'align="center"',$sortfield,$sortorder); +print_liste_field_titre($langs->trans("Status"), $_SERVER["PHP_SELF"], "p.status",$param,"",'align="center"',$sortfield,$sortorder); print_liste_field_titre(''); print '
    '. $form->selectarray('status', $arraystatus, $status).''; $searchpitco=$form->showFilterAndCheckAddButtons(0); print $searchpitco; print '
    '; @@ -176,11 +183,13 @@ while ($i < min($num,$limit)) print ''.$nbuser.''.dol_print_date($db->jdate($obj->date_fin),'day'); if ($db->jdate($obj->date_fin) < time()) { print ' ('.$langs->trans("Expired").')'; } print ''.$opensurvey_static->getLibStatut(5).'
    '; $linkback = ''.$langs->trans("BackToList").''; // Ref -print ''; +print ''; print ''; diff --git a/htdocs/public/opensurvey/studs.php b/htdocs/public/opensurvey/studs.php index 33bd1ba1373..834f17b8b2c 100644 --- a/htdocs/public/opensurvey/studs.php +++ b/htdocs/public/opensurvey/studs.php @@ -46,7 +46,7 @@ if ($result <= 0) dol_print_error('','Failed to get survey id '.$numsondage); $nblignes=$object->fetch_lines(); //If the survey has not yet finished, then it can be modified -$canbemodified = (empty($object->date_fin) || $object->date_fin > dol_now()); +$canbemodified = ((empty($object->date_fin) || $object->date_fin > dol_now()) && $object->status != Opensurveysondage::STATUS_CLOSED); /* From 47f27f353aad72b77e5b7996f36173c8859d0ed8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 17:42:34 +0200 Subject: [PATCH 627/834] update list of member --- htdocs/adherents/list.php | 91 ++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 29 deletions(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 1cc09394023..e4203209dec 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -360,45 +360,78 @@ if ($resql) print ''; // Ref - if (! empty($arrayfields['c.ref']['checked'])) + if (! empty($arrayfields['d.ref']['checked'])) { print ''; } - print ''; + if (! empty($arrayfields['d.firstname']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.lastname']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.company']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.login']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['t.libelle']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.address']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.zip']['checked'])) + { + print ''; + } + + if (! empty($arrayfields['d.town']['checked'])) + { + print ''; + } - print ''; + if (! empty($arrayfields['d.email']['checked'])) + { + print ''; + } - print ''; + if (! empty($arrayfields['d.datefin']['checked'])) + { + print ''; + } + - print ''; - - print ''; - - print ''; - - $parameters=array(); - $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - - // Status - print ''; - - // Action column - print ''; + print "\n"; $var=True; From 3784af00ea6b4f7329fe2b6c2233f278d66f8521 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 18:50:05 +0200 Subject: [PATCH 628/834] NEW Selection of fields is available on member list --- htdocs/adherents/list.php | 425 ++++++++++++++++++++++++++++---------- htdocs/commande/list.php | 3 +- htdocs/societe/list.php | 4 +- 3 files changed, 318 insertions(+), 114 deletions(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index e4203209dec..1a5eadfbde7 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -1,7 +1,7 @@ * Copyright (C) 2002-2003 Jean-Louis Bergamo - * Copyright (C) 2004-2014 Laurent Destailleur + * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2013-2015 Raphaël Doursenaud * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; $langs->load("members"); $langs->load("companies"); @@ -43,6 +44,11 @@ $search_ref=GETPOST("search_ref"); $search_lastname=GETPOST("search_lastname"); $search_firstname=GETPOST("search_firstname"); $search_login=GETPOST("search_login"); +$search_address=GETPOST("search_address"); +$search_zip=GETPOST("search_zip"); +$search_town=GETPOST("search_town"); +$search_state=GETPOST("search_state"); +$search_country=GETPOST("search_country"); $type=GETPOST("type"); $search_email=GETPOST("search_email"); $search_categ = GETPOST("search_categ",'int'); @@ -50,6 +56,8 @@ $catid = GETPOST("catid",'int'); $sall=GETPOST("sall"); $optioncss = GETPOST('optioncss','alpha'); +if ($statut < -1) $statut = ''; + $limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit; $sortfield = GETPOST("sortfield",'alpha'); $sortorder = GETPOST("sortorder",'alpha'); @@ -88,7 +96,7 @@ $arrayfields=array( 'd.ref'=>array('label'=>$langs->trans("Ref"), 'checked'=>1), 'd.lastname'=>array('label'=>$langs->trans("Lastname"), 'checked'=>1), 'd.firstname'=>array('label'=>$langs->trans("Firstname"), 'checked'=>1), - 'd.societe'=>array('label'=>$langs->trans("Company"), 'checked'=>1), + 'd.company'=>array('label'=>$langs->trans("Company"), 'checked'=>1), 'd.login'=>array('label'=>$langs->trans("Login"), 'checked'=>1), 'd.morphy'=>array('label'=>$langs->trans("MorPhy"), 'checked'=>1), 't.libelle'=>array('label'=>$langs->trans("Type"), 'checked'=>1), @@ -96,8 +104,10 @@ $arrayfields=array( 'd.address'=>array('label'=>$langs->trans("Address"), 'checked'=>0), 'd.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0), 'd.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0), - 'd.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0), - 'd.note_private'=>array('label'=>$langs->trans("NotePrivate"), 'checked'=>0), + 'state.nom'=>array('label'=>$langs->trans("State"), 'checked'=>0), + 'country.code_iso'=>array('label'=>$langs->trans("Country"), 'checked'=>0), + /*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0), + 'd.note_private'=>array('label'=>$langs->trans("NotePrivate"), 'checked'=>0),*/ 'd.datefin'=>array('label'=>$langs->trans("EndSubscription"), 'checked'=>1, 'position'=>500), 'd.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500), 'd.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500), @@ -139,10 +149,13 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_address=""; $search_zip=""; $search_town=""; + $search_state=""; + $search_country=''; $search_morphy=""; $search_categ=""; $catid=""; $sall=""; + $statut=''; } @@ -160,9 +173,11 @@ llxHeader('',$langs->trans("Member"),'EN:Module_Foundations|FR:Module_Adhé $now=dol_now(); $sql = "SELECT d.rowid, d.login, d.lastname, d.firstname, d.societe as company, d.fk_soc,"; -$sql.= " d.datefin,"; -$sql.= " d.email, d.fk_adherent_type as type_id, d.morphy, d.statut,"; -$sql.= " t.libelle as type, t.cotisation"; +$sql.= " d.datefin, d.address, d.zip, d.town, d.state_id, d.country,"; +$sql.= " d.email, d.phone, d.phone_perso, d.phone_mobile, d.skype, d.birth, d.public, d.photo,"; +$sql.= " d.fk_adherent_type as type_id, d.morphy, d.statut, d.datec as date_creation, d.tms as date_update,"; +$sql.= " t.libelle as type, t.cotisation,"; +$sql.= " state.code_departement as state_code, state.nom as state_name"; // Add fields for extrafields foreach ($extrafields->attribute_list as $key => $val) $sql.=",ef.".$key.' as options_'.$key; // Add fields from hooks @@ -171,6 +186,8 @@ $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // N $sql.=$hookmanager->resPrint; $sql.= " FROM ".MAIN_DB_PREFIX."adherent as d"; if (! empty($search_categ) || ! empty($catid)) $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX."categorie_member as cm ON d.rowid = cm.fk_member"; // We need this table joined to the select in order to filter by categ +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as country on (country.rowid = d.country)"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as state on (state.rowid = d.state_id)"; $sql.= ", ".MAIN_DB_PREFIX."adherent_type as t"; $sql.= " WHERE d.fk_adherent_type = t.rowid "; if ($catid > 0) $sql.= " AND cm.fk_categorie = ".$db->escape($catid); @@ -180,7 +197,7 @@ if ($search_categ == -2) $sql.= " AND cm.fk_categorie IS NULL"; $sql.= " AND d.entity IN (".getEntity('adherent', 1).")"; if ($sall) $sql.=natural_search(array_keys($fieldstosearchall), $sall); if ($type > 0) $sql.=" AND t.rowid=".$db->escape($type); -if (isset($_GET["statut"]) || isset($_POST["statut"])) $sql.=" AND d.statut in (".$db->escape($statut).")"; // Peut valoir un nombre ou liste de nombre separes par virgules +if ($statut != '') $sql.=" AND d.statut in (".$db->escape($statut).")"; // Peut valoir un nombre ou liste de nombre separes par virgules if ($search_ref) { if (is_numeric($search_ref)) $sql.= " AND (d.rowid = ".$db->escape($search_ref).")"; @@ -189,6 +206,10 @@ if ($search_ref) if ($search_lastname) $sql.= natural_search(array("d.firstname", "d.lastname", "d.societe"), $search_lastname); if ($search_login) $sql.= natural_search("d.login", $search_login); if ($search_email) $sql.= natural_search("d.email", $search_email); +if ($search_town) $sql.= natural_search("d.town",$search_town); +if ($search_zip) $sql.= natural_search("d.zip",$search_zip); +if ($search_state) $sql.= natural_search("state.nom",$search_state); +if ($search_country) $sql .= " AND d.country IN (".$search_country.')'; if ($filter == 'uptodate') $sql.=" AND datefin >= '".$db->idate($now)."'"; if ($filter == 'outofdate') $sql.=" AND (datefin IS NULL OR datefin < '".$db->idate($now)."')"; @@ -253,19 +274,23 @@ if ($resql) } $param=''; - if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; - if ($statut != "") $param.="&statut=".$statut; - if ($search_nom) $param.="&search_nom=".$search_nom; - if ($search_firstname) $param.="&search_firstname=".$search_firstname; - if ($search_lastname) $param.="&search_lastname=".$search_lastname; - if ($search_login) $param.="&search_login=".$search_login; - if ($search_email) $param.="&search_email=".$search_email; - if ($search_company) $param.="&search_company=".$search_company; - if ($search_zip) $param.="&search_zip=".$search_zip; - if ($search_town) $param.="&search_town=".$search_town; - if ($filter) $param.="&filter=".$filter; - if ($type > 0) $param.="&type=".$type; - if ($optioncss != '') $param.='&optioncss='.$optioncss; + if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); + if ($statut != "") $param.="&statut=".urlencode($statut); + if ($search_ref) $param.="&search_ref=".urlencode($search_ref); + if ($search_nom) $param.="&search_nom=".urlencode($search_nom); + if ($search_firstname) $param.="&search_firstname=".urlencode($search_firstname); + if ($search_lastname) $param.="&search_lastname=".urlencode($search_lastname); + if ($search_login) $param.="&search_login=".urlencode($search_login); + if ($search_email) $param.="&search_email=".urlencode($search_email); + if ($search_company) $param.="&search_company=".urlencode($search_company); + if ($search_address != '') $param.= "&search_address=".urlencode($search_address); + if ($search_town != '') $param.= "&search_town=".urlencode($search_town); + if ($search_zip != '') $param.= "&search_zip=".urlencode($search_zip); + if ($search_state != '') $param.= "&search_state=".urlencode($search_state); + if ($search_country != '') $param.= "&search_country=".urlencode($search_country); + if ($filter) $param.="&filter=".urlencode($filter); + if ($type > 0) $param.="&type=".urlencode($type); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); // Add $param from extra fields foreach ($search_array_options as $key => $val) { @@ -325,15 +350,17 @@ if ($resql) if (! empty($arrayfields['d.ref']['checked'])) print_liste_field_titre($arrayfields['d.ref']['label'],$_SERVER["PHP_SELF"],'d.rowid','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.firstname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.lastname']['checked'])) print_liste_field_titre($arrayfields['d.lastname']['label'],$_SERVER["PHP_SELF"],'d.lastname','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['d.company']['checked'])) print_liste_field_titre($arrayfields['d.company']['label'],$_SERVER["PHP_SELF"],'d.company','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.company']['checked'])) print_liste_field_titre($arrayfields['d.company']['label'],$_SERVER["PHP_SELF"],'d.societe','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.login']['checked'])) print_liste_field_titre($arrayfields['d.login']['label'],$_SERVER["PHP_SELF"],'d.login','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.morphy']['checked'])) print_liste_field_titre($arrayfields['d.morphy']['label'],$_SERVER["PHP_SELF"],'d.morphy','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['t.libelle']['checked'])) print_liste_field_titre($arrayfields['t.libelle']['label'],$_SERVER["PHP_SELF"],'t.libelle','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.address']['checked'])) print_liste_field_titre($arrayfields['d.address']['label'],$_SERVER["PHP_SELF"],'d.address','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.zip']['checked'])) print_liste_field_titre($arrayfields['d.zip']['label'],$_SERVER["PHP_SELF"],'d.zip','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.town']['checked'])) print_liste_field_titre($arrayfields['d.town']['label'],$_SERVER["PHP_SELF"],'d.town','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($langs->trans("StateShort"),$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($langs->trans("Country"),$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); if (! empty($arrayfields['d.email']['checked'])) print_liste_field_titre($arrayfields['d.email']['label'],$_SERVER["PHP_SELF"],'d.email','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'align="center"',$sortfield,$sortorder); // Extra fields if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { @@ -350,7 +377,7 @@ if ($resql) $parameters=array('arrayfields'=>$arrayfields); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; - if (! empty($arrayfields['d.datec']['checked'])) print_liste_field_titre($arrayfields['d.datec']['label'],$_SERVER["PHP_SELF"],"d.date_creation","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); + if (! empty($arrayfields['d.datec']['checked'])) print_liste_field_titre($arrayfields['d.datec']['label'],$_SERVER["PHP_SELF"],"d.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); if (! empty($arrayfields['d.tms']['checked'])) print_liste_field_titre($arrayfields['d.tms']['label'],$_SERVER["PHP_SELF"],"d.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); if (! empty($arrayfields['d.statut']['checked'])) print_liste_field_titre($arrayfields['d.statut']['label'],$_SERVER["PHP_SELF"],"d.statut","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch '); @@ -370,25 +397,31 @@ if ($resql) if (! empty($arrayfields['d.firstname']['checked'])) { print ''; + print ''; } if (! empty($arrayfields['d.lastname']['checked'])) { print ''; + print ''; } if (! empty($arrayfields['d.company']['checked'])) { print ''; + print ''; } if (! empty($arrayfields['d.login']['checked'])) { print ''; + print ''; + } + + if (! empty($arrayfields['d.morphy']['checked'])) + { + print ''; } if (! empty($arrayfields['t.libelle']['checked'])) @@ -402,25 +435,38 @@ if ($resql) if (! empty($arrayfields['d.address']['checked'])) { print ''; + print ''; } if (! empty($arrayfields['d.zip']['checked'])) { print ''; + print ''; } - if (! empty($arrayfields['d.town']['checked'])) { print ''; + print ''; } - + // State + if (! empty($arrayfields['state.nom']['checked'])) + { + print ''; + } + // Country + if (! empty($arrayfields['country.code_iso']['checked'])) + { + print ''; + } + // Email if (! empty($arrayfields['d.email']['checked'])) { print ''; + print ''; } if (! empty($arrayfields['d.datefin']['checked'])) @@ -428,31 +474,85 @@ if ($resql) print ''; } - - - + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) + { + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($arrayfields["ef.".$key]['checked'])) + { + $align=$extrafields->getAlignFlag($key); + $typeofextrafield=$extrafields->attribute_type[$key]; + print ''; + } + } + } + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields); + $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Date creation + if (! empty($arrayfields['d.datec']['checked'])) + { + print ''; + } + // Date modification + if (! empty($arrayfields['d.tms']['checked'])) + { + print ''; + } + // Status + if (! empty($arrayfields['d.statut']['checked'])) + { + print ''; + } + // Action column + print ''; print "\n"; $var=True; while ($i < $num && $i < $conf->liste_limit) { - $objp = $db->fetch_object($resql); + $obj = $db->fetch_object($resql); - $datefin=$db->jdate($objp->datefin); - $memberstatic->id=$objp->rowid; - $memberstatic->ref=$objp->rowid; - $memberstatic->lastname=$objp->lastname; - $memberstatic->firstname=$objp->firstname; - $memberstatic->statut=$objp->statut; + $datefin=$db->jdate($obj->datefin); + $memberstatic->id=$obj->rowid; + $memberstatic->ref=$obj->rowid; + $memberstatic->lastname=$obj->lastname; + $memberstatic->firstname=$obj->firstname; + $memberstatic->societe=$obj->company; + $memberstatic->statut=$obj->statut; $memberstatic->datefin= $datefin; - - if (! empty($objp->fk_soc)) { - $memberstatic->socid = $objp->fk_soc; - $memberstatic->fetch_thirdparty(); + $memberstatic->socid = $obj->fk_soc; + + if (! empty($obj->fk_soc)) { + $memberstatic->fetch_thirdparty(); $companyname=$memberstatic->thirdparty->name; } else { - $companyname=$objp->company; + $companyname=$obj->company; } $var=!$var; @@ -463,85 +563,188 @@ if ($resql) print ''; } - // Ref - print "\n"; - + // Ref + if (! empty($arrayfields['d.ref']['checked'])) + { + print "\n"; + } // Lastname - print "\n"; - - // Login - print "\n"; - - // Type - $membertypestatic->id=$objp->type_id; - $membertypestatic->libelle=$objp->type; - print ''; - - // Moral/Physique - print "\n"; - + if (! empty($arrayfields['d.lastname']['checked'])) + { + print "\n"; + } + // Firstname + if (! empty($arrayfields['d.firstname']['checked'])) + { + print "\n"; + } + // Company + if (! empty($arrayfields['d.company']['checked'])) + { + print "\n"; + } + // Login + if (! empty($arrayfields['d.login']['checked'])) + { + print "\n"; + } + // Moral/Physique + if (! empty($arrayfields['d.morphy']['checked'])) + { + print "\n"; + } + // Type label + if (! empty($arrayfields['t.libelle']['checked'])) + { + $membertypestatic->id=$obj->type_id; + $membertypestatic->libelle=$obj->type; + print ''; + } + // Address + if (! empty($arrayfields['d.address']['checked'])) + { + print ''; + } + // Town + if (! empty($arrayfields['d.town']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Zip + if (! empty($arrayfields['d.zip']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // State + if (! empty($arrayfields['state.nom']['checked'])) + { + print "\n"; + if (! $i) $totalarray['nbfield']++; + } + // Country + if (! empty($arrayfields['country.code_iso']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } // EMail - print "\n"; - - $parameters=array('obj' => $obj); - $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - - // Statut - print '"; - + if (! empty($arrayfields['d.email']['checked'])) + { + print "\n"; + } // End of subscription date - if ($datefin) + $datefin=$db->jdate($obj->datefin); + if (! empty($arrayfields['d.datefin']['checked'])) { - print ''; + if ($datefin) + { + print ''; + } + else + { + print ''; + } } - else + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { - print ''; + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($arrayfields["ef.".$key]['checked'])) + { + print 'getAlignFlag($key); + if ($align) print ' align="'.$align.'"'; + print '>'; + $tmpkey='options_'.$key; + print $extrafields->showOutputField($key, $obj->$tmpkey, '', 1); + print ''; + if (! $i) $totalarray['nbfield']++; + } + } } - - // Actions + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Date creation + if (! empty($arrayfields['d.datec']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Date modification + if (! empty($arrayfields['d.tms']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Status + if (! empty($arrayfields['d.statut']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Action column print '"; - + if (! $i) $totalarray['nbfield']++; + print "\n"; $i++; } diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php index 811e7320385..5878b640dc7 100644 --- a/htdocs/commande/list.php +++ b/htdocs/commande/list.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2012 Laurent Destailleur + * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005 Marc Barilley / Ocebo * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2012 Juanjo Menent @@ -991,6 +991,7 @@ if ($resql) if (! empty($arrayfields['c.facture']['checked'])) { print ''; + if (! $i) $totalarray['nbfield']++; } // Action column diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index b352e25a724..94212594eb2 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -212,6 +212,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_town=""; $search_zip=""; $search_state=""; + $search_country=''; $search_idprof1=''; $search_idprof2=''; $search_idprof3=''; @@ -219,7 +220,6 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_idprof5=''; $search_idprof6=''; $search_type=''; - $search_country=''; $search_type_thirdparty=''; $search_status=''; $search_stcomm=''; @@ -384,6 +384,7 @@ if ($search_account_supplier_code) $sql.= natural_search("s.code_compta_fourniss if ($search_town) $sql.= natural_search("s.town",$search_town); if ($search_zip) $sql.= natural_search("s.zip",$search_zip); if ($search_state) $sql.= natural_search("state.nom",$search_state); +if ($search_country) $sql .= " AND s.fk_pays IN (".$search_country.')'; if ($search_idprof1) $sql.= natural_search("s.siren",$search_idprof1); if ($search_idprof2) $sql.= natural_search("s.siret",$search_idprof2); if ($search_idprof3) $sql.= natural_search("s.ape",$search_idprof3); @@ -396,7 +397,6 @@ if ($search_type > 0 && in_array($search_type,array('4'))) $sql .= " AND if ($search_type == '0') $sql .= " AND s.client = 0 AND s.fournisseur = 0"; if ($search_status!='') $sql .= " AND s.status = ".$db->escape($search_status); if (!empty($conf->barcode->enabled) && $search_barcode) $sql.= " AND s.barcode LIKE '%".$db->escape($search_barcode)."%'"; -if ($search_country) $sql .= " AND s.fk_pays IN (".$search_country.')'; if ($search_type_thirdparty) $sql .= " AND s.fk_typent IN (".$search_type_thirdparty.')'; if ($search_levels) $sql .= " AND s.fk_prospectlevel IN (".$search_levels.')'; if ($search_stcomm != '' && $search_stcomm != -2) $sql.= natural_search("s.fk_stcomm",$search_stcomm,2); From 9311bb143c3deeef032892182ad68b2bbb91e680 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 18:54:36 +0200 Subject: [PATCH 629/834] Fix missing extrafields in list --- htdocs/adherents/list.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 1a5eadfbde7..01ce37ee3a8 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -74,7 +74,7 @@ $hookmanager->initHooks(array('memberlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels -$extralabels = $extrafields->fetch_name_optionals_label('member'); +$extralabels = $extrafields->fetch_name_optionals_label('adherent'); $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); // List of fields to search into when doing a "search in all" @@ -156,6 +156,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $catid=""; $sall=""; $statut=''; + $search_array_options=array(); } @@ -185,6 +186,7 @@ $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; $sql.= " FROM ".MAIN_DB_PREFIX."adherent as d"; +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."adherent_extrafields as ef on (d.rowid = ef.fk_object)"; if (! empty($search_categ) || ! empty($catid)) $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX."categorie_member as cm ON d.rowid = cm.fk_member"; // We need this table joined to the select in order to filter by categ $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as country on (country.rowid = d.country)"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as state on (state.rowid = d.state_id)"; From 606b7ef6667c52f1e962da5ee1cdaee497289691 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 7 May 2016 20:18:46 +0200 Subject: [PATCH 630/834] Revert "fix:erroneous form tag" This reverts commit 3093105cd0ed955773cc38f9fd6ce6288c4ddcac. --- htdocs/core/tpl/contacts.tpl.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/htdocs/core/tpl/contacts.tpl.php b/htdocs/core/tpl/contacts.tpl.php index 9b0291747ce..b3eaeeec503 100644 --- a/htdocs/core/tpl/contacts.tpl.php +++ b/htdocs/core/tpl/contacts.tpl.php @@ -139,12 +139,14 @@ if ($permission) { } ?> -
    trans("Source"); ?>
    -
    trans("Company"); ?>
    -
    trans("Contacts"); ?>
    -
    trans("ContactType"); ?>
    -
    trans("Status"); ?>
    -
     
    +
    +
    trans("Nature"); ?>
    +
    trans("ThirdParty"); ?>
    +
    trans("Users").'/'.$langs->trans("Contacts"); ?>
    +
    trans("ContactType"); ?>
    +
    trans("Status"); ?>
    +
     
    + From cb4e569a5fd0cd87104cc27a8eb2d46ddc7c61c6 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 10:48:55 +0200 Subject: [PATCH 631/834] FIX Merge manually PR #5161 - Bad translation key --- htdocs/comm/action/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/comm/action/card.php b/htdocs/comm/action/card.php index 62512562e9a..123189bdc87 100644 --- a/htdocs/comm/action/card.php +++ b/htdocs/comm/action/card.php @@ -1173,7 +1173,7 @@ if ($id > 0) // Clone event if($action == 'clone') { - $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . GETPOST('id'), $langs->trans('CloneAction'), $langs->trans('ConfirmCloneAction', $object->label), 'confirm_clone', $formquestion, 'yes', 1); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . GETPOST('id'), $langs->trans('CloneAction'), $langs->trans('ConfirmCloneEvent', $object->label), 'confirm_clone', $formquestion, 'yes', 1); print $formconfirm; } From c8d98dbfdcb7efdd763e0021b183854b0406e8df Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 11:33:46 +0200 Subject: [PATCH 632/834] FIX error reported by scrutinizer --- htdocs/accountancy/admin/card.php | 23 ++- .../html.formadvtargetemailing.class.php | 79 +++++---- .../mailings/advthirdparties.modules.php | 160 +++++++++--------- htdocs/product/class/product.class.php | 1 + 4 files changed, 129 insertions(+), 134 deletions(-) diff --git a/htdocs/accountancy/admin/card.php b/htdocs/accountancy/admin/card.php index f1f1218f554..0d927b7305b 100644 --- a/htdocs/accountancy/admin/card.php +++ b/htdocs/accountancy/admin/card.php @@ -76,19 +76,17 @@ if ($action == 'add') { $res = $object->create($user); - if ($res == 0) { - } else { - if ($res == - 3) { - $error = 1; - $action = "create"; - } - if ($res == - 4) { - $error = 2; - $action = "create"; - } + if ($res == - 3) { + $error = 1; + $action = "create"; + } + if ($res == - 4) { + $error = 2; + $action = "create"; } } - Header("Location: account.php"); + header("Location: account.php"); + exit; } else if ($action == 'edit') { if (! GETPOST('cancel', 'alpha')) { $result = $object->fetch($id); @@ -134,7 +132,8 @@ if ($action == 'add') { $result = $object->delete($user); if ($result > 0) { - Header("Location: account.php"); + header("Location: account.php"); + exit; } } diff --git a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php index 4c7ad5c71db..69fb747f364 100644 --- a/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php +++ b/htdocs/comm/mailing/class/html.formadvtargetemailing.class.php @@ -39,8 +39,6 @@ class FormAdvTargetEmailing extends Form global $langs; $this->db = $db; - - return 1; } /** @@ -89,52 +87,53 @@ class FormAdvTargetEmailing extends Form function multiselectCountry($htmlname = 'country_id', $selected_array=array()) { global $conf, $langs; - $langs->load ( "dict" ); - + $langs->load("dict"); + $maxlength = 0; + $out = ''; - $countryArray = array (); + $countryArray = array(); $label = array (); - $options_array = array (); + $options_array = array(); $sql = "SELECT rowid, code as code_iso, label"; $sql .= " FROM " . MAIN_DB_PREFIX . "c_country"; $sql .= " WHERE active = 1 AND code<>''"; $sql .= " ORDER BY code ASC"; - dol_syslog ( get_class ( $this ) . "::select_country sql=" . $sql ); - $resql = $this->db->query ( $sql ); + dol_syslog(get_class($this) . "::select_country sql=" . $sql); + $resql = $this->db->query($sql); if ($resql) { - $num = $this->db->num_rows ( $resql ); + $num = $this->db->num_rows($resql); $i = 0; if ($num) { $foundselected = false; - while ( $i < $num ) { + while ($i < $num) { $obj = $this->db->fetch_object ( $resql ); $countryArray [$i] ['rowid'] = $obj->rowid; $countryArray [$i] ['code_iso'] = $obj->code_iso; - $countryArray [$i] ['label'] = ($obj->code_iso && $langs->transnoentitiesnoconv ( "Country" . $obj->code_iso ) != "Country" . $obj->code_iso ? $langs->transnoentitiesnoconv ( "Country" . $obj->code_iso ) : ($obj->label != '-' ? $obj->label : '')); - $label [$i] = $countryArray [$i] ['label']; + $countryArray [$i] ['label'] = ($obj->code_iso && $langs->transnoentitiesnoconv("Country" . $obj->code_iso ) != "Country" . $obj->code_iso ? $langs->transnoentitiesnoconv ( "Country" . $obj->code_iso ) : ($obj->label != '-' ? $obj->label : '')); + $label[$i] = $countryArray[$i]['label']; $i ++; } - array_multisort ( $label, SORT_ASC, $countryArray ); + array_multisort($label, SORT_ASC, $countryArray); - foreach ( $countryArray as $row ) { - $label = dol_trunc ( $row ['label'], $maxlength, 'middle' ); - if ($row ['code_iso']) - $label .= ' (' . $row ['code_iso'] . ')'; + foreach ($countryArray as $row) { + $label = dol_trunc($row['label'], $maxlength, 'middle'); + if ($row['code_iso']) + $label .= ' (' . $row['code_iso'] . ')'; - $options_array [$row ['rowid']] = $label; + $options_array[$row['rowid']] = $label; } } } else { - dol_print_error ( $this->db ); + dol_print_error($this->db); } - return $this->advMultiselectarray ( $htmlname, $options_array, $selected_array ); + return $this->advMultiselectarray($htmlname, $options_array, $selected_array); } /** @@ -151,7 +150,7 @@ class FormAdvTargetEmailing extends Form $options_array = array (); - + $sql_usr = ''; $sql_usr .= "SELECT DISTINCT u2.rowid, u2.lastname as name, u2.firstname, u2.login"; $sql_usr .= " FROM " . MAIN_DB_PREFIX . "user as u2, " . MAIN_DB_PREFIX . "societe_commerciaux as sc"; $sql_usr .= " WHERE u2.entity IN (0," . $conf->entity . ")"; @@ -197,10 +196,10 @@ class FormAdvTargetEmailing extends Form foreach ($langs_available as $key => $value) { $label = $value; - $options_array [$key] = $label; + $options_array[$key] = $label; } asort($options_array); - return $this->advMultiselectarray ( $htmlname, $options_array, $selected_array ); + return $this->advMultiselectarray($htmlname, $options_array, $selected_array); } /** @@ -247,16 +246,18 @@ class FormAdvTargetEmailing extends Form $sql .= ' WHERE ' . $InfoFieldList [3]; } } - if (! empty ( $InfoFieldList [1] ) && $key == 'ts_payeur') { + if (! empty($InfoFieldList[1])) { $sql .= " ORDER BY nom"; } // $sql.= ' WHERE entity = '.$conf->entity; - dol_syslog ( get_class ( $this ) . "::".__METHOD__,LOG_DEBUG); - $resql = $this->db->query ( $sql ); + $options_array = array(); + + dol_syslog(get_class($this) . "::".__METHOD__,LOG_DEBUG); + $resql = $this->db->query($sql); if ($resql) { - $num = $this->db->num_rows ( $resql ); + $num = $this->db->num_rows($resql); $i = 0; if ($num) { while ( $i < $num ) { @@ -270,15 +271,15 @@ class FormAdvTargetEmailing extends Form } } - return $this->advMultiselectarray ( $htmlname, $options_array, $selected_array ); + return $this->advMultiselectarray($htmlname, $options_array, $selected_array); } /** * Return combo list with people title * - * @param string $htmlname Name of HTML select combo field - * @param array $selected_array array - * @return string HTML combo + * @param string $htmlname Name of HTML select combo field + * @param array $selected_array Array + * @return string HTML combo */ function multiselectCivility($htmlname='civilite_id',$selected_array = array()) { @@ -357,23 +358,21 @@ class FormAdvTargetEmailing extends Form // Find if keys is in selected array value if (is_array($selected_array) && count($selected_array)>0) { - $intersect_array = array_intersect_key ( $options_array, array_flip ( $selected_array ) ); + $intersect_array = array_intersect_key($options_array, array_flip($selected_array)); } else { $intersect_array=array(); } - if (count ( $options_array ) > 0) { - foreach ( $options_array as $keyoption => $valoption ) { + if (count($options_array) > 0) { + foreach ($options_array as $keyoption => $valoption) { // If key is in intersect table then it have to e selected - if (count ( $intersect_array ) > 0) { + $selected = ''; + if (count ( $intersect_array ) > 0) { if (array_key_exists ( $keyoption, $intersect_array )) { - $selected = ' selected="selected" '; - } else { - $selected = ''; + $selected = ' selected="selected"'; } } - - $return .= ''; + $return .= '' . $valoption . ''; } } diff --git a/htdocs/core/modules/mailings/advthirdparties.modules.php b/htdocs/core/modules/mailings/advthirdparties.modules.php index 9cb73a75d7a..cba8af72295 100755 --- a/htdocs/core/modules/mailings/advthirdparties.modules.php +++ b/htdocs/core/modules/mailings/advthirdparties.modules.php @@ -52,7 +52,7 @@ class mailing_advthirdparties extends MailingTargets * * @param int $mailing_id Id of mailing. No need to use it. * @param array $socid Array of id soc to add - * @param int $type_of_target define in advtargetemailing.class.php + * @param int $type_of_target Defined in advtargetemailing.class.php * @param array $contactid Array of contact id to add * @return int <0 if error, number of emails added if ok */ @@ -73,47 +73,45 @@ class mailing_advthirdparties extends MailingTargets $sql.= " WHERE s.entity IN (".getEntity('societe', 1).")"; $sql.= " AND s.rowid IN (".implode(',',$socid).")"; $sql.= " ORDER BY email"; - } - - dol_syslog(get_class($this)."::add_to_target societe sql=".$sql, LOG_DEBUG); - // Stock recipients emails into targets table - $result=$this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i = 0; - - dol_syslog(get_class($this)."::add_to_target mailing ".$num." targets found", LOG_DEBUG); - - $old = ''; - while ($i < $num) - { - $obj = $this->db->fetch_object($result); - - if (!empty($obj->email) && filter_var($obj->email, FILTER_VALIDATE_EMAIL)) { - if (!array_key_exists($obj->email, $cibles)) { - $cibles[$obj->email] = array( - 'email' => $obj->email, - 'fk_contact' => $obj->fk_contact, - 'name' => $obj->name, - 'firstname' => $obj->firstname, - 'other' => '', - 'source_url' => $this->url($obj->id,'thirdparty'), - 'source_id' => $obj->id, - 'source_type' => 'thirdparty' - ); - } - } - - $i++; - } - } - else - { - dol_syslog($this->db->error()); - $this->error=$this->db->error(); - return -1; + // Stock recipients emails into targets table + $result=$this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + + dol_syslog(get_class($this)."::add_to_target mailing ".$num." targets found", LOG_DEBUG); + + $old = ''; + while ($i < $num) + { + $obj = $this->db->fetch_object($result); + + if (!empty($obj->email) && filter_var($obj->email, FILTER_VALIDATE_EMAIL)) { + if (!array_key_exists($obj->email, $cibles)) { + $cibles[$obj->email] = array( + 'email' => $obj->email, + 'fk_contact' => $obj->fk_contact, + 'name' => $obj->name, + 'firstname' => $obj->firstname, + 'other' => '', + 'source_url' => $this->url($obj->id,'thirdparty'), + 'source_id' => $obj->id, + 'source_type' => 'thirdparty' + ); + } + } + + $i++; + } + } + else + { + dol_syslog($this->db->error()); + $this->error=$this->db->error(); + return -1; + } } } @@ -131,47 +129,45 @@ class mailing_advthirdparties extends MailingTargets $sql.= " AND socp.fk_soc IN (".implode(',',$socid).")"; } $sql.= " ORDER BY email"; - } - - - dol_syslog(get_class($this)."::add_to_target contact sql=".$sql); - // Stock recipients emails into targets table - $result=$this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i = 0; - - dol_syslog(get_class($this)."::add_to_target mailing ".$num." targets found"); - - $old = ''; - while ($i < $num) - { - $obj = $this->db->fetch_object($result); - - if (!empty($obj->email) && filter_var($obj->email, FILTER_VALIDATE_EMAIL)) { - if (!array_key_exists($obj->email, $cibles)) { - $cibles[$obj->email] = array( - 'email' => $obj->email, - 'fk_contact' =>$obj->id, - 'lastname' => $obj->lastname, - 'firstname' => $obj->firstname, - 'other' => '', - 'source_url' => $this->url($obj->id,'contact'), - 'source_id' => $obj->id, - 'source_type' => 'contact' - ); - } - } - - $i++; - } - } - else - { - dol_syslog($this->db->error()); - $this->error=$this->db->error(); - return -1; + + // Stock recipients emails into targets table + $result=$this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + + dol_syslog(get_class($this)."::add_to_target mailing ".$num." targets found"); + + $old = ''; + while ($i < $num) + { + $obj = $this->db->fetch_object($result); + + if (!empty($obj->email) && filter_var($obj->email, FILTER_VALIDATE_EMAIL)) { + if (!array_key_exists($obj->email, $cibles)) { + $cibles[$obj->email] = array( + 'email' => $obj->email, + 'fk_contact' =>$obj->id, + 'lastname' => $obj->lastname, + 'firstname' => $obj->firstname, + 'other' => '', + 'source_url' => $this->url($obj->id,'contact'), + 'source_id' => $obj->id, + 'source_type' => 'contact' + ); + } + } + + $i++; + } + } + else + { + dol_syslog($this->db->error()); + $this->error=$this->db->error(); + return -1; + } } } diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 68dfea68be7..86a11f57541 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -3041,6 +3041,7 @@ class Product extends CommonObject function get_sousproduits_arbo() { //$parent = $this->getParent(); + $parent=array(); $parent[$this->label]=array(0 => $this->id); foreach($parent as $key => $value) // key=label, value[0]=id From 0eb12f4c613427a391edb6baa6eab990bf4e9436 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 11:39:37 +0200 Subject: [PATCH 633/834] Fix scrutinizer --- .../class/opensurveysondage.class.php | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/htdocs/opensurvey/class/opensurveysondage.class.php b/htdocs/opensurvey/class/opensurveysondage.class.php index aaf5028fd8f..3b1c5beb6c1 100644 --- a/htdocs/opensurvey/class/opensurveysondage.class.php +++ b/htdocs/opensurvey/class/opensurveysondage.class.php @@ -313,15 +313,12 @@ class Opensurveysondage extends CommonObject { if (! $notrigger) { - // Uncomment this and change MYOBJECT to your own tag if you - // want this action calls a trigger. - - //// Call triggers - //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; - //$interface=new Interfaces($this->db); - //$result=$interface->run_triggers('MYOBJECT_MODIFY',$this,$user,$langs,$conf); - //if ($result < 0) { $error++; $this->errors=$interface->errors; } - //// End call triggers + // Call triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('OPENSURVEY_MODIFY',$this,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; } + // End call triggers } } @@ -343,17 +340,16 @@ class Opensurveysondage extends CommonObject } } - - /** - * Delete object in database - * + /** + * Delete object in database + * * @param User $user User that deletes * @param int $notrigger 0=launch triggers after, 1=disable triggers * @param string $numsondage Num sondage admin to delete - * @return int <0 if KO, >0 if OK - */ - function delete($user, $notrigger, $numsondage) - { + * @return int <0 if KO, >0 if OK + */ + function delete($user, $notrigger, $numsondage) + { global $conf, $langs; $error=0; From 5cf8612c583a01928cd82223360d1069d1d12f43 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 12:08:23 +0200 Subject: [PATCH 634/834] Fix scrutinizer errors --- .../actions_adherentcard_common.class.php | 19 ++++++++++--------- htdocs/core/class/commonobject.class.php | 5 +++-- .../class/opensurveysondage.class.php | 12 ++++++------ .../canvas/actions_card_common.class.php | 3 +-- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/htdocs/adherents/canvas/actions_adherentcard_common.class.php b/htdocs/adherents/canvas/actions_adherentcard_common.class.php index 8d629175902..a749ff269e5 100644 --- a/htdocs/adherents/canvas/actions_adherentcard_common.class.php +++ b/htdocs/adherents/canvas/actions_adherentcard_common.class.php @@ -48,7 +48,7 @@ abstract class ActionsAdherentCardCommon /** - * Instantiation of DAO class + * Instantiation of DAO class. Init ->object * * @return int 0 * @deprecated Using getInstanceDao should not be used. @@ -83,18 +83,18 @@ abstract class ActionsAdherentCardCommon */ function getObject($id) { - $ret = $this->getInstanceDao(); + //$ret = $this->getInstanceDao(); - if (is_object($this->object) && method_exists($this->object,'fetch')) + /*if (is_object($this->object) && method_exists($this->object,'fetch')) { if (! empty($id)) $this->object->fetch($id); } else - { + {*/ $object = new Adherent($this->db); if (! empty($id)) $object->fetch($id); $this->object = $object; - } + //} } /** @@ -121,11 +121,11 @@ abstract class ActionsAdherentCardCommon // Creation user $nuser = new User($this->db); - $result=$nuser->create_from_member($this->object,$_POST["login"]); + $result=$nuser->create_from_member($this->object,GETPOST("login")); if ($result > 0) { - $result2=$nuser->setPassword($user,$_POST["password"],0,1,1); + $result2=$nuser->setPassword($user,GETPOST("password"),0,1,1); if ($result2) { $this->db->commit(); @@ -424,13 +424,14 @@ abstract class ActionsAdherentCardCommon if ($resql) { $obj = $this->db->fetch_object($resql); + + $this->object->country_code = $obj->code; + $this->object->country = $langs->trans("Country".$obj->code)?$langs->trans("Country".$obj->code):$obj->libelle; } else { dol_print_error($this->db); } - $this->object->country_code = $obj->code; - $this->object->country = $langs->trans("Country".$obj->code)?$langs->trans("Country".$obj->code):$obj->libelle; } } diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 14dbb304333..5445733852e 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -229,12 +229,13 @@ abstract class CommonObject public $mode_reglement_id; /** - * @var string Payment terms ID + * @var int Payment terms ID * @see setPaymentTerms() */ public $cond_reglement_id; /** - * @deprecated + * @var int Payment terms ID + * @deprecated Kept for compatibility * @see cond_reglement_id; */ public $cond_reglement; diff --git a/htdocs/opensurvey/class/opensurveysondage.class.php b/htdocs/opensurvey/class/opensurveysondage.class.php index 3b1c5beb6c1..fecb7904a9c 100644 --- a/htdocs/opensurvey/class/opensurveysondage.class.php +++ b/htdocs/opensurvey/class/opensurveysondage.class.php @@ -572,37 +572,37 @@ class Opensurveysondage extends CommonObject if ($mode == 0) { if ($status==self::STATUS_DRAFT) return $langs->trans('Draft'); - if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened').$billedtext; + if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened'); if ($status==self::STATUS_CLOSED) return $langs->trans('Closed'); } elseif ($mode == 1) { if ($status==self::STATUS_DRAFT) return $langs->trans('Draft'); - if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened').$billedtext; + if ($status==self::STATUS_VALIDATED) return $langs->trans('Opened'); if ($status==self::STATUS_CLOSED) return $langs->trans('Closed'); } elseif ($mode == 2) { if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); - if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened'),'statut1').' '.$langs->trans('Opened').$billedtext; + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened'),'statut1').' '.$langs->trans('Opened'); if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6').' '.$langs->trans('Closed'); } elseif ($mode == 3) { if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0'); - if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened').$billedtext,'statut1'); + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened'),'statut1'); if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6'); } elseif ($mode == 4) { if ($status==self::STATUS_DRAFT) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); - if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened').$billedtext,'statut1').' '.$langs->trans('Opened').$billedtext; + if ($status==self::STATUS_VALIDATED) return img_picto($langs->trans('Opened').$billedtext,'statut1').' '.$langs->trans('Opened'); if ($status==self::STATUS_CLOSED) return img_picto($langs->trans('Closed'),'statut6').' '.$langs->trans('Closed'); } elseif ($mode == 5) { if ($status==self::STATUS_DRAFT) return ''.$langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut0'); - if ($status==self::STATUS_VALIDATED) return ''.$langs->trans('Opened').$billedtext.' '.img_picto($langs->trans('Opened').$billedtext,'statut1'); + if ($status==self::STATUS_VALIDATED) return ''.$langs->trans('Opened').' '.img_picto($langs->trans('Opened'),'statut1'); if ($status==self::STATUS_CLOSED) return ''.$langs->trans('Closed').' '.img_picto($langs->trans('Closed'),'statut6'); } } diff --git a/htdocs/societe/canvas/actions_card_common.class.php b/htdocs/societe/canvas/actions_card_common.class.php index 74a05cba39d..95d5b33e162 100644 --- a/htdocs/societe/canvas/actions_card_common.class.php +++ b/htdocs/societe/canvas/actions_card_common.class.php @@ -23,8 +23,7 @@ */ /** - * \class ActionsCardCommon - * \brief Classe permettant la gestion des tiers par defaut + * Classe permettant la gestion des tiers par defaut */ abstract class ActionsCardCommon { From 7c3c8f70beb343efd0bf215c01758c40a9ff644c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 12:32:18 +0200 Subject: [PATCH 635/834] Fix not initialized variables --- htdocs/admin/tools/eaccelerator.php | 10 +- htdocs/admin/tools/export.php | 2 +- htdocs/admin/websites.php | 228 +------------------------- htdocs/api/class/api.class.php | 2 + htdocs/api/class/api_access.class.php | 7 +- 5 files changed, 17 insertions(+), 232 deletions(-) diff --git a/htdocs/admin/tools/eaccelerator.php b/htdocs/admin/tools/eaccelerator.php index 588170fbb90..5a142f3e51e 100644 --- a/htdocs/admin/tools/eaccelerator.php +++ b/htdocs/admin/tools/eaccelerator.php @@ -119,19 +119,19 @@ function create_script_table($list) global $sortby,$bc,$langs; $var=true; - if (isset($_GET['order']) && ($_GET['order'] == "asc" || $_GET['order'] =="desc")) { - $order = $_GET['order']; + if (GETPOT('order') == "asc" || GETPOST('order') =="desc") { + $order = GETPOST('order'); } else { $order = "asc"; } - if (isset($_GET['sort'])) { - switch ($_GET['sort']) { + if (GETPOST('order')) { + switch (GETPOST('order')) { case "mtime": case "size": case "reloads": case "hits": - $sortby = $_GET['sort']; + $sortby = GETPOST('sort'); ($order == "asc" ? uasort($list, 'compare') : uasort($list, 'revcompare')); break; default: diff --git a/htdocs/admin/tools/export.php b/htdocs/admin/tools/export.php index b827fe88737..175aaa65665 100644 --- a/htdocs/admin/tools/export.php +++ b/htdocs/admin/tools/export.php @@ -318,7 +318,7 @@ function backup_tables($outputfile, $tables='*') $columns = count($row); for($j=0; $j<$columns; $j++) { // Processing each columns of the row to ensure that we correctly save the value (eg: add quotes for string - in fact we add quotes for everything, it's easier) - if ($row[$j] == null and !is_string($row[$j])) { + if ($row[$j] == null && !is_string($row[$j])) { // IMPORTANT: if the field is NULL we set it NULL $row[$j] = 'NULL'; } elseif(is_string($row[$j]) && $row[$j] == '') { diff --git a/htdocs/admin/websites.php b/htdocs/admin/websites.php index 3db5dd93e49..ecc6b6a0166 100644 --- a/htdocs/admin/websites.php +++ b/htdocs/admin/websites.php @@ -417,14 +417,7 @@ if ($id) if (empty($reshook)) { - if ($tabname[$id] == MAIN_DB_PREFIX.'c_email_templates' && $action == 'edit') - { - fieldList($fieldlist,$obj,$tabname[$id],'hide'); - } - else - { - fieldList($fieldlist,$obj,$tabname[$id],'add'); - } + fieldListWebsites($fieldlist,$obj,$tabname[$id],'add'); } if ($id == 4) print '
    '; @@ -487,46 +480,9 @@ if ($id) */ $valuetoshow=ucfirst($fieldlist[$field]); // Par defaut $valuetoshow=$langs->trans($valuetoshow); // try to translate - if ($fieldlist[$field]=='source') { $valuetoshow=$langs->trans("Contact"); } - if ($fieldlist[$field]=='price') { $valuetoshow=$langs->trans("PriceUHT"); } - if ($fieldlist[$field]=='taux') { - if ($tabname[$id] != MAIN_DB_PREFIX."c_revenuestamp") $valuetoshow=$langs->trans("Rate"); - else $valuetoshow=$langs->trans("Amount"); - $align='right'; - } - if ($fieldlist[$field]=='localtax1_type') { $valuetoshow=$langs->trans("UseLocalTax")." 2"; $align="center"; $sortable=0; } - if ($fieldlist[$field]=='localtax1') { $valuetoshow=$langs->trans("Rate")." 2"; $sortable=0; } - if ($fieldlist[$field]=='localtax2_type') { $valuetoshow=$langs->trans("UseLocalTax")." 3"; $align="center"; $sortable=0; } - if ($fieldlist[$field]=='localtax2') { $valuetoshow=$langs->trans("Rate")." 3"; $sortable=0; } - if ($fieldlist[$field]=='organization') { $valuetoshow=$langs->trans("Organization"); } if ($fieldlist[$field]=='lang') { $valuetoshow=$langs->trans("Language"); } if ($fieldlist[$field]=='type') { $valuetoshow=$langs->trans("Type"); } if ($fieldlist[$field]=='code') { $valuetoshow=$langs->trans("Code"); } - if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') - { - $valuetoshow=$langs->trans("Label"); - if ($id != 25) $valuetoshow.="*"; - } - if ($fieldlist[$field]=='libelle_facture') { $valuetoshow=$langs->trans("LabelOnDocuments")."*"; } - if ($fieldlist[$field]=='country') { $valuetoshow=$langs->trans("Country"); } - if ($fieldlist[$field]=='recuperableonly') { $valuetoshow=$langs->trans("NPR"); $align="center"; } - if ($fieldlist[$field]=='nbjour') { $valuetoshow=$langs->trans("NbOfDays"); } - if ($fieldlist[$field]=='fdm') { $valuetoshow=$langs->trans("AtEndOfMonth"); } - if ($fieldlist[$field]=='decalage') { $valuetoshow=$langs->trans("Offset"); } - if ($fieldlist[$field]=='width') { $valuetoshow=$langs->trans("Width"); } - if ($fieldlist[$field]=='height') { $valuetoshow=$langs->trans("Height"); } - if ($fieldlist[$field]=='unit') { $valuetoshow=$langs->trans("MeasuringUnit"); } - if ($fieldlist[$field]=='region_id' || $fieldlist[$field]=='country_id') { $showfield=0; } - if ($fieldlist[$field]=='accountancy_code'){ $valuetoshow=$langs->trans("AccountancyCode"); } - if ($fieldlist[$field]=='accountancy_code_sell'){ $valuetoshow=$langs->trans("AccountancyCodeSell"); $sortable=0; } - if ($fieldlist[$field]=='accountancy_code_buy'){ $valuetoshow=$langs->trans("AccountancyCodeBuy"); $sortable=0; } - if ($fieldlist[$field]=='fk_pcg_version') { $valuetoshow=$langs->trans("Pcg_version"); } - if ($fieldlist[$field]=='account_parent') { $valuetoshow=$langs->trans("Accountsparent"); } - if ($fieldlist[$field]=='pcg_type') { $valuetoshow=$langs->trans("Pcg_type"); } - if ($fieldlist[$field]=='pcg_subtype') { $valuetoshow=$langs->trans("Pcg_subtype"); } - if ($fieldlist[$field]=='sortorder') { $valuetoshow=$langs->trans("SortOrder"); } - if ($fieldlist[$field]=='short_label') { $valuetoshow=$langs->trans("ShortLabel"); } - if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } // Affiche nom du champ if ($showfield) @@ -534,8 +490,6 @@ if ($id) print getTitleFieldOfList($valuetoshow,0,$_SERVER["PHP_SELF"],($sortable?$fieldlist[$field]:''),($page?'page='.$page.'&':'').'&id='.$id,"","align=".$align,$sortfield,$sortorder); } } - // Favorite - Only activated on country dictionary - if ($id == 4) print getTitleFieldOfList($langs->trans("Favorite"),0,$_SERVER["PHP_SELF"],"favorite",($page?'page='.$page.'&':'').'&id='.$id,"",'align="center"',$sortfield,$sortorder); print getTitleFieldOfList($langs->trans("Status"),0,$_SERVER["PHP_SELF"],"status",($page?'page='.$page.'&':'').'&id='.$id,"",'align="center"',$sortfield,$sortorder); print getTitleFieldOfList(''); @@ -562,7 +516,7 @@ if ($id) $reshook=$hookmanager->executeHooks('editDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error=$hookmanager->error; $errors=$hookmanager->errors; - if (empty($reshook)) fieldList($fieldlist,$obj,$tabname[$id],'edit'); + if (empty($reshook)) fieldListWebsites($fieldlist,$obj,$tabname[$id],'edit'); print ''; @@ -582,172 +536,6 @@ if ($id) $showfield=1; $align="left"; $valuetoshow=$obj->$fieldlist[$field]; - if ($value == 'type_template') - { - $valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow; - } - if ($value == 'element') - { - $valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow; - } - else if ($value == 'source') - { - $valuetoshow = isset($sourceList[$valuetoshow])?$sourceList[$valuetoshow]:$valuetoshow; - } - else if ($valuetoshow=='all') { - $valuetoshow=$langs->trans('All'); - } - else if ($fieldlist[$field]=='country') { - if (empty($obj->country_code)) - { - $valuetoshow='-'; - } - else - { - $key=$langs->trans("Country".strtoupper($obj->country_code)); - $valuetoshow=($key != "Country".strtoupper($obj->country_code)?$obj->country_code." - ".$key:$obj->country); - } - } - else if ($fieldlist[$field]=='recuperableonly' || $fieldlist[$field]=='fdm' || $fieldlist[$field] == 'deductible') { - $valuetoshow=yn($valuetoshow); - $align="center"; - } - else if ($fieldlist[$field]=='price' || preg_match('/^amount/i',$fieldlist[$field])) { - $valuetoshow=price($valuetoshow); - } - else if ($fieldlist[$field]=='libelle_facture') { - $langs->load("bills"); - $key=$langs->trans("PaymentCondition".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "PaymentCondition".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - $valuetoshow=nl2br($valuetoshow); - } - else if ($fieldlist[$field]=='label' && $tabname[$id]==MAIN_DB_PREFIX.'c_country') { - $key=$langs->trans("Country".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "Country".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='label' && $tabname[$id]==MAIN_DB_PREFIX.'c_availability') { - $langs->load("propal"); - $key=$langs->trans("AvailabilityType".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "AvailabilityType".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_actioncomm') { - $key=$langs->trans("Action".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "Action".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if (! empty($obj->code_iso) && $fieldlist[$field]=='label' && $tabname[$id]==MAIN_DB_PREFIX.'c_currencies') { - $key=$langs->trans("Currency".strtoupper($obj->code_iso)); - $valuetoshow=($obj->code_iso && $key != "Currency".strtoupper($obj->code_iso)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_typent') { - $key=$langs->trans(strtoupper($obj->code)); - $valuetoshow=($key != strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_prospectlevel') { - $key=$langs->trans(strtoupper($obj->code)); - $valuetoshow=($key != strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='label' && $tabname[$id]==MAIN_DB_PREFIX.'c_civility') { - $key=$langs->trans("Civility".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "Civility".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_type_contact') { - $langs->load('agenda'); - $key=$langs->trans("TypeContact_".$obj->element."_".$obj->source."_".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "TypeContact_".$obj->element."_".$obj->source."_".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_payment_term') { - $langs->load("bills"); - $key=$langs->trans("PaymentConditionShort".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "PaymentConditionShort".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_paiement') { - $langs->load("bills"); - $key=$langs->trans("PaymentType".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "PaymentType".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='label' && $tabname[$id]==MAIN_DB_PREFIX.'c_input_reason') { - $key=$langs->trans("DemandReasonType".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "DemandReasonType".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_input_method') { - $langs->load("orders"); - $key=$langs->trans($obj->code); - $valuetoshow=($obj->code && $key != $obj->code)?$key:$obj->$fieldlist[$field]; - } - else if ($fieldlist[$field]=='libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_shipment_mode') { - $langs->load("sendings"); - $key=$langs->trans("SendingMethod".strtoupper($obj->code)); - $valuetoshow=($obj->code && $key != "SendingMethod".strtoupper($obj->code)?$key:$obj->$fieldlist[$field]); - } - else if ($fieldlist[$field] == 'libelle' && $tabname[$id]==MAIN_DB_PREFIX.'c_paper_format') - { - $key = $langs->trans('PaperFormat'.strtoupper($obj->code)); - $valuetoshow = ($obj->code && $key != 'PaperFormat'.strtoupper($obj->code) ? $key : $obj->$fieldlist[$field]); - } - else if ($fieldlist[$field] == 'label' && $tabname[$id] == MAIN_DB_PREFIX.'c_type_fees') - { - $langs->load('trips'); - $key = $langs->trans(strtoupper($obj->code)); - $valuetoshow = ($obj->code && $key != strtoupper($obj->code) ? $key : $obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='region_id' || $fieldlist[$field]=='country_id') { - $showfield=0; - } - else if ($fieldlist[$field]=='unicode') { - $valuetoshow = $langs->getCurrencySymbol($obj->code,1); - } - else if ($fieldlist[$field]=='label' && $tabname[$_GET["id"]]==MAIN_DB_PREFIX.'c_units') { - $langs->load("products"); - $valuetoshow=$langs->trans($obj->$fieldlist[$field]); - } - else if ($fieldlist[$field]=='short_label' && $tabname[$_GET["id"]]==MAIN_DB_PREFIX.'c_units') { - $langs->load("products"); - $valuetoshow = $langs->trans($obj->$fieldlist[$field]); - } - else if (($fieldlist[$field] == 'unit') && ($tabname[$id] == MAIN_DB_PREFIX.'c_paper_format')) - { - $key = $langs->trans('SizeUnit'.strtolower($obj->unit)); - $valuetoshow = ($obj->code && $key != 'SizeUnit'.strtolower($obj->unit) ? $key : $obj->$fieldlist[$field]); - } - - else if ($fieldlist[$field]=='localtax1_type') { - if ($obj->localtax1 != 0) - $valuetoshow=$localtax_typeList[$valuetoshow]; - else - $valuetoshow = ''; - $align="center"; - } - else if ($fieldlist[$field]=='localtax2_type') { - if ($obj->localtax2 != 0) - $valuetoshow=$localtax_typeList[$valuetoshow]; - else - $valuetoshow = ''; - $align="center"; - } - else if ($fieldlist[$field]=='localtax1') { - $valuetoshow = price($valuetoshow, 0, $langs, 0, 0); - if ($obj->localtax1 == 0) - $valuetoshow = ''; - $align="right"; - } - else if ($fieldlist[$field]=='localtax2') { - $valuetoshow = price($valuetoshow, 0, $langs, 0, 0); - if ($obj->localtax2 == 0) - $valuetoshow = ''; - $align="right"; - } - else if (in_array($fieldlist[$field],array('taux','localtax1','localtax2'))) - { - $valuetoshow = price($valuetoshow, 0, $langs, 0, 0); - $align="right"; - } - else if (in_array($fieldlist[$field],array('recuperableonly'))) - { - $align="center"; - } - else if ($fieldlist[$field]=='accountancy_code' || $fieldlist[$field]=='accountancy_code_sell' || $fieldlist[$field]=='accountancy_code_buy') { - $valuetoshow = length_accountg($valuetoshow); - } // Show value for field if ($showfield) print ''; @@ -757,16 +545,6 @@ if ($id) // Can an entry be erased or disabled ? $iserasable=1;$isdisable=1; // true by default - if (isset($obj->code) && $id != 10) - { - if (($obj->code == '0' || $obj->code == '' || preg_match('/unknown/i',$obj->code))) { $iserasable = 0; $isdisable = 0; } - else if ($obj->code == 'RECEP') { $iserasable = 0; $isdisable = 0; } - else if ($obj->code == 'EF0') { $iserasable = 0; $isdisable = 0; } - } - - if (isset($obj->type) && in_array($obj->type, array('system', 'systemauto'))) { $iserasable=0; } - if (in_array($obj->code, array('AC_OTH','AC_OTH_AUTO')) || in_array($obj->type, array('systemauto'))) { $isdisable=0; $isdisable = 0; } - $url = $_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.(! empty($obj->rowid)?$obj->rowid:(! empty($obj->code)?$obj->code:'')).'&code='.(! empty($obj->code)?urlencode($obj->code):'').'&id='.$id.'&'; // Favorite @@ -823,7 +601,7 @@ $db->close(); * @param string $context 'add'=Output field for the "add form", 'edit'=Output field for the "edit form", 'hide'=Output field for the "add form" but we dont want it to be rendered * @return void */ -function fieldList($fieldlist, $obj='', $tabname='', $context='') +function fieldListWebsites($fieldlist, $obj='', $tabname='', $context='') { global $conf,$langs,$db; global $form; diff --git a/htdocs/api/class/api.class.php b/htdocs/api/class/api.class.php index a5020d4e112..bc5b4378c0c 100644 --- a/htdocs/api/class/api.class.php +++ b/htdocs/api/class/api.class.php @@ -167,6 +167,8 @@ class DolibarrApiInit extends DolibarrApi */ public function login($login, $password, $entity = 0) { + global $conf, $dolibarr_main_authentication, $dolibarr_auto_user; + // Authentication mode if (empty($dolibarr_main_authentication)) $dolibarr_main_authentication = 'http,dolibarr'; diff --git a/htdocs/api/class/api_access.class.php b/htdocs/api/class/api_access.class.php index 67f8c710bb7..bebf9a6667b 100644 --- a/htdocs/api/class/api_access.class.php +++ b/htdocs/api/class/api_access.class.php @@ -70,6 +70,7 @@ class DolibarrApiAccess implements iAuthenticate { global $db; + $login = ''; $stored_key = ''; $userClass = Defaults::$userIdentifierClass; @@ -95,11 +96,15 @@ class DolibarrApiAccess implements iAuthenticate throw new RestException(503, 'Error when fetching user api_key :'.$db->error_msg); } - if ( $stored_key != $_GET['api_key']) { + if ($stored_key != $_GET['api_key']) { $userClass::setCacheIdentifier($_GET['api_key']); return false; } + if (! $login) + { + throw new RestException(503, 'Error when searching logn user fro mapi key'); + } $fuser = new User($db); if(! $fuser->fetch('',$login)) { throw new RestException(503, 'Error when fetching user :'.$fuser->error); From 698b93cf1e754fe5419214ec2161f97ecbfa75e5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 12:38:30 +0200 Subject: [PATCH 636/834] FIX #5179 --- htdocs/core/class/commonobject.class.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 5445733852e..3ad080d8a55 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -4251,7 +4251,7 @@ abstract class CommonObject if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; // For avoid conflicts if trigger used - if (! empty($this->array_options) && !empty($this->array_options["options_$key"])) + if (! empty($this->array_options) && isset($this->array_options["options_".$key])) { // Check parameters $langs->load('admin'); @@ -4259,7 +4259,7 @@ abstract class CommonObject $extrafields = new ExtraFields($this->db); $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element); - $value=$this->array_options["options_$key"]; + $value=$this->array_options["options_".$key]; $attributeType = $extrafields->attribute_type[$key]; $attributeLabel = $extrafields->attribute_label[$key]; $attributeParam = $extrafields->attribute_param[$key]; @@ -4273,17 +4273,17 @@ abstract class CommonObject } elseif ($value=='') { - $this->array_options["options_$key"] = null; + $this->array_options["options_".$key] = null; } break; case 'price': - $this->array_options["options_$key"] = price2num($this->array_options["options_$key"]); + $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]); break; case 'date': - $this->array_options["options_$key"]=$this->db->idate($this->array_options["options_$key"]); + $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]); break; case 'datetime': - $this->array_options["options_$key"]=$this->db->idate($this->array_options["options_$key"]); + $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]); break; case 'link': $param_list=array_keys($attributeParam ['options']); @@ -4295,13 +4295,13 @@ abstract class CommonObject if ($value) { $object->fetch(0,$value); - $this->array_options["options_$key"]=$object->id; + $this->array_options["options_".$key]=$object->id; } break; } $this->db->begin(); - $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET $key='".$this->db->escape($this->array_options["options_$key"])."'"; + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'"; $sql .= " WHERE fk_object = ".$this->id; $resql = $this->db->query($sql); if (! $resql) From 6e2443069a2e920527f6cc3905bb93d0f5ab20b0 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 8 May 2016 12:48:15 +0200 Subject: [PATCH 637/834] Exclude "includes" dir for scrutinizer --- .scrutinizer.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 0ca95ace51d..831aa15c68a 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -9,6 +9,7 @@ filter: - dev/* - doc/* - test/* + - htdocs/includes/* paths: - htdocs/* - scripts/* From a7d13a81f092a1b19a66e1b9263c3bb77c470912 Mon Sep 17 00:00:00 2001 From: philippe grand Date: Sun, 8 May 2016 15:43:36 +0200 Subject: [PATCH 638/834] Revert "add column numbering name and fix a lot of issues within members list" changes on the same file by eldy This reverts commit d5a18ab584645aecc638fe2489a70d1bb32e0883. --- htdocs/adherents/list.php | 180 ++++++++-------------------------- htdocs/langs/en_US/other.lang | 1 - 2 files changed, 42 insertions(+), 139 deletions(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 43aeee8ebdf..1cc09394023 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -3,7 +3,6 @@ * Copyright (C) 2002-2003 Jean-Louis Bergamo * Copyright (C) 2004-2014 Laurent Destailleur * Copyright (C) 2013-2015 Raphaël Doursenaud - * Copyright (C) 2016 Philippe Grand * * 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 @@ -321,10 +320,10 @@ if ($resql) print ''; if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) { - print ''; + print ''; } if (! empty($arrayfields['d.ref']['checked'])) print_liste_field_titre($arrayfields['d.ref']['label'],$_SERVER["PHP_SELF"],'d.rowid','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['d.firstname']['checked']) && empty($arrayfields['d.lastname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.firstname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.lastname']['checked'])) print_liste_field_titre($arrayfields['d.lastname']['label'],$_SERVER["PHP_SELF"],'d.lastname','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.company']['checked'])) print_liste_field_titre($arrayfields['d.company']['label'],$_SERVER["PHP_SELF"],'d.company','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.login']['checked'])) print_liste_field_titre($arrayfields['d.login']['label'],$_SERVER["PHP_SELF"],'d.login','',$param,'',$sortfield,$sortorder); @@ -360,89 +359,39 @@ if ($resql) // Line for filters fields print ''; - if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) - { - print ''; - } - // Ref - if (! empty($arrayfields['d.ref']['checked'])) + if (! empty($arrayfields['c.ref']['checked'])) { print ''; } - - if (! empty($arrayfields['d.firstname']['checked']) || ! empty($arrayfields['d.lastname']['checked']) || ! empty($arrayfields['d.company']['checked'])) - { - print ''; - } - if (! empty($arrayfields['d.login']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.morphy']['checked'])) - { - print ''; - } + print ''; - if (! empty($arrayfields['t.libelle']['checked'])) - { - print ''; - } + print ''; - if (! empty($arrayfields['d.address']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.zip']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.town']['checked'])) - { - print ''; - } + print ''; - if (! empty($arrayfields['d.email']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.datefin']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.datec']['checked'])) - { - print ''; - } - - if (! empty($arrayfields['d.tms']['checked'])) - { - print ''; - } + print ''; + + print ''; + + print ''; $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Status - if (! empty($arrayfields['d.statut']['checked'])) - { - print ''; - } + print ''; // Action column print '\n"; - } + print "\n"; // Lastname - if (! empty($arrayfields['d.firstname']['checked']) || ! empty($arrayfields['d.lastname']['checked']) || ! empty($arrayfields['d.company']['checked'])) - { - print "\n"; - } + print "\n"; // Login - if (! empty($arrayfields['d.login']['checked'])) - { - print "\n"; - } - - // Moral/Physique - if (! empty($arrayfields['d.morphy']['checked'])) - { - print "\n"; - } + print "\n"; // Type - if (! empty($arrayfields['t.libelle']['checked'])) - { - $membertypestatic->id=$objp->type_id; - $membertypestatic->libelle=$objp->type; - print ''; - } - - // Address - if (! empty($arrayfields['d.address']['checked'])) - { - print "\n"; - } - - // Zip - if (! empty($arrayfields['d.zip']['checked'])) - { - print "\n"; - } - - // Town - if (! empty($arrayfields['d.town']['checked'])) - { - print "\n"; - } + $membertypestatic->id=$objp->type_id; + $membertypestatic->libelle=$objp->type; + print ''; + + // Moral/Physique + print "\n"; // EMail print "\n"; @@ -546,10 +462,15 @@ if ($resql) $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; + // Statut + print '"; + // End of subscription date if ($datefin) { - print ''; } - - // Date create - if (! empty($arrayfields['d.datec']['checked'])) - { - print "\n"; - } - - // Date modif - if (! empty($arrayfields['d.tms']['checked'])) - { - print "\n"; - } - - // Statut - print '"; // Actions print ''; if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) { - print ''; + print ''; } if (! empty($arrayfields['d.ref']['checked'])) print_liste_field_titre($arrayfields['d.ref']['label'],$_SERVER["PHP_SELF"],'d.rowid','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.firstname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder); @@ -388,6 +388,12 @@ if ($resql) // Line for filters fields print ''; + // Line numbering + if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) + { + print ''; + } + // Ref if (! empty($arrayfields['d.ref']['checked'])) { From 07a10743f8a068b2d8b4fc91d0244b99e1b0225c Mon Sep 17 00:00:00 2001 From: philippe grand Date: Sun, 8 May 2016 16:20:32 +0200 Subject: [PATCH 641/834] Fix : inverted columns --- htdocs/adherents/list.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 4689b147cab..f9e25dd5bc0 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -625,14 +625,6 @@ if ($resql) print $obj->address; print ''; } - // Town - if (! empty($arrayfields['d.town']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } // Zip if (! empty($arrayfields['d.zip']['checked'])) { @@ -641,6 +633,14 @@ if ($resql) print ''; if (! $i) $totalarray['nbfield']++; } + // Town + if (! empty($arrayfields['d.town']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } // State if (! empty($arrayfields['state.nom']['checked'])) { From 1835caad7b57ba3012176595a7449715d54b9998 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 9 May 2016 08:53:09 +0200 Subject: [PATCH 642/834] Better translation --- htdocs/adherents/card.php | 21 ++++++++++++--------- htdocs/adherents/card_subscriptions.php | 12 ++++++++++-- htdocs/langs/en_US/members.lang | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index f17ca46299f..b3bfce7d9a1 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -1381,17 +1381,12 @@ else print $form->formconfirm("card.php?rowid=".$rowid,$langs->trans("DeleteMember"),$langs->trans("ConfirmDeleteMember"),"confirm_delete",$formquestion,0,1); } - /* - * Confirm add in spip - */ + // Confirm add in spip if ($action == 'add_spip') { print $form->formconfirm("card.php?rowid=".$rowid, $langs->trans('AddIntoSpip'), $langs->trans('AddIntoSpipConfirmation'), 'confirm_add_spip'); } - - /* - * Confirm removed from spip - */ + // Confirm removed from spip if ($action == 'del_spip') { print $form->formconfirm("card.php?rowid=$rowid", $langs->trans('DeleteIntoSpip'), $langs->trans('DeleteIntoSpipConfirmation'), 'confirm_del_spip'); @@ -1556,8 +1551,16 @@ else } else { - print $langs->trans("SubscriptionNotReceived"); - if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + if (! $adht->cotisation) + { + print $langs->trans("SubscriptionNotRecorded"); + if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + } + else + { + print $langs->trans("SubscriptionNotReceived"); + if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + } } print ''; diff --git a/htdocs/adherents/card_subscriptions.php b/htdocs/adherents/card_subscriptions.php index 998dc53d525..784f6d4dea7 100644 --- a/htdocs/adherents/card_subscriptions.php +++ b/htdocs/adherents/card_subscriptions.php @@ -722,8 +722,16 @@ if ($rowid > 0) } else { - print $langs->trans("SubscriptionNotReceived"); - if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + if (! $adht->cotisation) + { + print $langs->trans("SubscriptionNotRecorded"); + if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + } + else + { + print $langs->trans("SubscriptionNotReceived"); + if ($object->statut > 0) print " ".img_warning($langs->trans("Late")); // Affiche picto retard uniquement si non brouillon et non resilie + } } print ''; diff --git a/htdocs/langs/en_US/members.lang b/htdocs/langs/en_US/members.lang index 1844a22c227..500f25e4ae8 100644 --- a/htdocs/langs/en_US/members.lang +++ b/htdocs/langs/en_US/members.lang @@ -124,6 +124,7 @@ Int=Int DateAndTime=Date and time PublicMemberCard=Member public card MemberNotOrNoMoreExpectedToSubscribe=Member not or no more expected to subscribe +SubscriptionNotRecorded=Subscription not recorded AddSubscription=Create subscription ShowSubscription=Show subscription MemberModifiedInDolibarr=Member modified in Dolibarr From 9d3ad36b7869da48ec4c6c1aead84ee2ca57bb76 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 9 May 2016 11:06:18 +0200 Subject: [PATCH 643/834] NEW If error is reported during migration process, you can ignore it to avoid to be locked. --- htdocs/install/inc.php | 10 ++++++++-- htdocs/install/upgrade.php | 2 +- htdocs/langs/en_US/install.lang | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/htdocs/install/inc.php b/htdocs/install/inc.php index 1fa6189df67..a0d873d4f5c 100644 --- a/htdocs/install/inc.php +++ b/htdocs/install/inc.php @@ -420,7 +420,7 @@ function pHeader($subtitle,$next,$action='set',$param='',$forcejqueryurl='') /** * Print HTML footer of install pages * - * @param integer $nonext 1=No button "Next step", 2=Show button but disabled + * @param integer $nonext 1=No button "Next step", 2=Show button but disabled with a link to enable * @param string $setuplang Language code * @param string $jscheckfunction Add a javascript check function * @param integer $withpleasewait Add also please wait tags @@ -439,7 +439,13 @@ function pFooter($nonext=0,$setuplang='',$jscheckfunction='', $withpleasewait=0) if (! $nonext || ($nonext == '2')) { - print '
    "'; + print '
    '; + if ($nonext == '2') + { + print $langs->trans("ErrorFoundDuringMigration", $_SERVER["REQUEST_URI"].'&ignoreerrors=1').'

    '; + } + + print '"'; if ($jscheckfunction) print ' onClick="return '.$jscheckfunction.'();"'; print '>
    '; if ($withpleasewait) print ''; diff --git a/htdocs/install/upgrade.php b/htdocs/install/upgrade.php index 4d42e225429..2a07c55d051 100644 --- a/htdocs/install/upgrade.php +++ b/htdocs/install/upgrade.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2010 Laurent Destailleur + * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin * Copyright (C) 2015 Raphaël Doursenaud * diff --git a/htdocs/langs/en_US/install.lang b/htdocs/langs/en_US/install.lang index ed692c07424..ca2e94686fb 100644 --- a/htdocs/langs/en_US/install.lang +++ b/htdocs/langs/en_US/install.lang @@ -205,3 +205,4 @@ MigrationEvents=Migration of events to add event owner into assignement table MigrationReloadModule=Reload module %s ShowNotAvailableOptions=Show not available options HideNotAvailableOptions=Hide not available options +ErrorFoundDuringMigration=Error were reported during migration process so next step is not available. To ignore errors, you can click here, but application or some features may not work correctly until fixed. \ No newline at end of file From 1032583b02b70c7908e874d98046aa7858b264f7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 9 May 2016 14:59:59 +0200 Subject: [PATCH 644/834] Better support for accessiblity --- htdocs/core/menus/standard/eldy.lib.php | 17 +++++++++++++-- htdocs/core/menus/standard/eldy_menu.php | 2 +- htdocs/main.inc.php | 27 ++++++++++++++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 67708eba5fe..2fc166e7625 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -35,7 +35,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php'; * @param int $type_user 0=Menu for backoffice, 1=Menu for front office * @param array $tabMenu If array with menu entries already loaded, we put this array here (in most cases, it's empty) * @param array $menu Object Menu to return back list of menu entries - * @param int $noout Disable output (Initialise &$menu only). + * @param int $noout 1=Disable output (Initialise &$menu only). * @return int 0 */ function print_eldy_menu($db,$atarget,$type_user,&$tabMenu,&$menu,$noout=0) @@ -50,6 +50,19 @@ function print_eldy_menu($db,$atarget,$type_user,&$tabMenu,&$menu,$noout=0) if (empty($noout)) print_start_menu_array(); + // Show/Hide vertical menu + if (GETPOST('testhidemenu') && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $showmode=1; + $classname = 'class="tmenu"'; + $idsel='home'; + + if (empty($noout)) print_start_menu_entry($idsel,$classname,$showmode); + if (empty($noout)) print_text_menu_entry($langs->trans("XXX"), 1, '#', $id, $idsel, $classname, $atarget); + if (empty($noout)) print_end_menu_entry($showmode); + $menu->add('#', $langs->trans("XXX"), 0, $showmode, $atarget, "xxx", ''); + } + // Home $showmode=1; $classname=""; @@ -463,7 +476,7 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu print '
    '."\n"; } - if (is_array($moredata) && ! empty($moredata['searchform'])) + if (is_array($moredata) && ! empty($moredata['searchform']) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { print "\n"; print "\n"; diff --git a/htdocs/core/menus/standard/eldy_menu.php b/htdocs/core/menus/standard/eldy_menu.php index 354f7de1453..eb5e75dfef8 100644 --- a/htdocs/core/menus/standard/eldy_menu.php +++ b/htdocs/core/menus/standard/eldy_menu.php @@ -144,7 +144,7 @@ class MenuManager if ($mode == 'topnb') { - print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1); + print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1); // no output return $this->menu->getNbOfVisibleMenuEntries(); } diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index b17ce86ad17..ffdb4288928 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -563,7 +563,11 @@ if (! defined('NOLOGIN')) $reshook=$hookmanager->executeHooks('afterLoginFailed',$parameters,$user,$action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) $error++; - header('Location: '.DOL_URL_ROOT.'/index.php'); + $paramsurl=array(); + if (GETPOST('textbrowser')) $paramsurl[]='textbrowser='.GETPOST('textbrowser','int'); + if (GETPOST('nojs')) $paramsurl[]='nojs='.GETPOST('nojs','int'); + if (GETPOST('lang')) $paramsurl[]='lang='.GETPOST('lang','alpha'); + header('Location: '.DOL_URL_ROOT.'/index.php'.($count(paramsurl)?'?'.join('&',$paramsurl):'')); exit; } } @@ -617,7 +621,11 @@ if (! defined('NOLOGIN')) $reshook=$hookmanager->executeHooks('afterLoginFailed',$parameters,$user,$action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) $error++; - header('Location: '.DOL_URL_ROOT.'/index.php'); + $paramsurl=array(); + if (GETPOST('textbrowser')) $paramsurl[]='textbrowser='.GETPOST('textbrowser','int'); + if (GETPOST('nojs')) $paramsurl[]='nojs='.GETPOST('nojs','int'); + if (GETPOST('lang')) $paramsurl[]='lang='.GETPOST('lang','alpha'); + header('Location: '.DOL_URL_ROOT.'/index.php'.($count(paramsurl)?'?'.join('&',$paramsurl):'')); exit; } else @@ -756,6 +764,11 @@ if (! GETPOST('nojs')) // If javascript was not disabled on URL } } else $conf->use_javascript_ajax=0; +// Set MAIN_OPTIMIZEFORTEXTBROWSER +if (GETPOST('textbrowser') || ! empty($user->conf->MAIN_OPTIMIZEFORTEXTBROWSER)) // If text browser was enabled on URL +{ + $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER=1; +} // Set terminal output option according to conf->browser. if (GETPOST('dol_hide_leftmenu') || ! empty($_SESSION['dol_hide_leftmenu'])) $conf->dol_hide_leftmenu=1; @@ -1006,9 +1019,9 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs $favicon=dol_buildpath('/theme/'.$conf->theme.'/img/favicon.ico',1); if (! empty($conf->global->MAIN_FAVICON_URL)) $favicon=$conf->global->MAIN_FAVICON_URL; print ''."\n"; - if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print ''."\n"; - if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print ''."\n"; - if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print ''."\n"; + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; // Displays title $appli=constant('DOL_APPLICATION_TITLE'); @@ -1460,7 +1473,7 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a else $toprightmenu.=$result; // For backward compatibility // Link to print main content area - if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->browser->phone)) + if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && empty($conf->browser->phone)) { $qs=$_SERVER["QUERY_STRING"]; $qs.=(($qs && $morequerystring)?'&':'').$morequerystring; @@ -1471,7 +1484,7 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a } // Link to Dolibarr wiki pages - if (empty($conf->global->MAIN_HELP_DISABLELINK)) + if (empty($conf->global->MAIN_HELP_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { $langs->load("help"); From 03732fd1aa0cfe75564111883677099b0bb02fad Mon Sep 17 00:00:00 2001 From: Maxime Kohlhaas Date: Mon, 9 May 2016 15:31:44 +0200 Subject: [PATCH 645/834] Fix contract line creation was adding all proposal lines desc --- htdocs/contrat/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 3fe0530ec77..3c38cdf4421 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -320,7 +320,7 @@ if ($action == 'add' && $user->rights->contrat->creer) } if ($conf->global->PRODUIT_DESC_IN_FORM) - $desc .= ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; + $desc = ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; } else { $desc = dol_htmlentitiesbr($lines[$i]->desc); From 6278610d0aad44063c20b274bf07e0e4d936ea7f Mon Sep 17 00:00:00 2001 From: Maxime Kohlhaas Date: Mon, 9 May 2016 15:32:21 +0200 Subject: [PATCH 646/834] Fix contract line creation should take desc even if not displayed on screen --- htdocs/contrat/card.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 3c38cdf4421..c55defb5ce8 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -319,8 +319,7 @@ if ($action == 'add' && $user->rights->contrat->creer) $label = $lines[$i]->product_label; } - if ($conf->global->PRODUIT_DESC_IN_FORM) - $desc = ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; + $desc = ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; } else { $desc = dol_htmlentitiesbr($lines[$i]->desc); From a8f50283c06de3af9f4e0f0c8db5e9fb64f42e7b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 9 May 2016 19:37:14 +0200 Subject: [PATCH 647/834] Fix bad translation --- htdocs/langs/en_US/bills.lang | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 392050c6cd5..bfbe32b803e 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -321,8 +321,8 @@ PaymentConditionPT_5050=50%% in advance, 50%% on delivery FixAmount=Fix amount VarAmount=Variable amount (%% tot.) # PaymentType -PaymentTypeVIR=Bank deposit -PaymentTypeShortVIR=Bank deposit +PaymentTypeVIR=Bank transfer +PaymentTypeShortVIR=Bank transfer PaymentTypePRE=Bank's order PaymentTypeShortPRE=Bank's order PaymentTypeLIQ=Cash From 3607f1a296b14f8535248cbad5bf15b520a9cc85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Mon, 9 May 2016 22:15:00 +0200 Subject: [PATCH 648/834] Correction for Auguria Menu Accounting Expert Entry cause blank windows --- htdocs/core/menus/init_menu_auguria.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/menus/init_menu_auguria.sql b/htdocs/core/menus/init_menu_auguria.sql index 86b8a58e68f..2e37f989b0a 100644 --- a/htdocs/core/menus/init_menu_auguria.sql +++ b/htdocs/core/menus/init_menu_auguria.sql @@ -206,7 +206,7 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2303__+MAX_llx_menu__, 'accountancy', '', 2300__+MAX_llx_menu__, '/compta/tva/clients.php?leftmenu=tax_vat', 'ReportByCustomers', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2304__+MAX_llx_menu__, 'accountancy', '', 2300__+MAX_llx_menu__, '/compta/tva/quadri_detail.php?leftmenu=tax_vat', 'ReportByQuarter', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); -- Accounting Expert -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->accounting->enabled', __HANDLER__, 'left', 2400__+MAX_llx_menu__, 'accountancy', 'accounting', 6__+MAX_llx_menu__, '/accountancy/customer/index.php?leftmenu=accounting', 'MenuAccountancy', 0, 'accountancy', '(! empty($conf->accounting->enabled) || $user->rights->accounting->ventilation->read || $user->rights->accounting->ventilation->dispatch || $user->rights->compta->resultat->lire', '', 0, 7, __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->accounting->enabled', __HANDLER__, 'left', 2400__+MAX_llx_menu__, 'accountancy', 'accounting', 6__+MAX_llx_menu__, '/accountancy/customer/index.php?leftmenu=accounting', 'MenuAccountancy', 0, 'accountancy', '! empty($conf->accounting->enabled) || $user->rights->accounting->ventilation->read || $user->rights->accounting->ventilation->dispatch || $user->rights->compta->resultat->lire', '', 0, 7, __ENTITY__); -- Dispatch 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->accounting->enabled', __HANDLER__, 'left', 2401__+MAX_llx_menu__, 'accountancy', 'dispatch_customer', 2400__+MAX_llx_menu__, '/accountancy/customer/index.php?leftmenu=dispatch_customer', 'CustomersVentilation', 1, 'accountancy', '$user->rights->accounting->ventilation->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->accounting->enabled && $leftmenu=="dispatch_customer"', __HANDLER__, 'left', 2402__+MAX_llx_menu__, 'accountancy', '', 2401__+MAX_llx_menu__, '/accountancy/customer/list.php', 'ToDispatch', 2, 'accountancy', '$user->rights->accounting->ventilation->dispatch', '', 0, 2, __ENTITY__); From 84e75eed88371dcc7fdf49dbc785766bc74249ae Mon Sep 17 00:00:00 2001 From: gauthier Date: Tue, 10 May 2016 09:31:25 +0200 Subject: [PATCH 649/834] FIX : can't fetch by siret or siren because of first "if" --- htdocs/societe/class/societe.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 864820602c8..efb038a84f0 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -1006,7 +1006,7 @@ class Societe extends CommonObject global $langs; global $conf; - if (empty($rowid) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1; + if (empty($rowid) && empty($ref) && empty($ref_ext) && empty($ref_int) && empty($idprof1) && empty($idprof2) && empty($idprof3) && empty($idprof4)) return -1; $sql = 'SELECT s.rowid, s.nom as name, s.name_alias, s.entity, s.ref_ext, s.ref_int, s.address, s.datec as date_creation, s.prefix_comm'; $sql .= ', s.status'; From a57e0f5fd38f4c5a3a318ef9002f68f9172c6eec Mon Sep 17 00:00:00 2001 From: phf Date: Tue, 10 May 2016 14:27:24 +0200 Subject: [PATCH 650/834] Fix update supplier ref on supplier invoice --- htdocs/fourn/facture/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index f1de6a68a78..635c9195386 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -240,7 +240,7 @@ if (empty($reshook)) { $object->ref_supplier = GETPOST('ref_supplier', 'alpha'); - if ($object->update() < 0) { + if ($object->update($user) < 0) { setEventMessages($object->error, $object->errors, 'errors'); } } From d3b3e60d82edc38345473f0c4b478ae1c70310d1 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Tue, 10 May 2016 22:37:30 +0200 Subject: [PATCH 651/834] Fix email template edition --- htdocs/install/mysql/migration/3.7.0-3.8.0.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql index 89f75c1156d..068fbc753cb 100755 --- a/htdocs/install/mysql/migration/3.7.0-3.8.0.sql +++ b/htdocs/install/mysql/migration/3.7.0-3.8.0.sql @@ -797,7 +797,6 @@ ALTER TABLE llx_societe_remise_except MODIFY COLUMN description text NOT NULL; update llx_opensurvey_sondage set format = 'D' where format = 'D+'; update llx_opensurvey_sondage set format = 'A' where format = 'A+'; -ALTER TABLE llx_propal_merge_pdf_product ADD COLUMN lang varchar(5) DEFAULT NULL; --Deal with holidays_user that do not have rowid -- Disabled: too dangerous patch. rowid is a primary key. How is it possible to have no rowid ? From c957cfc260b7574fe3e60f19c1b4c5d082a10b55 Mon Sep 17 00:00:00 2001 From: nka11 Date: Wed, 11 May 2016 13:22:58 +0200 Subject: [PATCH 652/834] removing String class which cause server Error with php7 --- .../Luracast/Restler/Data/String.php | 84 ------------------- 1 file changed, 84 deletions(-) delete mode 100644 htdocs/includes/restler/framework/Luracast/Restler/Data/String.php diff --git a/htdocs/includes/restler/framework/Luracast/Restler/Data/String.php b/htdocs/includes/restler/framework/Luracast/Restler/Data/String.php deleted file mode 100644 index 268ca40c115..00000000000 --- a/htdocs/includes/restler/framework/Luracast/Restler/Data/String.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @copyright 2010 Luracast - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link http://luracast.com/products/restler/ - */ -class String -{ - /** - * Given haystack contains the needle or not? - * - * @param string $haystack - * @param string $needle - * @param bool $caseSensitive - * - * @return bool - */ - public static function contains($haystack, $needle, $caseSensitive = true) - { - if (empty($needle)) - return true; - return $caseSensitive - ? strpos($haystack, $needle) !== false - : stripos($haystack, $needle) !== false; - } - - /** - * Given haystack begins with the needle or not? - * - * @param string $haystack - * @param string $needle - * - * @return bool - */ - public static function beginsWith($haystack, $needle) - { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); - } - - /** - * Given haystack ends with the needle or not? - * - * @param string $haystack - * @param string $needle - * - * @return bool - */ - public static function endsWith($haystack, $needle) - { - $length = strlen($needle); - if ($length == 0) { - return true; - } - return (substr($haystack, -$length) === $needle); - } - - - /** - * Convert camelCased or underscored string in to a title - * - * @param string $name - * - * @return string - */ - public static function title($name) - { - return - ucwords( - preg_replace( - array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/', '/(_)/'), - array(' $0', ' $0', ' '), - $name - ) - ); - } -} \ No newline at end of file From 788d499bf614cb92401c605d86d353943c2ff0c3 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Wed, 11 May 2016 18:50:02 +0200 Subject: [PATCH 653/834] Change resource type to dolresource in class --- htdocs/resource/class/dolresource.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/resource/class/dolresource.class.php b/htdocs/resource/class/dolresource.class.php index b6b4eec8be2..f7adb19112e 100644 --- a/htdocs/resource/class/dolresource.class.php +++ b/htdocs/resource/class/dolresource.class.php @@ -30,7 +30,7 @@ require_once DOL_DOCUMENT_ROOT."/core/lib/functions2.lib.php"; */ class Dolresource extends CommonObject { - var $element='resource'; //!< Id that identify managed objects + var $element='dolresource'; //!< Id that identify managed objects var $table_element='resource'; //!< Name of table without prefix where object is stored var $resource_id; From 9b11907c70f3df91895b5a156fb47bcd98f4ec90 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Wed, 11 May 2016 18:50:25 +0200 Subject: [PATCH 654/834] Update existing linked elements to new type --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 20c1c1d0de3..14849e6fa9c 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -438,6 +438,7 @@ ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255); ALTER TABLE llx_element_resources ADD COLUMN duree real; -- total duration of using ressource +UPDATE llx_element_resources SET resource_type = 'dolresource' WHERE resource_type = 'resource'; CREATE TABLE llx_advtargetemailing ( From 9f4812e69ce1ed00c4d03b7dcf7f48a593522986 Mon Sep 17 00:00:00 2001 From: nka11 Date: Wed, 11 May 2016 22:25:04 +0200 Subject: [PATCH 655/834] upgrade reslter for composer --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f4ea5de0c73..18a71385b5a 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "mike42/escpos-php": "dev-master", "mobiledetect/mobiledetectlib": "2.8.17", "phpoffice/phpexcel": "1.8.1", - "restler/framework": "^3.0", + "restler/framework": "^3.0.0-RC6", "tecnickcom/tcpdf": "6.2.12" }, "require-dev": { From 018ba1e6c6fb499d19697ff2a1ffbae9a4fb1d6a Mon Sep 17 00:00:00 2001 From: nka11 Date: Wed, 11 May 2016 22:38:16 +0200 Subject: [PATCH 656/834] fix restler version for RC6 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 18a71385b5a..ab60ce4f1b2 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "mike42/escpos-php": "dev-master", "mobiledetect/mobiledetectlib": "2.8.17", "phpoffice/phpexcel": "1.8.1", - "restler/framework": "^3.0.0-RC6", + "restler/framework": "3.0.0-RC6", "tecnickcom/tcpdf": "6.2.12" }, "require-dev": { From 7ec8557db9e83a3a0ea4c55d1aa0b327133bb9e1 Mon Sep 17 00:00:00 2001 From: nka11 Date: Wed, 11 May 2016 23:02:30 +0200 Subject: [PATCH 657/834] remove composer.lock --- .gitignore | 1 + composer.lock | 420 -------------------------------------------------- 2 files changed, 1 insertion(+), 420 deletions(-) delete mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index 1efe4f4bb82..ca0334a63e9 100755 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ Thumbs.db # Vagrant generated files .vagrant # Composer installed repositories +composer.lock /htdocs/includes/**/.git # Composer autoloader and unwanted files htdocs/includes/autoload.php diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 43a9f1d2d7c..00000000000 --- a/composer.lock +++ /dev/null @@ -1,420 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "c2b53c577364dbe3a56137043081b511", - "content-hash": "8f7a86cfbc13f45e13b73c49531818cb", - "packages": [ - { - "name": "ccampbell/chromephp", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/ccampbell/chromephp.git", - "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ccampbell/chromephp/zipball/c3c297615d48ae5b2a86a82311152d1ed095fcef", - "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef", - "shasum": "" - }, - "require": { - "php": ">=5.0.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "ChromePhp": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Craig Campbell", - "email": "iamcraigcampbell@gmail.com", - "homepage": "http://craig.is", - "role": "Developer" - } - ], - "description": "Log variables to the Chrome console (via Chrome Logger Google Chrome extension).", - "homepage": "http://github.com/ccampbell/chromephp", - "keywords": [ - "log", - "logging" - ], - "time": "2013-06-26 03:44:33" - }, - { - "name": "ckeditor/ckeditor", - "version": "dev-full/stable", - "source": { - "type": "git", - "url": "https://github.com/ckeditor/ckeditor-releases.git", - "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", - "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0+", - "LGPL-2.1+", - "MPL-1.1+" - ], - "authors": [ - { - "name": "CKSource", - "homepage": "http://cksource.com" - } - ], - "description": "JavaScript WYSIWYG web text editor.", - "homepage": "http://ckeditor.com", - "keywords": [ - "CKEditor", - "editor", - "fckeditor", - "html", - "javascript", - "richtext", - "text", - "wysiwyg" - ], - "time": "2016-03-31 16:19:25" - }, - { - "name": "mike42/escpos-php", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/mike42/escpos-php.git", - "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mike42/escpos-php/zipball/96f05cbf460f5b67c2184ee4e91aedfbcedeb788", - "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "4.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Mike42\\": "src/Mike42" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roni Saha", - "email": "roni.cse@gmail.com" - }, - { - "name": "Michael Billington", - "email": "michael.billington@gmail.com" - }, - { - "name": "Gergely Radics", - "email": "gerifield@ustream.tv" - }, - { - "name": "Warren Doyle", - "email": "w.doyle@fuelled.co" - }, - { - "name": "vharo", - "email": "vharo@geepok.com" - } - ], - "description": "Thermal receipt printer library, for use with ESC/POS compatible printers", - "homepage": "https://github.com/mike42/escpos-php", - "keywords": [ - "ESC-POS", - "driver", - "escpos", - "print", - "receipt" - ], - "time": "2016-03-27 23:08:27" - }, - { - "name": "mobiledetect/mobiledetectlib", - "version": "2.8.17", - "source": { - "type": "git", - "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/b87da5f63a76e9615a0c74fcf168657b1ea7e41d", - "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d", - "shasum": "" - }, - "require": { - "php": ">=5.0.0" - }, - "require-dev": { - "codeclimate/php-test-reporter": "dev-master", - "johnkary/phpunit-speedtrap": "~1.0@dev", - "phpunit/phpunit": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "Mobile_Detect.php" - ], - "psr-0": { - "Detection": "namespaced/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Serban Ghita", - "email": "serbanghita@gmail.com", - "homepage": "http://mobiledetect.net", - "role": "Developer" - } - ], - "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", - "homepage": "https://github.com/serbanghita/Mobile-Detect", - "keywords": [ - "detect mobile devices", - "mobile", - "mobile detect", - "mobile detector", - "php mobile detect" - ], - "time": "2015-09-17 14:45:21" - }, - { - "name": "phpoffice/phpexcel", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/PHPOffice/PHPExcel.git", - "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32", - "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "ext-xmlwriter": "*", - "php": ">=5.2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "PHPExcel": "Classes/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL" - ], - "authors": [ - { - "name": "Maarten Balliauw", - "homepage": "http://blog.maartenballiauw.be" - }, - { - "name": "Mark Baker" - }, - { - "name": "Franck Lefevre", - "homepage": "http://blog.rootslabs.net" - }, - { - "name": "Erik Tilt" - } - ], - "description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", - "homepage": "http://phpexcel.codeplex.com", - "keywords": [ - "OpenXML", - "excel", - "php", - "spreadsheet", - "xls", - "xlsx" - ], - "time": "2015-05-01 07:00:55" - }, - { - "name": "restler/framework", - "version": "3.0.0", - "target-dir": "Luracast/Restler", - "source": { - "type": "git", - "url": "https://github.com/Luracast/Restler-Framework.git", - "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/6ee10b3e5dbc6376916fed55ec2340a37cce436b", - "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "replace": { - "luracast/restler": "3.*" - }, - "require-dev": { - "bshaffer/oauth2-server-php": "v1.0", - "luracast/explorer": "*", - "mustache/mustache": "dev-master", - "rodneyrehm/plist": "dev-master", - "symfony/yaml": "*", - "twig/twig": "v1.13.0", - "zendframework/zendamf": "dev-master" - }, - "suggest": { - "bshaffer/oauth2-server-php": "Restler can provide OAuth2 authentication using this library (see require-dev for details)", - "luracast/explorer": "Restler's very own api explorer (see require-dev for details)", - "mustache/mustache": "Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)", - "rodneyrehm/plist": "Restler supports tho Apple plist xml format (see require-dev for details)", - "symfony/yaml": "Restler can produce content in yaml format as well (see require-dev for details)", - "twig/twig": "Restler can render HtmlView using twig templates (see require-dev for details)", - "zendframework/zendamf": "Support for the amf document format (see require-dev for details)" - }, - "type": "library", - "extra": { - "branch-alias": { - "master": "v3.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Luracast\\Restler": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1" - ], - "authors": [ - { - "name": "Luracast", - "email": "arul@luracast.com" - }, - { - "name": "Nick nickl- Lombard", - "email": "github@jigsoft.co.za" - } - ], - "description": "Just the Restler Framework without the tests and examples", - "homepage": "http://luracast.com/products/restler/", - "keywords": [ - "api", - "framework", - "rest", - "server" - ], - "time": "2015-08-04 07:52:49" - }, - { - "name": "tecnickcom/tcpdf", - "version": "6.2.12", - "source": { - "type": "git", - "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/2f732eaa91b5665274689b1d40b285a7bacdc37f", - "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "fonts", - "config", - "include", - "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", - "tcpdf_barcodes_1d.php", - "tcpdf_barcodes_2d.php", - "include/tcpdf_colors.php", - "include/tcpdf_filters.php", - "include/tcpdf_font_data.php", - "include/tcpdf_fonts.php", - "include/tcpdf_images.php", - "include/tcpdf_static.php", - "include/barcodes/datamatrix.php", - "include/barcodes/pdf417.php", - "include/barcodes/qrcode.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPLv3" - ], - "authors": [ - { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "homepage": "http://nicolaasuni.tecnick.com" - } - ], - "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", - "homepage": "http://www.tcpdf.org/", - "keywords": [ - "PDFD32000-2008", - "TCPDF", - "barcodes", - "datamatrix", - "pdf", - "pdf417", - "qrcode" - ], - "time": "2015-09-12 10:08:34" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "ckeditor/ckeditor": 20, - "mike42/escpos-php": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.3.0", - "ext-curl": "*" - }, - "platform-dev": [] -} From 4cbab1cf47a4d3fcb99acaf7e6f7cad74bdaa0f7 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 12 May 2016 16:58:14 +0200 Subject: [PATCH 658/834] FIX PROPAL_MERGE_PDF with PRODUCT_USE_OLD_PATH --- htdocs/product/document.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/product/document.php b/htdocs/product/document.php index 6a8ee778590..766b24a941e 100644 --- a/htdocs/product/document.php +++ b/htdocs/product/document.php @@ -247,7 +247,8 @@ if ($object->id) if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) // For backward compatiblity, we scan also old dirs { - $filearray = dol_dir_list($upload_dirold, "files", 0, '', '\.meta$', 'name', SORT_ASC, 1); + + $filearray = array_merge($filearray,dol_dir_list($upload_dirold, "files", 0, '', '\.meta$', 'name', SORT_ASC, 1)); } // For each file build select list with PDF extention From 2b6029f5d348c763c6b440949fa93a6d5a75ea9d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 12 May 2016 19:44:49 +0200 Subject: [PATCH 659/834] FIX Some fix on stock. Condition to view warehouse as column and protection against infinite loop --- htdocs/product/class/product.class.php | 26 +++++++------- htdocs/product/composition/card.php | 15 +++++--- htdocs/product/list.php | 4 +-- htdocs/product/reassort.php | 47 ++++++++++++-------------- 4 files changed, 47 insertions(+), 45 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 86a11f57541..e8b32f978cb 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2897,7 +2897,7 @@ class Product extends CommonObject } /** - * reconstruit l'arborescence des categories sous la forme d'un tableau + * reconstruit l'arborescence des produits sous la forme d'un tableau * * @param int $multiply Because each sublevel must be multiplicated by parent nb * @return array $this->res @@ -2940,7 +2940,6 @@ class Product extends CommonObject * Return all Father products fo current product * * @return array Array of product - * @see getParent */ function getFather() { @@ -2985,6 +2984,8 @@ class Product extends CommonObject */ function getChildsArbo($id, $firstlevelonly=0, $level=1) { + global $alreadyfound; + $sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type, pa.incdec"; $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; $sql.= ", ".MAIN_DB_PREFIX."product_association as pa"; @@ -2994,6 +2995,7 @@ class Product extends CommonObject dol_syslog(get_class($this).'::getChildsArbo id='.$id.' level='.$level, LOG_DEBUG); + if ($level == 1) $alreadyfound=array($id=>1); // We init array of found object to start of tree, so if we found it later (should not happened), we stop immediatly // Protection against infinite loop if ($level > 30) return array(); @@ -3003,9 +3005,14 @@ class Product extends CommonObject $prods = array(); while ($rec = $this->db->fetch_array($res)) { - // TODO Add check to not add ne record if already added + if (! empty($alreadyfound[$rec['rowid']])) + { + dol_syslog(get_class($this).'::getChildsArbo the product id='.$rec['rowid'].' was already found at a higher level in tree. We discard to avoid infinite loop', LOG_WARNING); + continue; + } + $alreadyfound[$rec['rowid']]=1; $prods[$rec['rowid']]= array( - 0=>$rec['id'], + 0=>$rec['rowid'], 1=>$rec['qty'], 2=>$rec['fk_product_type'], 3=>$this->db->escape($rec['label']), @@ -3015,7 +3022,7 @@ class Product extends CommonObject //$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']); if (empty($firstlevelonly)) { - $listofchilds=$this->getChildsArbo($rec['id'], 0, $level + 1); + $listofchilds=$this->getChildsArbo($rec['rowid'], 0, $level + 1); foreach($listofchilds as $keyChild => $valueChild) { $prods[$rec['rowid']]['childs'][$keyChild] = $valueChild; @@ -3040,16 +3047,11 @@ class Product extends CommonObject */ function get_sousproduits_arbo() { - //$parent = $this->getParent(); $parent=array(); - $parent[$this->label]=array(0 => $this->id); - foreach($parent as $key => $value) // key=label, value[0]=id + foreach($this->getChildsArbo($this->id) as $keyChild => $valueChild) // Warning. getChildsArbo can call getChildsArbo recursively. Starting point is $value[0]=id of product { - foreach($this->getChildsArbo($value[0]) as $keyChild => $valueChild) // Warning. getChildsArbo can gell getChildsArbo recursively. - { - $parent[$key][$keyChild] = $valueChild; - } + $parent[$this->label][$keyChild] = $valueChild; } foreach($parent as $key => $value) // key=label, value is array of childs { diff --git a/htdocs/product/composition/card.php b/htdocs/product/composition/card.php index 25195f6c167..adfce7d6229 100644 --- a/htdocs/product/composition/card.php +++ b/htdocs/product/composition/card.php @@ -235,8 +235,9 @@ if ($id > 0 || ! empty($ref)) $prodsfather = $object->getFather(); // Parent Products - $object->get_sousproduits_arbo(); // Load $object->sousprod - $prods_arbo=$object->get_arbo_each_prod(); + $object->get_sousproduits_arbo(); // Load $object->sousprods + $prods_arbo=$object->get_arbo_each_prod(); + $nbofsubsubproducts=count($prods_arbo); // This include sub sub product into nb $prodschild = $object->getChildsArbo($id,1); $nbofsubproducts=count($prodschild); // This include only first level of childs @@ -379,10 +380,10 @@ if ($id > 0 || ! empty($ref)) print ''."\n"; } - else // By default, we do not show this. It makes screen very difficult to understand + else { $hide=''; - if (empty($conf->global->PRODUCT_SHOW_SUB_SUB_PRODUCTS)) $hide=' hideobject'; + if (empty($conf->global->PRODUCT_SHOW_SUB_SUB_PRODUCTS)) $hide=' hideobject'; // By default, we do not show this. It makes screen very difficult to understand $class=($class=='impair')?'pair':'impair'; print '
    '; @@ -394,9 +395,13 @@ if ($id > 0 || ! empty($ref)) print $productstatic->getNomUrl(1,'composition').''; print ''; + // Best buying price print ''; print ''; - + // Best selling price + print ''; + print ''; + if (! empty($conf->stock->enabled)) print ''; // Real stock print ''; print ''; diff --git a/htdocs/product/list.php b/htdocs/product/list.php index 1d63964be1c..d8adf66bcc1 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -145,7 +145,7 @@ $arrayfields=array( 'p.label'=>array('label'=>$langs->trans("Label"), 'checked'=>1), 'p.barcode'=>array('label'=>$langs->trans("Gencod"), 'checked'=>($contextpage != 'servicelist'), 'enabled'=>(! empty($conf->barcode->enabled))), 'p.duration'=>array('label'=>$langs->trans("Duration"), 'checked'=>($contextpage != 'productlist'), 'enabled'=>(! empty($conf->service->enabled))), - 'p.sellprice'=>array('label'=>$titlesellprice, 'checked'=>1, 'enabled'=>empty($conf->global->PRODUIT_MULTIPRICES)), + 'p.sellprice'=>array('label'=>$langs->trans("SellingPrice"), 'checked'=>1, 'enabled'=>empty($conf->global->PRODUIT_MULTIPRICES)), 'p.minbuyprice'=>array('label'=>$langs->trans("BuyingPriceMinShort"), 'checked'=>1, 'enabled'=>(! empty($user->rights->fournisseur->lire))), 'p.seuil_stock_alerte'=>array('label'=>$langs->trans("StockLimit"), 'checked'=>0, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service')), 'p.desiredstock'=>array('label'=>$langs->trans("DesiredStock"), 'checked'=>1, 'enabled'=>(! empty($conf->stock->enabled) && $user->rights->stock->lire && $contextpage != 'service')), @@ -425,7 +425,7 @@ else $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields - + print '
    '.$langs->trans('Ref').'
    '.$langs->trans('Ref').''; print $form->showrefnav($object, 'id', $linkback, 1, 'id_sondage', 'id_sondage'); print '
    '; print ''; print ''; - print ''; + print ''; + print ''; + print ''; + print ''; + $listetype=$membertypestatic->liste_array(); + print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); + print ''; + print ''; + print ''; + print ''; - print ''; + print ''; - print ''; + print ''; - $listetype=$membertypestatic->liste_array(); - print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); - print ' '; - print ' '; - $searchpitco=$form->showFilterAndCheckAddButtons(0); - print $searchpitco; - print '
    '; - print ''; - print ''; - print ''; - print ''; + print ''; - print ''; - print ''; - print ''; + print ''; + print ''; + print $form->select_country($search_country,'search_country','',0,'maxwidth100'); + print ''; - print ''; print ''; + if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select'))) + { + $crit=$val; + $tmpkey=preg_replace('/search_options_/','',$key); + $searchclass=''; + if (in_array($typeofextrafield, array('varchar', 'select'))) $searchclass='searchstring'; + if (in_array($typeofextrafield, array('int', 'double'))) $searchclass='searchnum'; + print ''; + } + print ''; + print ''; + print ''; + $liststatus=array( + '-1'=>$langs->trans("Draft"), + '1'=>$langs->trans("Validated"), + '0'=>$langs->trans("Resiliated") + ); + print $form->selectarray('statut', $liststatus, $statut, -2); + print ''; + $searchpitco=$form->showFilterAndCheckAddButtons(0); + print $searchpitco; + print '
    '.($i+1).'"; - print $memberstatic->getNomUrl(1); - print ""; + print $memberstatic->getNomUrl(1); + print "rowid\">"; - print ((! empty($objp->lastname) || ! empty($objp->firstname)) ? dol_trunc($memberstatic->getFullName($langs)) : ''); - print (((! empty($objp->lastname) || ! empty($objp->firstname)) && ! empty($companyname)) ? ' / ' : ''); - print (! empty($companyname) ? dol_trunc($companyname, 32) : ''); - print "".$objp->login."'; - print $membertypestatic->getNomUrl(1,32); - print '".$memberstatic->getmorphylib($objp->morphy).""; + print $obj->lastname; + print ""; + print $obj->firstname; + print ""; + print $companyname; + print "".$obj->login."".$memberstatic->getmorphylib($obj->morphy)."'; + print $membertypestatic->getNomUrl(1,32); + print ''; + print $obj->address; + print ''; + print $obj->town; + print ''; + print $obj->zip; + print '".$obj->state_name."'; + $tmparray=getCountry($obj->country,'all'); + print $tmparray['label']; + print '".dol_print_email($objp->email,0,0,1)."'; - print $memberstatic->LibStatut($objp->statut,$objp->cotisation,$datefin,2); - print "".dol_print_email($obj->email,0,0,1)."'; - print dol_print_date($datefin,'day'); - if ($memberstatic->hasDelay()) { - print " ".img_warning($langs->trans("SubscriptionLate")); - } - print ''; + print dol_print_date($datefin,'day'); + if ($memberstatic->hasDelay()) { + print " ".img_warning($langs->trans("SubscriptionLate")); + } + print ''; + if ($obj->cotisation == 'yes') + { + print $langs->trans("SubscriptionNotReceived"); + if ($obj->statut > 0) print " ".img_warning(); + } + else + { + print ' '; + } + print ''; - if ($objp->cotisation == 'yes') - { - print $langs->trans("SubscriptionNotReceived"); - if ($objp->statut > 0) print " ".img_warning(); - } - else - { - print ' '; - } - print ''; + print dol_print_date($db->jdate($obj->date_creation), 'dayhour'); + print ''; + print dol_print_date($db->jdate($obj->date_update), 'dayhour'); + print ''; + print $memberstatic->LibStatut($obj->statut,$obj->cotisation,$datefin,2); + print ''; if ($user->rights->adherent->creer) { - print "rowid."&action=edit&backtopage=1\">".img_edit().""; + print "rowid."&action=edit&backtopage=1\">".img_edit().""; } print ' '; - if ($user->rights->adherent->supprimer && $objp->statut == -1) + if ($user->rights->adherent->supprimer && $obj->statut == -1) { - print "rowid."&action=delete&backtopage=1\">".img_picto($langs->trans("Delete"),'disable.png').""; + print "rowid."&action=delete&backtopage=1\">".img_picto($langs->trans("Delete"),'disable.png').""; } - if ($user->rights->adherent->supprimer && $objp->statut == 1) + if ($user->rights->adherent->supprimer && $obj->statut == 1) { - print "rowid."&action=resign&backtopage=1\">".img_picto($langs->trans("Resiliate"),'disable.png').""; + print "rowid."&action=resign&backtopage=1\">".img_picto($langs->trans("Resiliate"),'disable.png').""; } print "
    '.yn($obj->billed).' '; print ' '.$valuetoshow.'
    '.$langs->trans("NumberingShort").' 
     '; print ''; print ''; - print ''; - print ' '; + print ''; - $listetype=$membertypestatic->liste_array(); - print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); - print ''; + print '   '; + print ''; - print '   '; + $listetype=$membertypestatic->liste_array(); + print $form->selectarray("type", $listetype, $type, 1, 0, 0, '', 0, 32); + print ' '; + print '  '; @@ -482,62 +431,29 @@ if ($resql) } // Ref - if (! empty($arrayfields['d.ref']['checked'])) - { - print ""; - print $memberstatic->getNomUrl(1); - print ""; + print $memberstatic->getNomUrl(1); + print "rowid\">"; - print ((! empty($objp->lastname) || ! empty($objp->firstname)) ? dol_trunc($memberstatic->getFullName($langs)) : ''); - print (((! empty($objp->lastname) || ! empty($objp->firstname)) && ! empty($companyname)) ? ' / ' : ''); - print (! empty($companyname) ? dol_trunc($companyname, 32) : ''); - print "rowid\">"; + print ((! empty($objp->lastname) || ! empty($objp->firstname)) ? dol_trunc($memberstatic->getFullName($langs)) : ''); + print (((! empty($objp->lastname) || ! empty($objp->firstname)) && ! empty($companyname)) ? ' / ' : ''); + print (! empty($companyname) ? dol_trunc($companyname, 32) : ''); + print "".$objp->login."".$memberstatic->getmorphylib($objp->morphy)."".$objp->login."'; - print $membertypestatic->getNomUrl(1,32); - print '".$memberstatic->address."".$memberstatic->zip."".$memberstatic->town."'; + print $membertypestatic->getNomUrl(1,32); + print '".$memberstatic->getmorphylib($objp->morphy)."".dol_print_email($objp->email,0,0,1)."'; + print $memberstatic->LibStatut($objp->statut,$objp->cotisation,$datefin,2); + print "'; + print ''; print dol_print_date($datefin,'day'); if ($memberstatic->hasDelay()) { print " ".img_warning($langs->trans("SubscriptionLate")); @@ -570,23 +491,6 @@ if ($resql) } print '".$memberstatic->datec."".$memberstatic->datem."'; - print $memberstatic->LibStatut($objp->statut,$objp->cotisation,$datefin,2); - print "'; diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 6187293aefd..3000eecf33b 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -1,7 +1,6 @@ # Dolibarr language file - Source file is en_US - other SecurityCode=Security code Calendar=Calendar -NumberingShort=N° Tools=Tools ToolsDesc=This area is dedicated to group miscellaneous tools not available into other menu entries.

    Those tools can be reached from menu on the side. Birthday=Birthday From e8bd56a0d7e47779f2baea18b1db0f11e08442b7 Mon Sep 17 00:00:00 2001 From: philippe grand Date: Sun, 8 May 2016 15:53:01 +0200 Subject: [PATCH 639/834] add new translation --- htdocs/langs/en_US/other.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 3000eecf33b..6187293aefd 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -1,6 +1,7 @@ # Dolibarr language file - Source file is en_US - other SecurityCode=Security code Calendar=Calendar +NumberingShort=N° Tools=Tools ToolsDesc=This area is dedicated to group miscellaneous tools not available into other menu entries.

    Those tools can be reached from menu on the side. Birthday=Birthday From f4a533bafa2685c2853cc7411b8adb819f27ab5e Mon Sep 17 00:00:00 2001 From: philippe grand Date: Sun, 8 May 2016 16:09:00 +0200 Subject: [PATCH 640/834] Fix : when MAIN_VIEW_LINE_NUMBER is on --- htdocs/adherents/list.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index 01ce37ee3a8..4689b147cab 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -347,7 +347,7 @@ if ($resql) print '
     '.$langs->trans("NumberingShort").'
     '; - print $obj->town; - print ''; + print $obj->town; + print '
    '.$productstatic->label.'    '.$value['nb'].' 
    '; print ''; if (! empty($arrayfields['p.ref']['checked'])) print_liste_field_titre($arrayfields['p.ref']['label'], $_SERVER["PHP_SELF"],"p.ref","",$param,"",$sortfield,$sortorder); diff --git a/htdocs/product/reassort.php b/htdocs/product/reassort.php index a3465480b24..e330e9d8b75 100644 --- a/htdocs/product/reassort.php +++ b/htdocs/product/reassort.php @@ -267,19 +267,16 @@ if ($resql) print_liste_field_titre($langs->trans("StockLimit"), $_SERVER["PHP_SELF"], "p.seuil_stock_alerte",$param,"",'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("DesiredStock"), $_SERVER["PHP_SELF"], "p.desiredstock",$param,"",'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("PhysicalStock"), $_SERVER["PHP_SELF"], "stock_physique",$param,"",'align="right"',$sortfield,$sortorder); - - /* - * Details per warehouse - */ - - if($nb_warehouse>1) { - - foreach($warehouses_list as &$wh) { - print_liste_field_titre($wh['label'], '', '','','','align="right"'); + // Details per warehouse + if (! empty($conf->global->STOCK_DETAIL_ON_WAREHOUSE)) // TODO This should be moved into the selection of fields on page product/list (page product/stock will be removed and replaced with product/list with its own context) + { + if ($nb_warehouse>1) { + foreach($warehouses_list as &$wh) { + print_liste_field_titre($wh['label'], '', '','','','align="right"'); + } + } - } - if ($virtualdiffersfromphysical) print_liste_field_titre($langs->trans("VirtualStock"),$_SERVER["PHP_SELF"], "stock_theorique",$param,"",'align="right"',$sortfield,$sortorder); print_liste_field_titre(''); print_liste_field_titre($langs->trans("Status").' ('.$langs->trans("Sell").')',$_SERVER["PHP_SELF"], "p.tosell",$param,"",'align="right"',$sortfield,$sortorder); @@ -309,8 +306,8 @@ if ($resql) print ''; print ''; print ''; print ''; @@ -349,19 +346,17 @@ if ($resql) print $objp->stock_physique; print ''; - /* - * Details per warehouse - */ - - if($nb_warehouse>1) { - - foreach($warehouses_list as &$wh) { - - print ''; - } - + // Details per warehouse + if (! empty($conf->global->STOCK_DETAIL_ON_WAREHOUSE)) // TODO This should be moved into the selection of fields on page product/list (page product/stock will be removed and replaced with product/list with its own context) + { + if($nb_warehouse>1) { + foreach($warehouses_list as &$wh) { + + print ''; + } + } } From a83a291856e560645932e518f7845ca56d47dee4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 12 May 2016 19:55:36 +0200 Subject: [PATCH 660/834] Translation for one char --- htdocs/core/js/select2_locale.js.php | 5 ++++- htdocs/langs/en_US/main.lang | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/htdocs/core/js/select2_locale.js.php b/htdocs/core/js/select2_locale.js.php index 660b41820df..31c6a91bc74 100644 --- a/htdocs/core/js/select2_locale.js.php +++ b/htdocs/core/js/select2_locale.js.php @@ -52,7 +52,10 @@ else header('Cache-Control: no-cache'); $.fn.select2.locales['xx'] = { formatMatches: function (matches) { return matches + " trans("Select2ResultFoundUseArrows")); ?>"; }, formatNoMatches: function () { return "trans("Select2NotFound")); ?>"; }, - formatInputTooShort: function (input, min) { var n = min - input.length; return "trans("Select2Enter")); ?> " + n + " trans("Select2MoreCharacters")); ?>"; }, + formatInputTooShort: function (input, min) { var n = min - input.length; + if (n > 1) return "trans("Select2Enter")); ?> " + n + " trans("Select2MoreCharacters")); ?>"; + else return "trans("Select2Enter")); ?> " + n + " trans("Select2MoreCharacter")); ?>" + }, formatLoadMore: function (pageNumber) { return "trans("Select2LoadingMoreResults")); ?>"; }, formatSearching: function () { return "trans("Select2SearchInProgress")); ?>"; } }; diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 90ccde2474a..75023bf9184 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -784,6 +784,7 @@ SetRef=Set ref Select2ResultFoundUseArrows= Select2NotFound=No result found Select2Enter=Enter +Select2MoreCharacter=or more character Select2MoreCharacters=or more characters Select2LoadingMoreResults=Loading more results... Select2SearchInProgress=Search in progress... From 55e904bc0143cb57e64ee650e49dcbc4cd8b74e5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 12 May 2016 20:45:05 +0200 Subject: [PATCH 661/834] Debug for expert accounting module --- htdocs/accountancy/admin/journal.php | 133 +++++++++++------- htdocs/cashdesk/index.php | 2 +- htdocs/comm/action/listactions.php | 2 +- htdocs/core/lib/accounting.lib.php | 16 +-- htdocs/core/modules/modAccounting.class.php | 3 +- htdocs/langs/en_US/agenda.lang | 1 + htdocs/opensurvey/card.php | 4 +- .../class/opensurveysondage.class.php | 2 +- 8 files changed, 99 insertions(+), 64 deletions(-) diff --git a/htdocs/accountancy/admin/journal.php b/htdocs/accountancy/admin/journal.php index ca4ab67aee8..b511c6dfd1a 100644 --- a/htdocs/accountancy/admin/journal.php +++ b/htdocs/accountancy/admin/journal.php @@ -1,31 +1,32 @@ * Copyright (C) 2013-2015 Alexandre Spangaro - * Copyright (C) 2014 Florian Henry - * Copyright (C) 2014 Marcos García - * Copyright (C) 2014 Juanjo Menent - * Copyright (C) 2015 Jean-François Ferry - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ +* Copyright (C) 2014 Florian Henry +* Copyright (C) 2014 Marcos García +* Copyright (C) 2014 Juanjo Menent +* Copyright (C) 2015 Jean-François Ferry +* Copyright (C) 2016 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 +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ /** * \file htdocs/accountancy/admin/journal.php - * \ingroup Advanced accountancy - * \brief Setup page to configure accounting expert module - */ +* \ingroup Advanced accountancy +* \brief Setup page to configure accounting expert module +*/ require '../../main.inc.php'; // Class @@ -48,24 +49,44 @@ $list = array ( 'ACCOUNTING_PURCHASE_JOURNAL', 'ACCOUNTING_SOCIAL_JOURNAL', 'ACCOUNTING_MISCELLANEOUS_JOURNAL', - 'ACCOUNTING_EXPENSEREPORT_JOURNAL' + 'ACCOUNTING_EXPENSEREPORT_JOURNAL' ); /* * Actions - */ +*/ if ($action == 'update') { $error = 0; - - foreach ( $list as $constname ) { + + // Save vars + foreach ($list as $constname) + { $constvalue = GETPOST($constname, 'alpha'); - + if (! dolibarr_set_const($db, $constname, $constvalue, 'chaine', 0, '', $conf->entity)) { $error ++; } } - + + // Save bank account journals + $arrayofbankaccount = GETPOST('bank_account', 'array'); + foreach($arrayofbankaccount as $key => $code) + { + $bankaccount = new Account($db); + $res = $bankaccount->fetch($key); + if ($res > 0) + { + $bankaccount->accountancy_journal = $code; + $bankaccount->update($user); + } + else + { + $error++; + break; + } + } + if (! $error) { setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); } else { @@ -75,7 +96,7 @@ if ($action == 'update') { /* * View - */ +*/ llxHeader(); @@ -99,13 +120,13 @@ print "\n"; foreach ( $list as $key ) { $var = ! $var; - + print ''; - + // Param $label = $langs->trans($key); print ''; - + // Value print '\n"; diff --git a/htdocs/core/lib/accounting.lib.php b/htdocs/core/lib/accounting.lib.php index 7a73861e71f..773e68913f9 100644 --- a/htdocs/core/lib/accounting.lib.php +++ b/htdocs/core/lib/accounting.lib.php @@ -37,26 +37,26 @@ function admin_accounting_prepare_head(AccountingAccount $object=null) $head = array (); $head[$h][0] = dol_buildpath('/accountancy/admin/index.php', 1); - $head[$h][1] = $langs->trans("Configuration"); + $head[$h][1] = $langs->trans("Miscellaneous"); $head[$h][2] = 'general'; $h ++; - // Show more tabs from modules - // Entries must be declared in modules descriptor with line - // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab - // $this->tabs = array('entity:-tabname); to remove a tab - complete_head_from_modules($conf, $langs, $object, $head, $h, 'accounting_admin'); - $head[$h][0] = DOL_URL_ROOT.'/accountancy/admin/journal.php'; $head[$h][1] = $langs->trans("Journaux"); $head[$h][2] = 'journal'; $h ++; $head[$h][0] = DOL_URL_ROOT.'/accountancy/admin/export.php'; - $head[$h][1] = $langs->trans("Export"); + $head[$h][1] = $langs->trans("ExportOptions"); $head[$h][2] = 'export'; $h ++; + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab + // $this->tabs = array('entity:-tabname); to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'accounting_admin'); + complete_head_from_modules($conf, $langs, $object, $head, $h, 'accounting_admin', 'remove'); return $head; diff --git a/htdocs/core/modules/modAccounting.class.php b/htdocs/core/modules/modAccounting.class.php index f20cf663b2c..03b450aa275 100644 --- a/htdocs/core/modules/modAccounting.class.php +++ b/htdocs/core/modules/modAccounting.class.php @@ -3,6 +3,7 @@ * Copyright (C) 2013-2016 Alexandre Spangaro * Copyright (C) 2014 Ari Elbaz (elarifr) * Copyright (C) 2014 Florian Henry + * Copyright (C) 2016 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 @@ -50,7 +51,7 @@ class modAccounting extends DolibarrModules $this->description = "Advanced accounting management"; // Possible values for version are: 'development', 'experimental', 'dolibarr' or version - $this->version = 'development'; + $this->version = 'experimental'; $this->const_name = 'MAIN_MODULE_' . strtoupper($this->name); $this->special = 0; diff --git a/htdocs/langs/en_US/agenda.lang b/htdocs/langs/en_US/agenda.lang index a01e0c6c412..d4b651b46b5 100644 --- a/htdocs/langs/en_US/agenda.lang +++ b/htdocs/langs/en_US/agenda.lang @@ -8,6 +8,7 @@ Calendar=Calendar Calendars=Calendars LocalAgenda=Internal calendar ActionsOwnedBy=Event owned by +ActionsOwnedByShort=Owner AffectedTo=Assigned to DoneBy=Done by Event=Event diff --git a/htdocs/opensurvey/card.php b/htdocs/opensurvey/card.php index ce941a680c7..b1c69268325 100644 --- a/htdocs/opensurvey/card.php +++ b/htdocs/opensurvey/card.php @@ -84,14 +84,14 @@ if (empty($reshook)) if ($action == 'close') { $object->status = Opensurveysondage::STATUS_CLOSED; - $object->update(); + $object->update($user); } // Reopend if ($action == 'reopen') { $object->status = Opensurveysondage::STATUS_VALIDATED; - $object->update(); + $object->update($user); } // Update diff --git a/htdocs/opensurvey/class/opensurveysondage.class.php b/htdocs/opensurvey/class/opensurveysondage.class.php index fecb7904a9c..02952f4bf75 100644 --- a/htdocs/opensurvey/class/opensurveysondage.class.php +++ b/htdocs/opensurvey/class/opensurveysondage.class.php @@ -275,7 +275,7 @@ class Opensurveysondage extends CommonObject * @param int $notrigger 0=launch triggers after, 1=disable triggers * @return int <0 if KO, >0 if OK */ - function update($user=null, $notrigger=0) + function update($user, $notrigger=0) { global $conf, $langs; $error=0; From 986119d2b509aef3d8a0d49c73009556438c4200 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 12 May 2016 21:32:24 +0200 Subject: [PATCH 662/834] Start debug --- dev/skeletons/skeleton_list.php | 2 +- htdocs/accountancy/admin/account.php | 82 ++-- htdocs/accountancy/admin/fiscalyear.php | 30 +- htdocs/accountancy/admin/fiscalyear_card.php | 11 +- htdocs/accountancy/bookkeeping/balance.php | 28 +- htdocs/accountancy/bookkeeping/list.php | 350 +++++++++--------- .../class/html.formventilation.class.php | 4 +- htdocs/core/menus/init_menu_auguria.sql | 2 +- htdocs/core/menus/standard/eldy.lib.php | 7 +- 9 files changed, 295 insertions(+), 221 deletions(-) diff --git a/dev/skeletons/skeleton_list.php b/dev/skeletons/skeleton_list.php index 78f17dc1a63..6fbecbf2675 100644 --- a/dev/skeletons/skeleton_list.php +++ b/dev/skeletons/skeleton_list.php @@ -280,7 +280,7 @@ if ($resql) print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit); - print '
    '; + print ''; if ($optioncss != '') print ''; print ''; print ''; diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index 5bdaa3c5828..2f7228eb9d4 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -1,6 +1,7 @@ * Copyright (C) 2013-2016 Alexandre Spangaro + * Copyright (C) 2016 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 @@ -48,6 +49,8 @@ if ($user->societe_id > 0) if (! $user->rights->accounting->chartofaccount) accessforbidden(); +// Load variable for pagination +$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit; $sortfield = GETPOST("sortfield", 'alpha'); $sortorder = GETPOST("sortorder", 'sortorder'); $limit = GETPOST('limit') ? GETPOST('limit', 'int') : $conf->liste_limit; @@ -55,7 +58,7 @@ $page = GETPOST("page", 'int'); if ($page == - 1) { $page = 0; } -$offset = $conf->liste_limit * $page; +$offset = $limit * $page; $pageprev = $page - 1; $pagenext = $page + 1; if (! $sortfield) @@ -63,13 +66,31 @@ if (! $sortfield) if (! $sortorder) $sortorder = "ASC"; -if ($action == 'delete') { - $formconfirm = $html->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $id, $langs->trans('DeleteAccount'), $langs->trans('ConfirmDeleteAccount'), 'confirm_delete', '', 0, 1); - print $formconfirm; -} - $accounting = new AccountingAccount($db); + +/* + * Actions + */ + +if (GETPOST('cancel')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction')) { $massaction=''; } + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers +{ + $search_account = ""; + $search_label = ""; + $search_accountparent = ""; + $search_pcgtype = ""; + $search_pcgsubtype = ""; +} + if ($action == 'disable') { if ($accounting->fetch($id)) { $result = $accounting->account_desactivate($id); @@ -89,21 +110,18 @@ if ($action == 'disable') { } } -if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers -{ - $search_account = ""; - $search_label = ""; - $search_accountparent = ""; - $search_pcgtype = ""; - $search_pcgsubtype = ""; -} /* * View - * */ + llxHeader('', $langs->trans("ListAccounts")); +if ($action == 'delete') { + $formconfirm = $html->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $id, $langs->trans('DeleteAccount'), $langs->trans('ConfirmDeleteAccount'), 'confirm_delete', '', 0, 1); + print $formconfirm; +} + $pcgver = $conf->global->CHARTOFACCOUNTS; $sql = "SELECT aa.rowid, aa.fk_pcg_version, aa.pcg_type, aa.pcg_subtype, aa.account_number, aa.account_parent , aa.label, aa.active, "; @@ -128,8 +146,16 @@ if (strlen(trim($search_pcgtype))) { if (strlen(trim($search_pcgsubtype))) { $sql .= " AND aa.pcg_subtype like '%" . $search_pcgsubtype . "%'"; } - $sql .= $db->order($sortfield, $sortorder); + +// Count total nb of records +$nbtotalofrecords = 0; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); +} + $sql .= $db->plimit($limit + 1, $offset); dol_syslog('accountancy/admin/account.php:: $sql=' . $sql); @@ -138,7 +164,15 @@ $result = $db->query($sql); if ($result) { $num = $db->num_rows($result); - print_barre_liste($langs->trans('ListAccounts'), $page, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '', $num); + $params=''; + if ($search_account != "") $params.= '&search_account='.urlencode($search_account); + if ($search_label != "") $params.= '&search_label='.urlencode($search_label); + if ($search_accountparent != "") $params.= '&search_accountparent='.urlencode($search_accountparent); + if ($search_pcgtype != "") $params.= '&search_pcgtype='.urlencode($search_pcgtype); + if ($search_pcgsubtype != "") $params.= '&search_pcgsubtype='.urlencode($search_pcgsubtype); + if ($optioncss != '') $param.='&optioncss='.$optioncss; + + print_barre_liste($langs->trans('ListAccounts'), $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords); $i = 0; @@ -154,13 +188,13 @@ if ($result) { print '
      '; - $searchpitco=$form->showFilterAndCheckAddButtons(0); - print $searchpitco; + $searchpitco=$form->showFilterAndCheckAddButtons(0); + print $searchpitco; print '
    '; - print empty($product->stock_warehouse[$wh['id']]->real) ? '0' : $product->stock_warehouse[$wh['id']]->real; - print ''; + print empty($product->stock_warehouse[$wh['id']]->real) ? '0' : $product->stock_warehouse[$wh['id']]->real; + print '
    '; print ''; @@ -118,11 +139,11 @@ print '
    '; print ''; print ''; -print ''; +print ''; print "\n"; // Bank account -$sql = "SELECT rowid, label, accountancy_journal"; +$sql = "SELECT rowid, label, number, accountancy_journal"; $sql .= " FROM " . MAIN_DB_PREFIX . "bank_account"; $sql .= " WHERE entity = " . $conf->entity; $sql .= " AND clos = 0"; @@ -132,29 +153,41 @@ $resql = $db->query($sql); if ($resql) { $numr = $db->num_rows($resql); $i = 0; - + if ($numr > 0) - - while ( $i < $numr ) { - $objp = $db->fetch_object($resql); + + $bankaccountstatic=new Account($db); + + while ( $i < $numr ) { + $objp = $db->fetch_object($resql); - $var = ! $var; + $var = ! $var; - print ''; + $bankaccountstatic->id = $objp->rowid; + $bankaccountstatic->label = $objp->label; + $bankaccountstatic->number = $objp->number; + $bankaccountstatic->accountancy_journal = $objp->accountancy_journal; - // Param - print ''; + print ''; - // Value - print ''; + // Param + print ''; - $i ++; - } -} else + // Value + print ''; + + $i ++; + } + $db->free($resql); +} +else +{ dol_print_error($db); -$db->free($resql); +} print "
    ' . $langs->trans('JournalFinancial') . '' . $langs->trans('JournalFinancial') . ' ('.$langs->trans('Opened').')
    '; - print ''; - print '
    '; + print ''; + print '
    \n"; diff --git a/htdocs/cashdesk/index.php b/htdocs/cashdesk/index.php index 3a5b54519a8..4a892f909e5 100644 --- a/htdocs/cashdesk/index.php +++ b/htdocs/cashdesk/index.php @@ -59,7 +59,7 @@ top_htmlhead('','',0,0,'',$arrayofcss);
    diff --git a/htdocs/comm/action/listactions.php b/htdocs/comm/action/listactions.php index 31290318742..1f597ffeddc 100644 --- a/htdocs/comm/action/listactions.php +++ b/htdocs/comm/action/listactions.php @@ -308,7 +308,7 @@ if ($resql) print_liste_field_titre($langs->trans("DateEnd"),$_SERVER["PHP_SELF"],"a.datep2",$param,'','align="center"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("ThirdParty"),$_SERVER["PHP_SELF"],"s.nom",$param,"","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("Contact"),$_SERVER["PHP_SELF"],"a.fk_contact",$param,"","",$sortfield,$sortorder); - print_liste_field_titre($langs->trans("ActionsOwnedBy"),$_SERVER["PHP_SELF"],"",$param,"","",$sortfield,$sortorder); + print_liste_field_titre($langs->trans("ActionsOwnedByShort"),$_SERVER["PHP_SELF"],"",$param,"","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"a.percent",$param,"",'align="center"',$sortfield,$sortorder); print_liste_field_titre(""); print "
    '; print ''; - print_liste_field_titre($langs->trans("AccountNumber"), $_SERVER["PHP_SELF"], "aa.account_number", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Label"), $_SERVER["PHP_SELF"], "aa.label", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Accountparent"), $_SERVER["PHP_SELF"], "aa.account_parent", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Pcgtype"), $_SERVER["PHP_SELF"], "aa.pcg_type", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Pcgsubtype"), $_SERVER["PHP_SELF"], "aa.pcg_subtype", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Activated"), $_SERVER["PHP_SELF"], "aa.active", "", $param, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $param, "", 'width="60" align="center"', $sortfield, $sortorder); + print_liste_field_titre($langs->trans("AccountNumber"), $_SERVER["PHP_SELF"], "aa.account_number", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Label"), $_SERVER["PHP_SELF"], "aa.label", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Accountparent"), $_SERVER["PHP_SELF"], "aa.account_parent", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Pcgtype"), $_SERVER["PHP_SELF"], "aa.pcg_type", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Pcgsubtype"), $_SERVER["PHP_SELF"], "aa.pcg_subtype", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Activated"), $_SERVER["PHP_SELF"], "aa.active", "", $params, "", $sortfield, $sortorder); + print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $params, "", 'width="60" align="center"', $sortfield, $sortorder); print ''; print ''; diff --git a/htdocs/accountancy/admin/fiscalyear.php b/htdocs/accountancy/admin/fiscalyear.php index 75411d6402f..00cf70b9e73 100644 --- a/htdocs/accountancy/admin/fiscalyear.php +++ b/htdocs/accountancy/admin/fiscalyear.php @@ -27,6 +27,18 @@ require_once DOL_DOCUMENT_ROOT . '/core/class/fiscalyear.class.php'; $action = GETPOST('action'); +// Load variable for pagination +$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit; +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if ($page == -1) { $page = 0; } +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortfield) $sortfield="f.rowid"; // Set here default search field +if (! $sortorder) $sortorder="ASC"; + $langs->load("admin"); $langs->load("compta"); @@ -54,6 +66,8 @@ $object = new Fiscalyear($db); * Actions */ + + /* * View */ @@ -66,11 +80,20 @@ $title = $langs->trans('FiscalYears'); llxHeader('', $title, LOG_ERR); -print load_fiche_titre($langs->trans('FiscalYears')); - $sql = "SELECT f.rowid, f.label, f.date_start, f.date_end, f.statut, f.entity"; $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_fiscalyear as f"; $sql .= " WHERE f.entity = " . $conf->entity; +$sql.=$db->order($sortfield,$sortorder); + +// Count total nb of records +$nbtotalofrecords = 0; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); +} + +$sql.= $db->plimit($limit+1, $offset); $result = $db->query($sql); if ($result) { @@ -78,6 +101,9 @@ if ($result) { $num = $db->num_rows($result); $i = 0; + + $title = $langs->trans('FiscalYears'); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_generic', 0, '', '', $limit, 1); // Load attribute_label print '
    '; diff --git a/htdocs/accountancy/admin/fiscalyear_card.php b/htdocs/accountancy/admin/fiscalyear_card.php index e663557d22c..c45700ce4f5 100644 --- a/htdocs/accountancy/admin/fiscalyear_card.php +++ b/htdocs/accountancy/admin/fiscalyear_card.php @@ -53,6 +53,7 @@ $object = new Fiscalyear($db); $date_start = dol_mktime(0, 0, 0, GETPOST('fiscalyearmonth', 'int'), GETPOST('fiscalyearday', 'int'), GETPOST('fiscalyearyear', 'int')); $date_end = dol_mktime(0, 0, 0, GETPOST('fiscalyearendmonth', 'int'), GETPOST('fiscalyearendday', 'int'), GETPOST('fiscalyearendyear', 'int')); + /* * Actions */ @@ -143,10 +144,8 @@ llxHeader(); $form = new Form($db); -/* - * Action create - */ -if ($action == 'create') { +if ($action == 'create') +{ print load_fiche_titre($langs->trans("NewFiscalYear")); print ''; @@ -172,7 +171,7 @@ if ($action == 'create') { // Statut print ''; - print ''; + print ''; print ''; @@ -205,7 +204,7 @@ if ($action == 'create') { // Ref print ""; - print ''; diff --git a/htdocs/accountancy/bookkeeping/balance.php b/htdocs/accountancy/bookkeeping/balance.php index f94557ce0b0..973086e95b9 100644 --- a/htdocs/accountancy/bookkeeping/balance.php +++ b/htdocs/accountancy/bookkeeping/balance.php @@ -100,9 +100,11 @@ if (! empty($search_accountancy_code_end)) { $options .= '&search_accountancy_code_end=' . $search_accountancy_code_end; } + /* * Action */ + if ($action == 'export_csv') { $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV; $journal = 'bookkepping'; @@ -157,14 +159,26 @@ else { print '
    '; print ''; - print '
    '; - print $langs->trans('DateStart') . ': '; - print $form->select_date($search_date_start, 'date_start', 0, 0, 1); - print $langs->trans('DateEnd') . ': '; - print $form->select_date($search_date_end, 'date_end', 0, 0, 1); - print '
    '; + $moreforfilter=''; + + $moreforfilter.='
    '; + $moreforfilter.=$langs->trans('DateStart') . ': '; + $moreforfilter.=$form->select_date($search_date_start, 'date_start', 0, 0, 1, '', 1, 0, 1); + $moreforfilter.=$langs->trans('DateEnd') . ': '; + $moreforfilter.=$form->select_date($search_date_end, 'date_end', 0, 0, 1, '', 1, 0, 1); + $moreforfilter.='
    '; - print '
    ' . $langs->trans("Statut") . '' . $langs->trans("Status") . ''; print $form->selectarray('statut', $statut2label, GETPOST('statut')); print '
    ' . $langs->trans("Ref") . ''; + print '' . $langs->trans("Ref") . ''; print $object->ref; print '
    '; + if (! empty($moreforfilter)) + { + print '
    '; + print $moreforfilter; + $parameters=array(); + $reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + print '
    '; + } + + print '
    '; print ''; print_liste_field_titre($langs->trans("AccountAccountingShort"), $_SERVER['PHP_SELF'], "t.numero_compte", "", $options, "", $sortfield, $sortorder); print_liste_field_titre($langs->trans("Labelcompte"), $_SERVER['PHP_SELF'], "t.label_compte", "", $options, "", $sortfield, $sortorder); diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index f287c0ad9dd..6100e54c9a3 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -87,24 +87,8 @@ $formventilation = new FormVentilation($db); $formother = new FormOther($db); $form = new Form($db); -if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers -{ - $search_mvt_num = ''; - $search_doc_type = ''; - $search_doc_ref = ''; - $search_doc_date = ''; - $search_accountancy_code = ''; - $search_accountancy_code_start = ''; - $search_accountancy_code_end = ''; - $search_accountancy_aux_code = ''; - $search_accountancy_aux_code_start = ''; - $search_accountancy_aux_code_end = ''; - $search_mvt_label = ''; - $search_direction = ''; - $search_ledger_code = ''; - $search_date_start = ''; - $search_date_end = ''; -} + + if (empty($search_date_start)) { $search_date_start = dol_mktime(0, 0, 0, 1, 1, dol_print_date(dol_now(), '%Y')); @@ -178,9 +162,30 @@ if (! empty($search_mvt_num)) { $options .= '&search_mvt_num=' . $search_mvt_num; } + /* * Action */ + +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers +{ + $search_mvt_num = ''; + $search_doc_type = ''; + $search_doc_ref = ''; + $search_doc_date = ''; + $search_accountancy_code = ''; + $search_accountancy_code_start = ''; + $search_accountancy_code_end = ''; + $search_accountancy_aux_code = ''; + $search_accountancy_aux_code_start = ''; + $search_accountancy_aux_code_end = ''; + $search_mvt_label = ''; + $search_direction = ''; + $search_ledger_code = ''; + $search_date_start = ''; + $search_date_end = ''; +} + if ($action == 'delbookkeeping') { $import_key = GETPOST('importkey', 'alpha'); @@ -193,7 +198,8 @@ if ($action == 'delbookkeeping') { Header("Location: list.php"); exit(); } -} elseif ($action == 'delbookkeepingyearconfirm') { +} +if ($action == 'delbookkeepingyearconfirm') { $delyear = GETPOST('delyear', 'int'); @@ -205,7 +211,8 @@ if ($action == 'delbookkeeping') { Header("Location: list.php"); exit(); } -} elseif ($action == 'delmouvconfirm') { +} +if ($action == 'delmouvconfirm') { $mvt_num = GETPOST('mvt_num', 'int'); @@ -217,7 +224,8 @@ if ($action == 'delbookkeeping') { Header("Location: list.php"); exit(); } -} elseif ($action == 'export_csv') { +} +if ($action == 'export_csv') { include DOL_DOCUMENT_ROOT . '/accountancy/class/accountancyexport.class.php'; @@ -311,168 +319,166 @@ if ($action == 'delbookkeeping') { print "\n"; } } + + exit; } /* * View */ -else { - $title_page = $langs->trans("Bookkeeping") . ' ' . dol_print_date($search_date_start) . '-' . dol_print_date($search_date_end); +$title_page = $langs->trans("Bookkeeping") . ' ' . dol_print_date($search_date_start) . '-' . dol_print_date($search_date_end); - llxHeader('', $title_page); +llxHeader('', $title_page); - /* - * List - */ +// List - $nbtotalofrecords = 0; - if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { - $nbtotalofrecords = $object->fetchAll($sortorder, $sortfield, 0, 0, $filter); - if ($nbtotalofrecords < 0) { - setEventMessages($object->error, $object->errors, 'errors'); - } - } - - $result = $object->fetchAll($sortorder, $sortfield, $limit, $offset, $filter); - if ($result < 0) { +$nbtotalofrecords = 0; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { + $nbtotalofrecords = $object->fetchAll($sortorder, $sortfield, 0, 0, $filter); + if ($nbtotalofrecords < 0) { setEventMessages($object->error, $object->errors, 'errors'); } - - if ($action == 'delmouv') { - $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?mvt_num=' . GETPOST('mvt_num'), $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delmouvconfirm', '', 0, 1); - print $formconfirm; - } - if ($action == 'delbookkeepingyear') { - - $form_question = array (); - $delyear = GETPOST('delyear'); - - if (empty($delyear)) { - $delyear = dol_print_date(dol_now(), '%Y'); - } - $year_array = $formventilation->selectyear_accountancy_bookkepping($delyear, 'delyear', 0, 'array'); - - $form_question['delyear'] = array ( - 'name' => 'delyear', - 'type' => 'select', - 'label' => $langs->trans('DelYear'), - 'values' => $year_array, - 'default' => $delyear - ); - - $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delbookkeepingyearconfirm', $form_question, 0, 1); - print $formconfirm; - } - - print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $options, $sortfield, $sortorder, '', $result, $nbtotalofrecords); - - print ''; - print '
    ' . "\n"; - print '
    '; - print ''; - print '
    '; - - print '
    '; - - print '
    '; - print ''; - print_liste_field_titre($langs->trans("NumPiece"), $_SERVER['PHP_SELF'], "t.piece_num", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Docdate"), $_SERVER['PHP_SELF'], "t.doc_date", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Docref"), $_SERVER['PHP_SELF'], "t.doc_ref", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("AccountAccountingShort"), $_SERVER['PHP_SELF'], "t.numero_compte", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Code_tiers"), $_SERVER['PHP_SELF'], "t.code_tiers", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Labelcompte"), $_SERVER['PHP_SELF'], "bk_label_compte", "", $options, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Codejournal"), $_SERVER['PHP_SELF'], "t.code_journal", "", $options, 'align="center"', $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder); - print "\n"; - - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - - print ''; - print ''; - print ''; - print ''; - print ''; - - print ''; - - $var = True; - - $total_debit = 0; - $total_credit = 0; - - foreach ( $object->lines as $line ) { - $var = ! $var; - - $total_debit += $line->debit; - $total_credit += $line->credit; - - print ""; - - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print "\n"; - } - - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - - print "
    '; - print $langs->trans('From') . ': '; - print $form->select_date($search_date_start, 'date_start', 0, 0, 1); - print '
    '; - print $langs->trans('to') . ': '; - print $form->select_date($search_date_end, 'date_end', 0, 0, 1); - print '
    '; - print $langs->trans('From'); - print $formventilation->select_account($search_accountancy_code_start, 'search_accountancy_code_start', 1, array (), 1, 1, ''); - print '
    '; - print $langs->trans('to'); - print $formventilation->select_account($search_accountancy_code_end, 'search_accountancy_code_end', 1, array (), 1, 1, ''); - print '
    '; - print $langs->trans('From'); - print $formventilation->select_auxaccount($search_accountancy_aux_code_start, 'search_accountancy_aux_code_start', 1); - print '
    '; - print $langs->trans('to'); - print $formventilation->select_auxaccount($search_accountancy_aux_code_end, 'search_accountancy_aux_code_end', 1); - print '
    '; - print ''; - print '  '; - print ''; - print ' '; - print ''; - print '
    ' . $line->piece_num . '' . dol_print_date($line->doc_date, 'day') . '' . $line->doc_ref . '' . length_accountg($line->numero_compte) . '' . length_accounta($line->code_tiers) . '' . $line->label_compte . '' . price($line->debit) . '' . price($line->credit) . '' . $line->code_journal . ''; - print '' . img_edit() . ' '; - print '' . img_delete() . ''; - print '
    '; - print price($total_debit); - print ''; - print price($total_credit); - print '
    "; - print ''; - - llxFooter(); } +$result = $object->fetchAll($sortorder, $sortfield, $limit, $offset, $filter); +if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); +} + +if ($action == 'delmouv') { + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?mvt_num=' . GETPOST('mvt_num'), $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delmouvconfirm', '', 0, 1); + print $formconfirm; +} +if ($action == 'delbookkeepingyear') { + + $form_question = array (); + $delyear = GETPOST('delyear'); + + if (empty($delyear)) { + $delyear = dol_print_date(dol_now(), '%Y'); + } + $year_array = $formventilation->selectyear_accountancy_bookkepping($delyear, 'delyear', 0, 'array'); + + $form_question['delyear'] = array ( + 'name' => 'delyear', + 'type' => 'select', + 'label' => $langs->trans('DelYear'), + 'values' => $year_array, + 'default' => $delyear + ); + + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delbookkeepingyearconfirm', $form_question, 0, 1); + print $formconfirm; +} + +print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $options, $sortfield, $sortorder, '', $result, $nbtotalofrecords); + +print '
    '; +print '
    ' . "\n"; +print '
    '; +print ''; +print '
    '; + +print '
    '; + +print ''; +print ''; +print_liste_field_titre($langs->trans("NumPiece"), $_SERVER['PHP_SELF'], "t.piece_num", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Docdate"), $_SERVER['PHP_SELF'], "t.doc_date", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Docref"), $_SERVER['PHP_SELF'], "t.doc_ref", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("AccountAccountingShort"), $_SERVER['PHP_SELF'], "t.numero_compte", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Code_tiers"), $_SERVER['PHP_SELF'], "t.code_tiers", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Labelcompte"), $_SERVER['PHP_SELF'], "bk_label_compte", "", $options, "", $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Codejournal"), $_SERVER['PHP_SELF'], "t.code_journal", "", $options, 'align="center"', $sortfield, $sortorder); +print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder); +print "\n"; + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; +print ''; + +print ''; + +$var = True; + +$total_debit = 0; +$total_credit = 0; + +foreach ( $object->lines as $line ) { + $var = ! $var; + + $total_debit += $line->debit; + $total_credit += $line->credit; + + print ""; + + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; +} + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +print "
    '; +print $langs->trans('From') . ': '; +print $form->select_date($search_date_start, 'date_start', 0, 0, 1); +print '
    '; +print $langs->trans('to') . ': '; +print $form->select_date($search_date_end, 'date_end', 0, 0, 1); +print '
    '; +print $langs->trans('From'); +print $formventilation->select_account($search_accountancy_code_start, 'search_accountancy_code_start', 1, array (), 1, 1, ''); +print '
    '; +print $langs->trans('to'); +print $formventilation->select_account($search_accountancy_code_end, 'search_accountancy_code_end', 1, array (), 1, 1, ''); +print '
    '; +print $langs->trans('From'); +print $formventilation->select_auxaccount($search_accountancy_aux_code_start, 'search_accountancy_aux_code_start', 1); +print '
    '; +print $langs->trans('to'); +print $formventilation->select_auxaccount($search_accountancy_aux_code_end, 'search_accountancy_aux_code_end', 1); +print '
    '; +print ''; +print '  '; +$searchpitco=$form->showFilterAndCheckAddButtons(0); +print $searchpitco; +print '
    ' . $line->piece_num . '' . dol_print_date($line->doc_date, 'day') . '' . $line->doc_ref . '' . length_accountg($line->numero_compte) . '' . length_accounta($line->code_tiers) . '' . $line->label_compte . '' . price($line->debit) . '' . price($line->credit) . '' . $line->code_journal . ''; + print '' . img_edit() . ' '; + print '' . img_delete() . ''; + print '
    '; +print price($total_debit); +print ''; +print price($total_credit); +print '
    "; +print '
    '; + +llxFooter(); + + $db->close(); \ No newline at end of file diff --git a/htdocs/accountancy/class/html.formventilation.class.php b/htdocs/accountancy/class/html.formventilation.class.php index 37f843ec5a6..e3ae592a47f 100644 --- a/htdocs/accountancy/class/html.formventilation.class.php +++ b/htdocs/accountancy/class/html.formventilation.class.php @@ -123,7 +123,7 @@ class FormVentilation extends Form $options[$select_value_out] = $label; } - $out .= Form::selectarray($htmlname, $options, $selected, $showempty); + $out .= Form::selectarray($htmlname, $options, $selected, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300'); $this->db->free($resql); return $out; } @@ -264,7 +264,7 @@ class FormVentilation extends Form // Build select $out = ajax_combobox($htmlname, $event); - $out .= Form::selectarray($htmlname, $aux_account, $selectid, $showempty); + $out .= Form::selectarray($htmlname, $aux_account, $selectid, $showempty, 0, 0, '', 0, 0, 0, '', 'maxwidth300'); return $out; } diff --git a/htdocs/core/menus/init_menu_auguria.sql b/htdocs/core/menus/init_menu_auguria.sql index c0fa6b9fad9..a39783820f8 100644 --- a/htdocs/core/menus/init_menu_auguria.sql +++ b/htdocs/core/menus/init_menu_auguria.sql @@ -26,7 +26,6 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$user->admin', __HANDLER__, 'left', 100__+MAX_llx_menu__, 'home', 'setup', 1__+MAX_llx_menu__, '/admin/index.php?leftmenu=setup', 'Setup', 0, 'admin', '', '', 2, 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 ('', '$leftmenu=="setup"', __HANDLER__, 'left', 101__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/admin/company.php?leftmenu=setup', 'MenuCompanySetup', 1, 'admin', '', '', 2, 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 ('', '$leftmenu=="setup"', __HANDLER__, 'left', 102__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/admin/ihm.php?leftmenu=setup', 'GUISetup', 1, 'admin', '', '', 2, 4, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="setup" && $conf->accounting->enabled', __HANDLER__, 'left', 115__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/accountancy/admin/fiscalyear.php?mainmenu=setup', 'Fiscalyear', 1, 'admin', '', '', 2, 4, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="setup"', __HANDLER__, 'left', 114__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/admin/translation.php?leftmenu=setup', 'Translation', 1, 'admin', '', '', 2, 4, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="setup"', __HANDLER__, 'left', 103__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/admin/modules.php?leftmenu=setup', 'Modules', 1, 'admin', '', '', 2, 2, __ENTITY__); @@ -235,6 +234,7 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->accounting->enabled && $leftmenu=="ca"', __HANDLER__, 'left', 2447__+MAX_llx_menu__, 'accountancy', '', 2440__+MAX_llx_menu__, '/compta/stats/cabyprodserv.php?leftmenu=ca', 'ByProductsAndServices', 3, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 24, __ENTITY__); -- Admin 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->accounting->enabled', __HANDLER__, 'left', 2451__+MAX_llx_menu__, 'home', '', 2400__+MAX_llx_menu__, '/accountancy/admin/account.php?mainmenu=accountancy', 'Chartofaccounts', 1, 'admin', '$user->rights->accounting->chartofaccount', '', 0, 25, __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 ('', '$leftmenu=="setup" && $conf->accounting->enabled', __HANDLER__, 'left', 115__+MAX_llx_menu__, 'home', '', 100__+MAX_llx_menu__, '/accountancy/admin/fiscalyear.php?mainmenu=setup', 'Fiscalyear', 1, 'admin', '', '', 2, 4, __ENTITY__); -- Rapports compta simple 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->comptabilite->enabled', __HANDLER__, 'left', 2700__+MAX_llx_menu__, 'accountancy', 'ca', 6__+MAX_llx_menu__, '/compta/resultat/index.php?leftmenu=ca&mainmenu=accountancy', 'Reportings', 0, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 11, __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->comptabilite->enabled && $leftmenu=="ca"', __HANDLER__, 'left', 2701__+MAX_llx_menu__, 'accountancy', '', 2700__+MAX_llx_menu__, '/compta/resultat/index.php?leftmenu=ca', 'ReportInOut', 1, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 0, __ENTITY__); diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 67708eba5fe..87f8e4f5011 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -513,11 +513,6 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu $newmenu->add("/admin/menus.php?mainmenu=home", $langs->trans("Menus"),1); $newmenu->add("/admin/ihm.php?mainmenu=home", $langs->trans("GUISetup"),1); - if (! empty($conf->accounting->enabled)) - { - $newmenu->add("/accountancy/admin/fiscalyear.php?mainmenu=home", $langs->trans("Fiscalyear"),1); - } - $newmenu->add("/admin/translation.php", $langs->trans("Translation"),1); $newmenu->add("/admin/boxes.php?mainmenu=home", $langs->trans("Boxes"),1); $newmenu->add("/admin/delais.php?mainmenu=home",$langs->trans("Alerts"),1); @@ -994,8 +989,8 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu // Admin $langs->load("admin"); - if (preg_match('/accountancy/',$leftmenu)) $newmenu->add("/accountancy/admin/fiscalyear.php?mainmenu=accountancy&leftmenu=accountancy_admin", $langs->trans("Fiscalyear"),1,$user->rights->accounting->fiscalyear, '', $mainmenu, 'fiscalyear'); if (preg_match('/accountancy/',$leftmenu)) $newmenu->add("/accountancy/admin/account.php?mainmenu=accountancy&leftmenu=accountancy_admin", $langs->trans("Chartofaccounts"),1,$user->rights->accounting->chartofaccount, '', $mainmenu, 'chartofaccount'); + if (preg_match('/accountancy/',$leftmenu)) $newmenu->add("/accountancy/admin/fiscalyear.php?mainmenu=accountancy&leftmenu=accountancy_admin", $langs->trans("Fiscalyear"),1,$user->rights->accounting->fiscalyear, '', $mainmenu, 'fiscalyear'); } // Accountancy (simple) From e6a95b2af9792a604dca55f06deba6a480f648e4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 14:28:37 +0200 Subject: [PATCH 663/834] Clean code --- htdocs/supplier_proposal/card.php | 52 ++++------------------ htdocs/supplier_proposal/list.php | 73 ++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 58 deletions(-) diff --git a/htdocs/supplier_proposal/card.php b/htdocs/supplier_proposal/card.php index 9101b6efda2..78bde5d1ea5 100644 --- a/htdocs/supplier_proposal/card.php +++ b/htdocs/supplier_proposal/card.php @@ -108,8 +108,14 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e if (empty($reshook)) { - include DOL_DOCUMENT_ROOT . '/core/actions_setnotes.inc.php'; // Must be include, not includ_once - + if ($cancel) $action=''; + + include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not include_once + + include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once + + include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be include, not include_once + // Action clone object if ($action == 'confirm_clone' && $confirm == 'yes') { @@ -952,47 +958,7 @@ if (empty($reshook)) $result = $object->setMulticurrencyRate(GETPOST('multicurrency_tx', 'int')); } - /* - * Ordonnancement des lignes - */ - - else if ($action == 'up' && $user->rights->supplier_proposal->creer) { - $object->line_up(GETPOST('rowid')); - - if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) { - // Define output language - $outputlangs = $langs; - if (! empty($conf->global->MAIN_MULTILANGS)) { - $outputlangs = new Translate("", $conf); - $newlang = (GETPOST('lang_id') ? GETPOST('lang_id') : $object->thirdparty->default_lang); - $outputlangs->setDefaultLang($newlang); - } - $ret = $object->fetch($id); // Reload to get new records - $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id . '#' . GETPOST('rowid')); - exit(); - } - - else if ($action == 'down' && $user->rights->supplier_proposal->creer) { - $object->line_down(GETPOST('rowid')); - - if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) { - // Define output language - $outputlangs = $langs; - if (! empty($conf->global->MAIN_MULTILANGS)) { - $outputlangs = new Translate("", $conf); - $newlang = (GETPOST('lang_id') ? GETPOST('lang_id') : $object->thirdparty->default_lang); - $outputlangs->setDefaultLang($newlang); - } - $ret = $object->fetch($id); // Reload to get new records - $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id . '#' . GETPOST('rowid')); - exit(); - } else if ($action == 'update_extras') { + else if ($action == 'update_extras') { // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); diff --git a/htdocs/supplier_proposal/list.php b/htdocs/supplier_proposal/list.php index 30c56182380..ea9b68d30be 100644 --- a/htdocs/supplier_proposal/list.php +++ b/htdocs/supplier_proposal/list.php @@ -89,21 +89,6 @@ $pagenext = $page + 1; if (! $sortfield) $sortfield='p.date_livraison'; if (! $sortorder) $sortorder='DESC'; -if (GETPOST("button_removefilter") || GETPOST("button_removefilter_x")) // Both tests are required to be compatible with all browsers -{ - $search_categ=''; - $search_user=''; - $search_sale=''; - $search_ref=''; - $search_societe=''; - $search_montant_ht=''; - $search_author=''; - $year=''; - $month=''; - $viewstatut=''; - $object_statut=''; -} - if($object_statut != '') $viewstatut=$object_statut; @@ -127,11 +112,69 @@ $hookmanager->initHooks(array('supplier_proposallist')); * Actions */ +if (GETPOST('cancel')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction')) { $massaction=''; } + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers +{ + $search_categ=''; + $search_user=''; + $search_sale=''; + $search_ref=''; + $search_societe=''; + $search_montant_ht=''; + $search_author=''; + $year=''; + $month=''; + $viewstatut=''; + $object_statut=''; +} $parameters=array('socid'=>$socid); $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +if (empty($reshook)) +{ + // Mass actions. Controls on number of lines checked + $maxformassaction=1000; + if (! empty($massaction) && count($toselect) < 1) + { + $error++; + setEventMessages($langs->trans("NoLineChecked"), null, "warnings"); + } + if (! $error && count($toselect) > $maxformassaction) + { + setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors'); + $error++; + } + + // Action to delete + /* + if ($action == 'confirm_delete') + { + $result=$object->delete($user); + if ($result > 0) + { + // Delete OK + setEventMessages("RecordDeleted", null, 'mesgs'); + header("Location: ".dol_buildpath('/mymodule/list.php',1)); + exit; + } + else + { + if (! empty($object->errors)) setEventMessages(null,$object->errors,'errors'); + else setEventMessages($object->error,null,'errors'); + } + }*/ +} + /* From 740b6819c73adc4c0373ff6e5b2ba425eb49a2e1 Mon Sep 17 00:00:00 2001 From: philippe grand Date: Fri, 13 May 2016 17:49:16 +0200 Subject: [PATCH 664/834] add phone pro search within member list --- htdocs/adherents/list.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index f9e25dd5bc0..ab9ac5aca1b 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -49,6 +49,7 @@ $search_zip=GETPOST("search_zip"); $search_town=GETPOST("search_town"); $search_state=GETPOST("search_state"); $search_country=GETPOST("search_country"); +$search_phone=GETPOST("search_phone"); $type=GETPOST("type"); $search_email=GETPOST("search_email"); $search_categ = GETPOST("search_categ",'int'); @@ -104,6 +105,7 @@ $arrayfields=array( 'd.address'=>array('label'=>$langs->trans("Address"), 'checked'=>0), 'd.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0), 'd.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0), + 'd.phone'=>array('label'=>$langs->trans("Phone"), 'checked'=>0), 'state.nom'=>array('label'=>$langs->trans("State"), 'checked'=>0), 'country.code_iso'=>array('label'=>$langs->trans("Country"), 'checked'=>0), /*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0), @@ -151,6 +153,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_town=""; $search_state=""; $search_country=''; + $search_phone=''; $search_morphy=""; $search_categ=""; $catid=""; @@ -290,6 +293,7 @@ if ($resql) if ($search_zip != '') $param.= "&search_zip=".urlencode($search_zip); if ($search_state != '') $param.= "&search_state=".urlencode($search_state); if ($search_country != '') $param.= "&search_country=".urlencode($search_country); + if ($search_phone != '') $param.= "&search_phone=".urlencode($search_phone); if ($filter) $param.="&filter=".urlencode($filter); if ($type > 0) $param.="&type=".urlencode($type); if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); @@ -361,6 +365,7 @@ if ($resql) if (! empty($arrayfields['d.town']['checked'])) print_liste_field_titre($arrayfields['d.town']['label'],$_SERVER["PHP_SELF"],'d.town','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($langs->trans("StateShort"),$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder); if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($langs->trans("Country"),$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); + if (! empty($arrayfields['d.phone']['checked'])) print_liste_field_titre($arrayfields['d.phone']['label'],$_SERVER["PHP_SELF"],'d.phone','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.email']['checked'])) print_liste_field_titre($arrayfields['d.email']['label'],$_SERVER["PHP_SELF"],'d.email','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'align="center"',$sortfield,$sortorder); // Extra fields @@ -470,6 +475,12 @@ if ($resql) print $form->select_country($search_country,'search_country','',0,'maxwidth100'); print ''; } + // Phone pro + if (! empty($arrayfields['d.phone']['checked'])) + { + print ''; + print ''; + } // Email if (! empty($arrayfields['d.email']['checked'])) { @@ -656,6 +667,14 @@ if ($resql) print ''; if (! $i) $totalarray['nbfield']++; } + // Phone pro + if (! empty($arrayfields['d.phone']['checked'])) + { + print ''; + print $obj->phone; + print ''; + if (! $i) $totalarray['nbfield']++; + } // EMail if (! empty($arrayfields['d.email']['checked'])) { From 23cae18765249ab531c1ae512dba9e32ef89b7a9 Mon Sep 17 00:00:00 2001 From: philippe grand Date: Fri, 13 May 2016 19:06:28 +0200 Subject: [PATCH 665/834] add phone perso search within member list --- htdocs/adherents/list.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index ab9ac5aca1b..a50b3bd4a14 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -50,6 +50,7 @@ $search_town=GETPOST("search_town"); $search_state=GETPOST("search_state"); $search_country=GETPOST("search_country"); $search_phone=GETPOST("search_phone"); +$search_phone_perso=GETPOST("search_phone_perso"); $type=GETPOST("type"); $search_email=GETPOST("search_email"); $search_categ = GETPOST("search_categ",'int'); @@ -106,6 +107,7 @@ $arrayfields=array( 'd.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0), 'd.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0), 'd.phone'=>array('label'=>$langs->trans("Phone"), 'checked'=>0), + 'd.phone_perso'=>array('label'=>$langs->trans("PhonePerso"), 'checked'=>0), 'state.nom'=>array('label'=>$langs->trans("State"), 'checked'=>0), 'country.code_iso'=>array('label'=>$langs->trans("Country"), 'checked'=>0), /*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0), @@ -154,6 +156,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_state=""; $search_country=''; $search_phone=''; + $search_phone_perso=''; $search_morphy=""; $search_categ=""; $catid=""; @@ -294,6 +297,7 @@ if ($resql) if ($search_state != '') $param.= "&search_state=".urlencode($search_state); if ($search_country != '') $param.= "&search_country=".urlencode($search_country); if ($search_phone != '') $param.= "&search_phone=".urlencode($search_phone); + if ($search_phone_perso != '') $param.= "&search_phone_perso=".urlencode($search_phone_perso); if ($filter) $param.="&filter=".urlencode($filter); if ($type > 0) $param.="&type=".urlencode($type); if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); @@ -366,6 +370,7 @@ if ($resql) if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($langs->trans("StateShort"),$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder); if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($langs->trans("Country"),$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); if (! empty($arrayfields['d.phone']['checked'])) print_liste_field_titre($arrayfields['d.phone']['label'],$_SERVER["PHP_SELF"],'d.phone','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.phone_perso']['checked'])) print_liste_field_titre($arrayfields['d.phone_perso']['label'],$_SERVER["PHP_SELF"],'d.phone_perso','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.email']['checked'])) print_liste_field_titre($arrayfields['d.email']['label'],$_SERVER["PHP_SELF"],'d.email','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'align="center"',$sortfield,$sortorder); // Extra fields @@ -481,6 +486,12 @@ if ($resql) print ''; print ''; } + // Phone perso + if (! empty($arrayfields['d.phone_perso']['checked'])) + { + print ''; + print ''; + } // Email if (! empty($arrayfields['d.email']['checked'])) { @@ -573,7 +584,7 @@ if ($resql) } else { $companyname=$obj->company; } - +var_dump($obj);exit; $var=!$var; print ""; @@ -675,6 +686,14 @@ if ($resql) print ''; if (! $i) $totalarray['nbfield']++; } + // Phone perso + if (! empty($arrayfields['d.phone_perso']['checked'])) + { + print ''; + print $obj->phone_perso; + print ''; + if (! $i) $totalarray['nbfield']++; + } // EMail if (! empty($arrayfields['d.email']['checked'])) { From 2308d28c197f194c5941c36ae5ffb6bd91b6f182 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 19:23:56 +0200 Subject: [PATCH 666/834] FIX Add test code to detect/test autoload conflict --- .../restler/framework/Luracast/Restler/AutoLoader.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php index be9aa9c11df..bf3f24c9920 100644 --- a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php +++ b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php @@ -263,6 +263,12 @@ class AutoLoader * @return bool false unless className now exists */ private function loadLastResort($className, $loader = null) { + // DOL_LDR Add protection to avoid conflict with other autouploader + /*print 'Try to load '.$className."\n"; + if (in_array($className, array('Google_Client'))) + { + return false; + }*/ $loaders = array_unique(static::$rogueLoaders); if (isset($loader)) { if (false === array_search($loader, $loaders)) From 45bdcd282a2f86af5622e9ac3f63b3355bd6b00e Mon Sep 17 00:00:00 2001 From: philippe grand Date: Fri, 13 May 2016 19:24:55 +0200 Subject: [PATCH 667/834] add mobile search within member list --- htdocs/adherents/list.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/htdocs/adherents/list.php b/htdocs/adherents/list.php index a50b3bd4a14..5878aac2755 100644 --- a/htdocs/adherents/list.php +++ b/htdocs/adherents/list.php @@ -51,6 +51,7 @@ $search_state=GETPOST("search_state"); $search_country=GETPOST("search_country"); $search_phone=GETPOST("search_phone"); $search_phone_perso=GETPOST("search_phone_perso"); +$search_phone_mobile=GETPOST("search_phone_mobile"); $type=GETPOST("type"); $search_email=GETPOST("search_email"); $search_categ = GETPOST("search_categ",'int'); @@ -108,6 +109,7 @@ $arrayfields=array( 'd.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0), 'd.phone'=>array('label'=>$langs->trans("Phone"), 'checked'=>0), 'd.phone_perso'=>array('label'=>$langs->trans("PhonePerso"), 'checked'=>0), + 'd.phone_mobile'=>array('label'=>$langs->trans("PhoneMobile"), 'checked'=>0), 'state.nom'=>array('label'=>$langs->trans("State"), 'checked'=>0), 'country.code_iso'=>array('label'=>$langs->trans("Country"), 'checked'=>0), /*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0), @@ -157,6 +159,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $search_country=''; $search_phone=''; $search_phone_perso=''; + $search_phone_mobile=''; $search_morphy=""; $search_categ=""; $catid=""; @@ -298,6 +301,7 @@ if ($resql) if ($search_country != '') $param.= "&search_country=".urlencode($search_country); if ($search_phone != '') $param.= "&search_phone=".urlencode($search_phone); if ($search_phone_perso != '') $param.= "&search_phone_perso=".urlencode($search_phone_perso); + if ($search_phone_mobile != '') $param.= "&search_phone_mobile=".urlencode($search_phone_mobile); if ($filter) $param.="&filter=".urlencode($filter); if ($type > 0) $param.="&type=".urlencode($type); if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); @@ -371,6 +375,7 @@ if ($resql) if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($langs->trans("Country"),$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); if (! empty($arrayfields['d.phone']['checked'])) print_liste_field_titre($arrayfields['d.phone']['label'],$_SERVER["PHP_SELF"],'d.phone','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.phone_perso']['checked'])) print_liste_field_titre($arrayfields['d.phone_perso']['label'],$_SERVER["PHP_SELF"],'d.phone_perso','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['d.phone_mobile']['checked'])) print_liste_field_titre($arrayfields['d.phone_mobile']['label'],$_SERVER["PHP_SELF"],'d.phone_mobile','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.email']['checked'])) print_liste_field_titre($arrayfields['d.email']['label'],$_SERVER["PHP_SELF"],'d.email','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'align="center"',$sortfield,$sortorder); // Extra fields @@ -492,6 +497,12 @@ if ($resql) print ''; print ''; } + // Phone mobile + if (! empty($arrayfields['d.phone_mobile']['checked'])) + { + print ''; + print ''; + } // Email if (! empty($arrayfields['d.email']['checked'])) { @@ -584,7 +595,7 @@ if ($resql) } else { $companyname=$obj->company; } -var_dump($obj);exit; + $var=!$var; print ""; @@ -694,6 +705,14 @@ var_dump($obj);exit; print ''; if (! $i) $totalarray['nbfield']++; } + // Phone mobile + if (! empty($arrayfields['d.phone_mobile']['checked'])) + { + print ''; + print $obj->phone_mobile; + print ''; + if (! $i) $totalarray['nbfield']++; + } // EMail if (! empty($arrayfields['d.email']['checked'])) { From 8aebc37a5f0a35adb05184900282500d837ea92d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 19:55:49 +0200 Subject: [PATCH 668/834] FIX Error message --- htdocs/user/class/user.class.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 2e4f2a7351a..85c1e0e63da 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -917,7 +917,7 @@ class User extends CommonObject if (empty($this->login)) { $langs->load("errors"); - $this->error = $langs->trans("ErrorFieldRequired",$this->login); + $this->error = $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Login")); return -1; } @@ -929,7 +929,7 @@ class User extends CommonObject $sql = "SELECT login FROM ".MAIN_DB_PREFIX."user"; $sql.= " WHERE login ='".$this->db->escape($this->login)."'"; $sql.= " AND entity IN (0,".$this->db->escape($conf->entity).")"; - + dol_syslog(get_class($this)."::create", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) @@ -962,7 +962,7 @@ class User extends CommonObject $this->db->rollback(); return -5; } - + // Update minor fields $result = $this->update($user,1,1); if ($result < 0) @@ -982,12 +982,12 @@ class User extends CommonObject $entrepot->country_id = $mysoc->country_id; $entrepot->create($user); } - + if (! $notrigger) { // Call trigger $result=$this->call_trigger('USER_CREATE',$user); - if ($result < 0) { $error++; } + if ($result < 0) { $error++; } // End call triggers } From d1d1b824b4293e0a93231496860e3552cffd31df Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 20:20:48 +0200 Subject: [PATCH 669/834] Better coding of #5181. I removed need of external lib. --- htdocs/core/class/commonobject.class.php | 2 +- htdocs/core/class/interfaces.class.php | 20 +- htdocs/user/class/api_user.class.php | 124 ++++++++----- test/phpunit/AllTests.php | 3 + test/phpunit/RestAPIUserTest.php | 225 +++++++++++++++++++++++ 5 files changed, 324 insertions(+), 50 deletions(-) create mode 100644 test/phpunit/RestAPIUserTest.php diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 3ad080d8a55..3287ac5d4b7 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -3987,7 +3987,7 @@ abstract class CommonObject function call_trigger($trigger_name, $user) { global $langs,$conf; - + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; $interface=new Interfaces($this->db); $result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf); diff --git a/htdocs/core/class/interfaces.class.php b/htdocs/core/class/interfaces.class.php index 7b233a4fff8..1ac219029fd 100644 --- a/htdocs/core/class/interfaces.class.php +++ b/htdocs/core/class/interfaces.class.php @@ -77,7 +77,7 @@ class Interfaces $modules = array(); $orders = array(); $i=0; - + $dirtriggers=array_merge(array('/core/triggers'),$conf->modules_parts['triggers']); foreach($dirtriggers as $reldir) { @@ -100,9 +100,9 @@ class Interfaces $part3=$reg[3]; $nbfile++; - + $modName = "Interface".ucfirst($reg[3]); - //print "file=$file"; print "modName=$modName"; exit; + //print "file=$file - modName=$modName\n"; if (in_array($modName,$modules)) { $langs->load("errors"); @@ -111,9 +111,17 @@ class Interfaces } else { - include_once $newdir.'/'.$file; + try { + //print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n"; + include_once $newdir.'/'.$file; + //print 'Done for '.$modName."\n"; + } + catch(Exception $e) + { + dol_syslog('ko for '.$modName." ".$e->getMessage()."\n", LOG_ERROR); + } } - + // Check if trigger file is disabled by name if (preg_match('/NORUN$/i',$file)) continue; // Check if trigger file is for a particular module @@ -142,7 +150,7 @@ class Interfaces } asort($orders); - + // Loop on each trigger foreach ($orders as $key => $value) { diff --git a/htdocs/user/class/api_user.class.php b/htdocs/user/class/api_user.class.php index af0db5bfb2d..e89a5faac0b 100644 --- a/htdocs/user/class/api_user.class.php +++ b/htdocs/user/class/api_user.class.php @@ -96,36 +96,74 @@ class UserApi extends DolibarrApi function createFromContact($contactid, $request_data = NULL) { //if (!DolibarrApiAccess::$user->rights->user->user->creer) { //throw new RestException(401); - //} + //} + + if (!isset($request_data["login"])) + throw new RestException(400, "login field missing"); + if (!isset($request_data["password"])) + throw new RestException(400, "password field missing"); + if (!DolibarrApiAccess::$user->rights->societe->contact->lire) { + throw new RestException(401); + } + $contact = new Contact($this->db); + $contact->fetch($contactid); + if ($contact->id <= 0) { + throw new RestException(404, 'Contact not found'); + } - if (!isset($request_data["login"])) - throw new RestException(400, "login field missing"); - if (!isset($request_data["password"])) - throw new RestException(400, "password field missing"); - if (!DolibarrApiAccess::$user->rights->societe->contact->lire) { - throw new RestException(401); - } - $contact = new Contact($this->db); - $contact->fetch($contactid); - if ($contact->id <= 0) { - throw new RestException(404, 'Contact not found'); - } - - if (!DolibarrApi::_checkAccessToResource('contact', $contact->id, 'socpeople&societe')) { - throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login); - } - // Check mandatory fields - $login = $request_data["login"]; - $password = $request_data["password"]; - $result = $this->useraccount->create_from_contact($contact,$login,$password); - if ($result <= 0) { - throw new RestException(500, "User not created"); - } - // password parameter not used in create_from_contact - $this->useraccount->setPassword($this->useraccount,$password); - return $result; + if (!DolibarrApi::_checkAccessToResource('contact', $contact->id, 'socpeople&societe')) { + throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login); + } + // Check mandatory fields + $login = $request_data["login"]; + $password = $request_data["password"]; + $result = $this->useraccount->create_from_contact($contact,$login,$password); + if ($result <= 0) { + throw new RestException(500, "User not created"); + } + // password parameter not used in create_from_contact + $this->useraccount->setPassword($this->useraccount,$password); + + return $result; } - + + + /** + * Create user account + * + * @param array $request_data New user data + * @return int + * + * @url POST user/ + */ + function post($request_data = NULL) { + // check user authorization + //if(! DolibarrApiAccess::$user->rights->user->creer) { + // throw new RestException(401, "User creation not allowed"); + //} + // check mandatory fields + /*if (!isset($request_data["login"])) + throw new RestException(400, "login field missing"); + if (!isset($request_data["password"])) + throw new RestException(400, "password field missing"); + if (!isset($request_data["lastname"])) + throw new RestException(400, "lastname field missing");*/ + //assign field values + $xxx=var_export($request_data, true); + dol_syslog("xxx=".$xxx); + foreach ($request_data as $field => $value) + { + $this->useraccount->$field = $value; + } + + $result = $this->useraccount->create(DolibarrApiAccess::$user); + if ($result <=0) { + throw new RestException(500, "User not created : ".$this->useraccount->error); + } + return array('id'=>$result); + } + + /** * Update account * @@ -159,10 +197,10 @@ class UserApi extends DolibarrApi if ($this->useraccount->update($id, DolibarrApiAccess::$user, 1, '', '', 'update')) return $this->get($id); - return false; - } + return false; + } - /** + /** * add user to group * * @param int $id User ID @@ -175,20 +213,20 @@ class UserApi extends DolibarrApi //if (!DolibarrApiAccess::$user->rights->user->user->supprimer) { //throw new RestException(401); //} - $result = $this->useraccount->fetch($id); - if (!$result) - { - throw new RestException(404, 'User not found'); + $result = $this->useraccount->fetch($id); + if (!$result) + { + throw new RestException(404, 'User not found'); + } + + if (!DolibarrApi::_checkAccessToResource('user', $this->useraccount->id, 'user')) + { + throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login); + } + + return $this->useraccount->SetInGroup($group,1); } - if (!DolibarrApi::_checkAccessToResource('user', $this->useraccount->id, 'user')) - { - throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login); - } - - return $this->useraccount->SetInGroup($group,1); - } - /** * Delete account * diff --git a/test/phpunit/AllTests.php b/test/phpunit/AllTests.php index 8dd9941d8b4..2dfdf4cb5c2 100644 --- a/test/phpunit/AllTests.php +++ b/test/phpunit/AllTests.php @@ -184,6 +184,9 @@ class AllTests require_once dirname(__FILE__).'/CategorieTest.php'; $suite->addTestSuite('CategorieTest'); + require_once dirname(__FILE__).'/RestAPIUserTest.php'; + $suite->addTestSuite('RestAPIUserTest'); + require_once dirname(__FILE__).'/WebservicesProductsTest.php'; $suite->addTestSuite('WebservicesProductsTest'); require_once dirname(__FILE__).'/WebservicesInvoicesTest.php'; diff --git a/test/phpunit/RestAPIUserTest.php b/test/phpunit/RestAPIUserTest.php new file mode 100644 index 00000000000..466c3cab782 --- /dev/null +++ b/test/phpunit/RestAPIUserTest.php @@ -0,0 +1,225 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see http://www.gnu.org/ + */ + +/** + * \file test/phpunit/RestAPIUserTest.php + * \ingroup test + * \brief PHPUnit test + * \remarks To run this script as CLI: phpunit filename.php + */ + +global $conf,$user,$langs,$db; +//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver +//require_once 'PHPUnit/Autoload.php'; +require_once dirname(__FILE__).'/../../htdocs/master.inc.php'; +require_once dirname(__FILE__).'/../../htdocs/core/lib/date.lib.php'; +require_once dirname(__FILE__).'/../../htdocs/core/lib/geturl.lib.php'; + + +if (empty($user->id)) { + print "Load permissions for admin user nb 1\n"; + $user->fetch(1); + $user->getrights(); +} +$conf->global->MAIN_DISABLE_ALL_MAILS=1; +$conf->global->MAIN_UMASK='0666'; + + +/** + * Class for PHPUnit tests + * + * @backupGlobals disabled + * @backupStaticAttributes enabled + * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased. + */ +class RestAPIUserTest extends PHPUnit_Framework_TestCase +{ + protected $savconf; + protected $savuser; + protected $savlangs; + protected $savdb; + protected $api_url; + protected $api_key; + + /** + * Constructor + * We save global variables into local variables + * + * @return DateLibTest + */ + function __construct() + { + //$this->sharedFixture + global $conf,$user,$langs,$db; + $this->savconf=$conf; + $this->savuser=$user; + $this->savlangs=$langs; + $this->savdb=$db; + + print __METHOD__." db->type=".$db->type." user->id=".$user->id; + //print " - db ".$db->db; + print "\n"; + } + + // Static methods + public static function setUpBeforeClass() + { + global $conf,$user,$langs,$db; + $db->begin(); // This is to have all actions inside a transaction even if test launched without suite. + + print __METHOD__."\n"; + } + + // tear down after class + public static function tearDownAfterClass() + { + global $conf,$user,$langs,$db; + $db->rollback(); + + print __METHOD__."\n"; + } + + /** + * Init phpunit tests + * + * @return void + */ + protected function setUp() + { + global $conf,$user,$langs,$db; + $conf=$this->savconf; + $user=$this->savuser; + $langs=$this->savlangs; + $db=$this->savdb; + + $this->api_url=DOL_MAIN_URL_ROOT.'/api/index.php'; + + $login='admin'; + $password='admin'; + $url=$this->api_url.'/login?login='.$login.'&password='.$password; + // Call the API login method to save api_key for this test class + $result=getURLContent($url, 'GET', '', 1, array()); + //print __METHOD__." result = ".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertEquals('200', $object['success']['code']); + + $this->api_key = $object['success']['token']; + print __METHOD__." api_key: $this->api_key \n"; + + print __METHOD__."\n"; + } + + /** + * End phpunit tests + * + * @return void + */ + protected function tearDown() + { + print __METHOD__."\n"; + } + + + /** + * testRestGetUser + * + * @return int + */ + public function testRestGetUser() + { + global $conf,$user,$langs,$db; + + $url = $this->api_url.'/user/123456789?api_key='.$this->api_key; + //$addheaders=array('Content-Type: application/json'); + + print __METHOD__." Request url=".$url."\n"; + $result=getURLContent($url, 'GET', '', 1, array()); + //print __METHOD__." Result for unexisting user: ".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertEquals(404, $object['error']['code']); + + $url = $this->api_url.'/user/1?api_key='.$this->api_key; + + print __METHOD__." Request url=".$url."\n"; + $result=getURLContent($url, 'GET', '', 1, array()); + //print __METHOD__." Result for existing user user: ".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertEquals(1, $object['statut']); + } + + public function testRestCreateUser() { + + // attemp to create without mandatory fields : + $url = $this->api_url.'/user?api_key='.$this->api_key; + $addheaders=array('Content-Type: application/json'); + + $bodyobj = array( + "lastname"=>"testRestUser", + "password"=>"testRestPassword", + "email"=>"test@restuser.com" + ); + $body = json_encode($bodyobj); + + print __METHOD__." Request url=".$url."\n"; + $result=getURLContent($url, 'POST', $body, 1, $addheaders); + //print __METHOD__." Result for creating incomplete user".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertEquals(500, $object['error']['code'], $object['error']['code'].' '.$object['error']['message']); + + // create regular user + unset($result); + $bodyobj = array( + "login"=>"testRestLogin".mt_rand(), + "lastname"=>"testRestUser", + "password"=>"testRestPassword", + "email"=>"test@restuser.com" + ); + $body = json_encode($bodyobj); + print __METHOD__." Request url=".$url."\n"; + $result=getURLContent($url, 'POST', $body, 1, $addheaders); + print __METHOD__." Result code for creating user ".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertGreaterThan(0, $object['id'], $object['error']['code'].' '.$object['error']['message']); + + // attempt to create duplicated user + print __METHOD__." Request url=".$url."\n"; + $result=getURLContent($url, 'POST', $body, 1, $addheaders); + //print __METHOD__." Result for creating duplicate user".var_export($result, true)."\n"; + print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; + $this->assertEquals($result['curl_error_no'],''); + $object=json_decode($result['content'], true); + $this->assertNotNull($object, "Parsing of json result must no be null"); + $this->assertEquals(500, $object['error']['code'], $object['error']['code'].' '.$object['error']['message']); + } + +} \ No newline at end of file From 6a0f16e9aaf701309d4fc3da301f36c7f2b8a769 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 20:39:31 +0200 Subject: [PATCH 670/834] FIX #5189 --- htdocs/product/card.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 92d8a7f4288..d9863792546 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1435,7 +1435,7 @@ else print ''; - if (($action != 'editbarcodetype') && ! empty($user->rights->barcode->creer)) print ''; + if (($action != 'editbarcodetype') && ! empty($user->rights->produit->creer) && $createbarcode) print ''; print '
    '; print $langs->trans("BarcodeType"); print 'id.'">'.img_edit($langs->trans('Edit'),1).'id.'">'.img_edit($langs->trans('Edit'),1).'
    '; print ''; if ($action == 'editbarcodetype') @@ -1456,7 +1456,7 @@ else print ''; - if (($action != 'editbarcode') && ! empty($user->rights->barcode->creer)) print ''; + if (($action != 'editbarcode') && ! empty($user->rights->produit->creer) && $createbarcode) print ''; print '
    '; print $langs->trans("BarcodeValue"); print 'id.'">'.img_edit($langs->trans('Edit'),1).'id.'">'.img_edit($langs->trans('Edit'),1).'
    '; print ''; if ($action == 'editbarcode') From 1bba8166f8c2dd69f45899f684f0921a7c86f9fd Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 23:12:53 +0200 Subject: [PATCH 671/834] FIX #5207 --- htdocs/fourn/facture/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index f1de6a68a78..635c9195386 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -240,7 +240,7 @@ if (empty($reshook)) { $object->ref_supplier = GETPOST('ref_supplier', 'alpha'); - if ($object->update() < 0) { + if ($object->update($user) < 0) { setEventMessages($object->error, $object->errors, 'errors'); } } From af38d038252978540d85e578c44f67866415b0fa Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 13 May 2016 23:22:39 +0200 Subject: [PATCH 672/834] Clean copyright and libs --- COPYRIGHT | 2 +- htdocs/includes/pdfmake/pdfmake.js | 68149 ----------------------- htdocs/includes/pdfmake/pdfmake.min.js | 17 - htdocs/includes/pdfmake/vfs_fonts.js | 1 - 4 files changed, 1 insertion(+), 68168 deletions(-) delete mode 100644 htdocs/includes/pdfmake/pdfmake.js delete mode 100644 htdocs/includes/pdfmake/pdfmake.min.js delete mode 100644 htdocs/includes/pdfmake/vfs_fonts.js diff --git a/COPYRIGHT b/COPYRIGHT index 99d5a189f99..b8cf93e595a 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -27,7 +27,7 @@ PHPExcel 1.8.1 LGPL-2.1+ Yes php-iban 1.4.7 LGPL-3+ Yes Parse and validate IBAN (and IIBAN) bank account information in PHP PHPoAuthLib 0.8.2 MIT License Yes Library to provide oauth1 and oauth2 to different service PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests -Restler 3.0 LGPL-3+ Yes Library to develop REST Web services +Restler 3.0.0RC6 LGPL-3+ Yes Library to develop REST Web services TCPDF 6.2.12 LGPL-3+ Yes PDF generation TCPDI 1.0.0 LGPL-3+ / Apache 2.0 Yes FPDI replacement Swift Mailer 5.4.2-DEV MIT license Yes Comprehensive mailing tools for PHP diff --git a/htdocs/includes/pdfmake/pdfmake.js b/htdocs/includes/pdfmake/pdfmake.js deleted file mode 100644 index 6e458e51cb0..00000000000 --- a/htdocs/includes/pdfmake/pdfmake.js +++ /dev/null @@ -1,68149 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(global) {module.exports = global["pdfMake"] = __webpack_require__(1); - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(Buffer) {/* jslint node: true */ - /* jslint browser: true */ - /* global BlobBuilder */ - 'use strict'; - - var PdfPrinter = __webpack_require__(6); - var FileSaver = __webpack_require__(105); - var saveAs = FileSaver.saveAs; - - var defaultClientFonts = { - Roboto: { - normal: 'Roboto-Regular.ttf', - bold: 'Roboto-Medium.ttf', - italics: 'Roboto-Italic.ttf', - bolditalics: 'Roboto-Italic.ttf' - } - }; - - function Document(docDefinition, fonts, vfs) { - this.docDefinition = docDefinition; - this.fonts = fonts || defaultClientFonts; - this.vfs = vfs; - } - - Document.prototype._createDoc = function(options, callback) { - var printer = new PdfPrinter(this.fonts); - printer.fs.bindFS(this.vfs); - - var doc = printer.createPdfKitDocument(this.docDefinition, options); - var chunks = []; - var result; - - doc.on('data', function(chunk) { - chunks.push(chunk); - }); - doc.on('end', function() { - result = Buffer.concat(chunks); - callback(result, doc._pdfMakePages); - }); - doc.end(); - }; - - Document.prototype._getPages = function(options, cb){ - if (!cb) throw 'getBuffer is an async method and needs a callback argument'; - this._createDoc(options, function(ignoreBuffer, pages){ - cb(pages); - }); - }; - - Document.prototype.open = function(message) { - // we have to open the window immediately and store the reference - // otherwise popup blockers will stop us - var win = window.open('', '_blank'); - - try { - this.getDataUrl(function(result) { - win.location.href = result; - }); - } catch(e) { - win.close(); - throw e; - } - }; - - - Document.prototype.print = function() { - this.getDataUrl(function(dataUrl) { - var iFrame = document.createElement('iframe'); - iFrame.style.position = 'absolute'; - iFrame.style.left = '-99999px'; - iFrame.src = dataUrl; - iFrame.onload = function() { - function removeIFrame(){ - document.body.removeChild(iFrame); - document.removeEventListener('click', removeIFrame); - } - document.addEventListener('click', removeIFrame, false); - }; - - document.body.appendChild(iFrame); - }, { autoPrint: true }); - }; - - Document.prototype.download = function(defaultFileName, cb) { - if(typeof defaultFileName === "function") { - cb = defaultFileName; - defaultFileName = null; - } - - defaultFileName = defaultFileName || 'file.pdf'; - this.getBuffer(function (result) { - var blob; - try { - blob = new Blob([result], { type: 'application/pdf' }); - } - catch (e) { - // Old browser which can't handle it without making it an byte array (ie10) - if (e.name == "InvalidStateError") { - var byteArray = new Uint8Array(result); - blob = new Blob([byteArray.buffer], { type: 'application/pdf' }); - } - } - if (blob) { - saveAs(blob, defaultFileName); - } - else { - throw 'Could not generate blob'; - } - if (typeof cb === "function") { - cb(); - } - }); - }; - - Document.prototype.getBase64 = function(cb, options) { - if (!cb) throw 'getBase64 is an async method and needs a callback argument'; - this._createDoc(options, function(buffer) { - cb(buffer.toString('base64')); - }); - }; - - Document.prototype.getDataUrl = function(cb, options) { - if (!cb) throw 'getDataUrl is an async method and needs a callback argument'; - this._createDoc(options, function(buffer) { - cb('data:application/pdf;base64,' + buffer.toString('base64')); - }); - }; - - Document.prototype.getBuffer = function(cb, options) { - if (!cb) throw 'getBuffer is an async method and needs a callback argument'; - this._createDoc(options, function(buffer){ - cb(buffer); - }); - }; - - module.exports = { - createPdf: function(docDefinition) { - return new Document(docDefinition, window.pdfMake.fonts, window.pdfMake.vfs); - } - }; - - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2).Buffer)) - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(Buffer, global) {/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ - /* eslint-disable no-proto */ - - var base64 = __webpack_require__(3) - var ieee754 = __webpack_require__(4) - var isArray = __webpack_require__(5) - - exports.Buffer = Buffer - exports.SlowBuffer = SlowBuffer - exports.INSPECT_MAX_BYTES = 50 - Buffer.poolSize = 8192 // not used by this implementation - - var rootParent = {} - - /** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Use Object implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * Due to various browser bugs, sometimes the Object implementation will be used even - * when the browser supports typed arrays. - * - * Note: - * - * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, - * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. - * - * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property - * on objects. - * - * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. - * - * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of - * incorrect length in some situations. - - * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they - * get the Object implementation, which is slower but behaves correctly. - */ - Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined - ? global.TYPED_ARRAY_SUPPORT - : typedArraySupport() - - function typedArraySupport () { - function Bar () {} - try { - var arr = new Uint8Array(1) - arr.foo = function () { return 42 } - arr.constructor = Bar - return arr.foo() === 42 && // typed array instances can be augmented - arr.constructor === Bar && // constructor can be set - typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` - arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` - } catch (e) { - return false - } - } - - function kMaxLength () { - return Buffer.TYPED_ARRAY_SUPPORT - ? 0x7fffffff - : 0x3fffffff - } - - /** - * Class: Buffer - * ============= - * - * The Buffer constructor returns instances of `Uint8Array` that are augmented - * with function properties for all the node `Buffer` API functions. We use - * `Uint8Array` so that square bracket notation works as expected -- it returns - * a single octet. - * - * By augmenting the instances, we can avoid modifying the `Uint8Array` - * prototype. - */ - function Buffer (arg) { - if (!(this instanceof Buffer)) { - // Avoid going through an ArgumentsAdaptorTrampoline in the common case. - if (arguments.length > 1) return new Buffer(arg, arguments[1]) - return new Buffer(arg) - } - - this.length = 0 - this.parent = undefined - - // Common case. - if (typeof arg === 'number') { - return fromNumber(this, arg) - } - - // Slightly less common case. - if (typeof arg === 'string') { - return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') - } - - // Unusual. - return fromObject(this, arg) - } - - function fromNumber (that, length) { - that = allocate(that, length < 0 ? 0 : checked(length) | 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) { - for (var i = 0; i < length; i++) { - that[i] = 0 - } - } - return that - } - - function fromString (that, string, encoding) { - if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' - - // Assumption: byteLength() return value is always < kMaxLength. - var length = byteLength(string, encoding) | 0 - that = allocate(that, length) - - that.write(string, encoding) - return that - } - - function fromObject (that, object) { - if (Buffer.isBuffer(object)) return fromBuffer(that, object) - - if (isArray(object)) return fromArray(that, object) - - if (object == null) { - throw new TypeError('must start with number, buffer, array or string') - } - - if (typeof ArrayBuffer !== 'undefined') { - if (object.buffer instanceof ArrayBuffer) { - return fromTypedArray(that, object) - } - if (object instanceof ArrayBuffer) { - return fromArrayBuffer(that, object) - } - } - - if (object.length) return fromArrayLike(that, object) - - return fromJsonObject(that, object) - } - - function fromBuffer (that, buffer) { - var length = checked(buffer.length) | 0 - that = allocate(that, length) - buffer.copy(that, 0, 0, length) - return that - } - - function fromArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that - } - - // Duplicate of fromArray() to keep fromArray() monomorphic. - function fromTypedArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - // Truncating the elements is probably not what people expect from typed - // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior - // of the old Buffer constructor. - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that - } - - function fromArrayBuffer (that, array) { - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - array.byteLength - that = Buffer._augment(new Uint8Array(array)) - } else { - // Fallback: Return an object instance of the Buffer class - that = fromTypedArray(that, new Uint8Array(array)) - } - return that - } - - function fromArrayLike (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that - } - - // Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. - // Returns a zero-length buffer for inputs that don't conform to the spec. - function fromJsonObject (that, object) { - var array - var length = 0 - - if (object.type === 'Buffer' && isArray(object.data)) { - array = object.data - length = checked(array.length) | 0 - } - that = allocate(that, length) - - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that - } - - if (Buffer.TYPED_ARRAY_SUPPORT) { - Buffer.prototype.__proto__ = Uint8Array.prototype - Buffer.__proto__ = Uint8Array - } - - function allocate (that, length) { - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - that = Buffer._augment(new Uint8Array(length)) - that.__proto__ = Buffer.prototype - } else { - // Fallback: Return an object instance of the Buffer class - that.length = length - that._isBuffer = true - } - - var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 - if (fromPool) that.parent = rootParent - - return that - } - - function checked (length) { - // Note: cannot use `length < kMaxLength` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= kMaxLength()) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + kMaxLength().toString(16) + ' bytes') - } - return length | 0 - } - - function SlowBuffer (subject, encoding) { - if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) - - var buf = new Buffer(subject, encoding) - delete buf.parent - return buf - } - - Buffer.isBuffer = function isBuffer (b) { - return !!(b != null && b._isBuffer) - } - - Buffer.compare = function compare (a, b) { - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError('Arguments must be Buffers') - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - var i = 0 - var len = Math.min(x, y) - while (i < len) { - if (a[i] !== b[i]) break - - ++i - } - - if (i !== len) { - x = a[i] - y = b[i] - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 - } - - Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'binary': - case 'base64': - case 'raw': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } - } - - Buffer.concat = function concat (list, length) { - if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') - - if (list.length === 0) { - return new Buffer(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; i++) { - length += list[i].length - } - } - - var buf = new Buffer(length) - var pos = 0 - for (i = 0; i < list.length; i++) { - var item = list[i] - item.copy(buf, pos) - pos += item.length - } - return buf - } - - function byteLength (string, encoding) { - if (typeof string !== 'string') string = '' + string - - var len = string.length - if (len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'binary': - // Deprecated - case 'raw': - case 'raws': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) return utf8ToBytes(string).length // assume utf8 - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } - } - Buffer.byteLength = byteLength - - // pre-set for values that may exist in the future - Buffer.prototype.length = undefined - Buffer.prototype.parent = undefined - - function slowToString (encoding, start, end) { - var loweredCase = false - - start = start | 0 - end = end === undefined || end === Infinity ? this.length : end | 0 - - if (!encoding) encoding = 'utf8' - if (start < 0) start = 0 - if (end > this.length) end = this.length - if (end <= start) return '' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'binary': - return binarySlice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } - } - - Buffer.prototype.toString = function toString () { - var length = this.length | 0 - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) - } - - Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 - } - - Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - if (this.length > 0) { - str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') - if (this.length > max) str += ' ... ' - } - return '' - } - - Buffer.prototype.compare = function compare (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return 0 - return Buffer.compare(this, b) - } - - Buffer.prototype.indexOf = function indexOf (val, byteOffset) { - if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff - else if (byteOffset < -0x80000000) byteOffset = -0x80000000 - byteOffset >>= 0 - - if (this.length === 0) return -1 - if (byteOffset >= this.length) return -1 - - // Negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) - - if (typeof val === 'string') { - if (val.length === 0) return -1 // special case: looking for empty string always fails - return String.prototype.indexOf.call(this, val, byteOffset) - } - if (Buffer.isBuffer(val)) { - return arrayIndexOf(this, val, byteOffset) - } - if (typeof val === 'number') { - if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { - return Uint8Array.prototype.indexOf.call(this, val, byteOffset) - } - return arrayIndexOf(this, [ val ], byteOffset) - } - - function arrayIndexOf (arr, val, byteOffset) { - var foundIndex = -1 - for (var i = 0; byteOffset + i < arr.length; i++) { - if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex - } else { - foundIndex = -1 - } - } - return -1 - } - - throw new TypeError('val must be string, number or Buffer') - } - - // `get` is deprecated - Buffer.prototype.get = function get (offset) { - console.log('.get() is deprecated. Access using array indexes instead.') - return this.readUInt8(offset) - } - - // `set` is deprecated - Buffer.prototype.set = function set (v, offset) { - console.log('.set() is deprecated. Access using array indexes instead.') - return this.writeUInt8(v, offset) - } - - function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - // must be an even number of digits - var strLen = string.length - if (strLen % 2 !== 0) throw new Error('Invalid hex string') - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; i++) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (isNaN(parsed)) throw new Error('Invalid hex string') - buf[offset + i] = parsed - } - return i - } - - function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) - } - - function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) - } - - function binaryWrite (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) - } - - function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) - } - - function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) - } - - Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset | 0 - if (isFinite(length)) { - length = length | 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - // legacy write(string, encoding, offset, length) - remove in v0.13 - } else { - var swap = encoding - encoding = offset - offset = length | 0 - length = swap - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'binary': - return binaryWrite(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } - } - - Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } - } - - function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } - } - - function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) - } - - // Based on http://stackoverflow.com/a/22747272/680742, the browser with - // the lowest limit is Chrome, with 0x10000 args. - // We go 1 magnitude less, for safety - var MAX_ARGUMENTS_LENGTH = 0x1000 - - function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res - } - - function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; i++) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret - } - - function binarySlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; i++) { - ret += String.fromCharCode(buf[i]) - } - return ret - } - - function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; i++) { - out += toHex(buf[i]) - } - return out - } - - function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) - } - return res - } - - Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf - if (Buffer.TYPED_ARRAY_SUPPORT) { - newBuf = Buffer._augment(this.subarray(start, end)) - } else { - var sliceLen = end - start - newBuf = new Buffer(sliceLen, undefined) - for (var i = 0; i < sliceLen; i++) { - newBuf[i] = this[i + start] - } - } - - if (newBuf.length) newBuf.parent = this.parent || this - - return newBuf - } - - /* - * Need to make sure that buffer isn't trying to write out of bounds. - */ - function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') - } - - Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val - } - - Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val - } - - Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] - } - - Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) - } - - Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] - } - - Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) - } - - Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) - } - - Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val - } - - Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val - } - - Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) - } - - Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val - } - - Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val - } - - Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) - } - - Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) - } - - Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) - } - - Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) - } - - Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) - } - - Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) - } - - function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') - if (value > max || value < min) throw new RangeError('value is out of bounds') - if (offset + ext > buf.length) throw new RangeError('index out of range') - } - - Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength - } - - Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - byteLength = byteLength | 0 - if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength - } - - Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) - this[offset] = (value & 0xff) - return offset + 1 - } - - function objectWriteUInt16 (buf, value, offset, littleEndian) { - if (value < 0) value = 0xffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { - buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> - (littleEndian ? i : 1 - i) * 8 - } - } - - Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - } else { - objectWriteUInt16(this, value, offset, true) - } - return offset + 2 - } - - Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - } else { - objectWriteUInt16(this, value, offset, false) - } - return offset + 2 - } - - function objectWriteUInt32 (buf, value, offset, littleEndian) { - if (value < 0) value = 0xffffffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { - buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff - } - } - - Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, true) - } - return offset + 4 - } - - Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, false) - } - return offset + 4 - } - - Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) { - var limit = Math.pow(2, 8 * byteLength - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = value < 0 ? 1 : 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength - } - - Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) { - var limit = Math.pow(2, 8 * byteLength - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = value < 0 ? 1 : 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength - } - - Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 - } - - Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - } else { - objectWriteUInt16(this, value, offset, true) - } - return offset + 2 - } - - Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - } else { - objectWriteUInt16(this, value, offset, false) - } - return offset + 2 - } - - Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - } else { - objectWriteUInt32(this, value, offset, true) - } - return offset + 4 - } - - Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset | 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - } else { - objectWriteUInt32(this, value, offset, false) - } - return offset + 4 - } - - function checkIEEE754 (buf, value, offset, ext, max, min) { - if (value > max || value < min) throw new RangeError('value is out of bounds') - if (offset + ext > buf.length) throw new RangeError('index out of range') - if (offset < 0) throw new RangeError('index out of range') - } - - function writeFloat (buf, value, offset, littleEndian, noAssert) { - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 - } - - Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) - } - - Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) - } - - function writeDouble (buf, value, offset, littleEndian, noAssert) { - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 - } - - Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) - } - - Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) - } - - // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) - Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - var i - - if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (i = len - 1; i >= 0; i--) { - target[i + targetStart] = this[i + start] - } - } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { - // ascending copy from start - for (i = 0; i < len; i++) { - target[i + targetStart] = this[i + start] - } - } else { - target._set(this.subarray(start, start + len), targetStart) - } - - return len - } - - // fill(value, start=0, end=buffer.length) - Buffer.prototype.fill = function fill (value, start, end) { - if (!value) value = 0 - if (!start) start = 0 - if (!end) end = this.length - - if (end < start) throw new RangeError('end < start') - - // Fill 0 bytes; we're done - if (end === start) return - if (this.length === 0) return - - if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') - if (end < 0 || end > this.length) throw new RangeError('end out of bounds') - - var i - if (typeof value === 'number') { - for (i = start; i < end; i++) { - this[i] = value - } - } else { - var bytes = utf8ToBytes(value.toString()) - var len = bytes.length - for (i = start; i < end; i++) { - this[i] = bytes[i % len] - } - } - - return this - } - - /** - * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. - * Added in Node 0.12. Only available in browsers that support ArrayBuffer. - */ - Buffer.prototype.toArrayBuffer = function toArrayBuffer () { - if (typeof Uint8Array !== 'undefined') { - if (Buffer.TYPED_ARRAY_SUPPORT) { - return (new Buffer(this)).buffer - } else { - var buf = new Uint8Array(this.length) - for (var i = 0, len = buf.length; i < len; i += 1) { - buf[i] = this[i] - } - return buf.buffer - } - } else { - throw new TypeError('Buffer.toArrayBuffer not supported in this browser') - } - } - - // HELPER FUNCTIONS - // ================ - - var BP = Buffer.prototype - - /** - * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods - */ - Buffer._augment = function _augment (arr) { - arr.constructor = Buffer - arr._isBuffer = true - - // save reference to original Uint8Array set method before overwriting - arr._set = arr.set - - // deprecated - arr.get = BP.get - arr.set = BP.set - - arr.write = BP.write - arr.toString = BP.toString - arr.toLocaleString = BP.toString - arr.toJSON = BP.toJSON - arr.equals = BP.equals - arr.compare = BP.compare - arr.indexOf = BP.indexOf - arr.copy = BP.copy - arr.slice = BP.slice - arr.readUIntLE = BP.readUIntLE - arr.readUIntBE = BP.readUIntBE - arr.readUInt8 = BP.readUInt8 - arr.readUInt16LE = BP.readUInt16LE - arr.readUInt16BE = BP.readUInt16BE - arr.readUInt32LE = BP.readUInt32LE - arr.readUInt32BE = BP.readUInt32BE - arr.readIntLE = BP.readIntLE - arr.readIntBE = BP.readIntBE - arr.readInt8 = BP.readInt8 - arr.readInt16LE = BP.readInt16LE - arr.readInt16BE = BP.readInt16BE - arr.readInt32LE = BP.readInt32LE - arr.readInt32BE = BP.readInt32BE - arr.readFloatLE = BP.readFloatLE - arr.readFloatBE = BP.readFloatBE - arr.readDoubleLE = BP.readDoubleLE - arr.readDoubleBE = BP.readDoubleBE - arr.writeUInt8 = BP.writeUInt8 - arr.writeUIntLE = BP.writeUIntLE - arr.writeUIntBE = BP.writeUIntBE - arr.writeUInt16LE = BP.writeUInt16LE - arr.writeUInt16BE = BP.writeUInt16BE - arr.writeUInt32LE = BP.writeUInt32LE - arr.writeUInt32BE = BP.writeUInt32BE - arr.writeIntLE = BP.writeIntLE - arr.writeIntBE = BP.writeIntBE - arr.writeInt8 = BP.writeInt8 - arr.writeInt16LE = BP.writeInt16LE - arr.writeInt16BE = BP.writeInt16BE - arr.writeInt32LE = BP.writeInt32LE - arr.writeInt32BE = BP.writeInt32BE - arr.writeFloatLE = BP.writeFloatLE - arr.writeFloatBE = BP.writeFloatBE - arr.writeDoubleLE = BP.writeDoubleLE - arr.writeDoubleBE = BP.writeDoubleBE - arr.fill = BP.fill - arr.inspect = BP.inspect - arr.toArrayBuffer = BP.toArrayBuffer - - return arr - } - - var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g - - function base64clean (str) { - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = stringtrim(str).replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str - } - - function stringtrim (str) { - if (str.trim) return str.trim() - return str.replace(/^\s+|\s+$/g, '') - } - - function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) - } - - function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; i++) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes - } - - function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; i++) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray - } - - function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; i++) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray - } - - function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) - } - - function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; i++) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i - } - - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2).Buffer, (function() { return this; }()))) - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - - ;(function (exports) { - 'use strict'; - - var Arr = (typeof Uint8Array !== 'undefined') - ? Uint8Array - : Array - - var PLUS = '+'.charCodeAt(0) - var SLASH = '/'.charCodeAt(0) - var NUMBER = '0'.charCodeAt(0) - var LOWER = 'a'.charCodeAt(0) - var UPPER = 'A'.charCodeAt(0) - var PLUS_URL_SAFE = '-'.charCodeAt(0) - var SLASH_URL_SAFE = '_'.charCodeAt(0) - - function decode (elt) { - var code = elt.charCodeAt(0) - if (code === PLUS || - code === PLUS_URL_SAFE) - return 62 // '+' - if (code === SLASH || - code === SLASH_URL_SAFE) - return 63 // '/' - if (code < NUMBER) - return -1 //no match - if (code < NUMBER + 10) - return code - NUMBER + 26 + 26 - if (code < UPPER + 26) - return code - UPPER - if (code < LOWER + 26) - return code - LOWER + 26 - } - - function b64ToByteArray (b64) { - var i, j, l, tmp, placeHolders, arr - - if (b64.length % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // the number of equal signs (place holders) - // if there are two placeholders, than the two characters before it - // represent one byte - // if there is only one, then the three characters before it represent 2 bytes - // this is just a cheap hack to not do indexOf twice - var len = b64.length - placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 - - // base64 is 4/3 + up to two characters of the original data - arr = new Arr(b64.length * 3 / 4 - placeHolders) - - // if there are placeholders, only get up to the last complete 4 chars - l = placeHolders > 0 ? b64.length - 4 : b64.length - - var L = 0 - - function push (v) { - arr[L++] = v - } - - for (i = 0, j = 0; i < l; i += 4, j += 3) { - tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) - push((tmp & 0xFF0000) >> 16) - push((tmp & 0xFF00) >> 8) - push(tmp & 0xFF) - } - - if (placeHolders === 2) { - tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) - push(tmp & 0xFF) - } else if (placeHolders === 1) { - tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) - push((tmp >> 8) & 0xFF) - push(tmp & 0xFF) - } - - return arr - } - - function uint8ToBase64 (uint8) { - var i, - extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes - output = "", - temp, length - - function encode (num) { - return lookup.charAt(num) - } - - function tripletToBase64 (num) { - return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) - } - - // go through the array every three bytes, we'll deal with trailing stuff later - for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { - temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) - output += tripletToBase64(temp) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - switch (extraBytes) { - case 1: - temp = uint8[uint8.length - 1] - output += encode(temp >> 2) - output += encode((temp << 4) & 0x3F) - output += '==' - break - case 2: - temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) - output += encode(temp >> 10) - output += encode((temp >> 4) & 0x3F) - output += encode((temp << 2) & 0x3F) - output += '=' - break - } - - return output - } - - exports.toByteArray = b64ToByteArray - exports.fromByteArray = uint8ToBase64 - }( false ? (this.base64js = {}) : exports)) - - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = nBytes * 8 - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) - } - - exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = nBytes * 8 - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = (value * c - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 - } - - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - - /** - * isArray - */ - - var isArray = Array.isArray; - - /** - * toString - */ - - var str = Object.prototype.toString; - - /** - * Whether or not the given `val` - * is an array. - * - * example: - * - * isArray([]); - * // > true - * isArray(arguments); - * // > false - * isArray(''); - * // > false - * - * @param {mixed} val - * @return {bool} - */ - - module.exports = isArray || function (val) { - return !! val && '[object Array]' == str.call(val); - }; - - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - /* jslint node: true */ - /* global window */ - 'use strict'; - - var _ = __webpack_require__(7); - var FontProvider = __webpack_require__(9); - var LayoutBuilder = __webpack_require__(11); - var PdfKit = __webpack_require__(24); - var PDFReference = __webpack_require__(46); - var sizes = __webpack_require__(102); - var ImageMeasure = __webpack_require__(103); - var textDecorator = __webpack_require__(104); - var FontProvider = __webpack_require__(9); - - //////////////////////////////////////// - // PdfPrinter - - /** - * @class Creates an instance of a PdfPrinter which turns document definition into a pdf - * - * @param {Object} fontDescriptors font definition dictionary - * - * @example - * var fontDescriptors = { - * Roboto: { - * normal: 'fonts/Roboto-Regular.ttf', - * bold: 'fonts/Roboto-Medium.ttf', - * italics: 'fonts/Roboto-Italic.ttf', - * bolditalics: 'fonts/Roboto-Italic.ttf' - * } - * }; - * - * var printer = new PdfPrinter(fontDescriptors); - */ - function PdfPrinter(fontDescriptors) { - this.fontDescriptors = fontDescriptors; - } - - /** - * Executes layout engine for the specified document and renders it into a pdfkit document - * ready to be saved. - * - * @param {Object} docDefinition document definition - * @param {Object} docDefinition.content an array describing the pdf structure (for more information take a look at the examples in the /examples folder) - * @param {Object} [docDefinition.defaultStyle] default (implicit) style definition - * @param {Object} [docDefinition.styles] dictionary defining all styles which can be used in the document - * @param {Object} [docDefinition.pageSize] page size (pdfkit units, A4 dimensions by default) - * @param {Number} docDefinition.pageSize.width width - * @param {Number} docDefinition.pageSize.height height - * @param {Object} [docDefinition.pageMargins] page margins (pdfkit units) - * - * @example - * - * var docDefinition = { - * info: { - * title: 'awesome Document', - * author: 'john doe', - * subject: 'subject of document', - * keywords: 'keywords for document', - * }, - * content: [ - * 'First paragraph', - * 'Second paragraph, this time a little bit longer', - * { text: 'Third paragraph, slightly bigger font size', fontSize: 20 }, - * { text: 'Another paragraph using a named style', style: 'header' }, - * { text: ['playing with ', 'inlines' ] }, - * { text: ['and ', { text: 'restyling ', bold: true }, 'them'] }, - * ], - * styles: { - * header: { fontSize: 30, bold: true } - * } - * } - * - * var pdfDoc = printer.createPdfKitDocument(docDefinition); - * - * pdfDoc.pipe(fs.createWriteStream('sample.pdf')); - * pdfDoc.end(); - * - * @return {Object} a pdfKit document object which can be saved or encode to data-url - */ - PdfPrinter.prototype.createPdfKitDocument = function(docDefinition, options) { - options = options || {}; - - var pageSize = pageSize2widthAndHeight(docDefinition.pageSize || 'a4'); - - if(docDefinition.pageOrientation === 'landscape') { - pageSize = { width: pageSize.height, height: pageSize.width}; - } - pageSize.orientation = docDefinition.pageOrientation === 'landscape' ? docDefinition.pageOrientation : 'portrait'; - - this.pdfKitDoc = new PdfKit({ size: [ pageSize.width, pageSize.height ], compress: false}); - this.pdfKitDoc.info.Producer = 'pdfmake'; - this.pdfKitDoc.info.Creator = 'pdfmake'; - - // pdf kit maintains the uppercase fieldnames from pdf spec - // to keep the pdfmake api consistent, the info field are defined lowercase - if(docDefinition.info){ - var info = docDefinition.info; - // check for falsey an set null, so that pdfkit always get either null or value - this.pdfKitDoc.info.Title = docDefinition.info.title ? docDefinition.info.title : null; - this.pdfKitDoc.info.Author = docDefinition.info.author ? docDefinition.info.author : null; - this.pdfKitDoc.info.Subject = docDefinition.info.subject ? docDefinition.info.subject : null; - this.pdfKitDoc.info.Keywords = docDefinition.info.keywords ? docDefinition.info.keywords : null; - } - - this.fontProvider = new FontProvider(this.fontDescriptors, this.pdfKitDoc); - - docDefinition.images = docDefinition.images || {}; - - var builder = new LayoutBuilder( - pageSize, - fixPageMargins(docDefinition.pageMargins || 40), - new ImageMeasure(this.pdfKitDoc, docDefinition.images)); - - registerDefaultTableLayouts(builder); - if (options.tableLayouts) { - builder.registerTableLayouts(options.tableLayouts); - } - - var pages = builder.layoutDocument(docDefinition.content, this.fontProvider, docDefinition.styles || {}, docDefinition.defaultStyle || { fontSize: 12, font: 'Roboto' }, docDefinition.background, docDefinition.header, docDefinition.footer, docDefinition.images, docDefinition.watermark, docDefinition.pageBreakBefore); - - renderPages(pages, this.fontProvider, this.pdfKitDoc); - - if(options.autoPrint){ - var printActionRef = this.pdfKitDoc.ref({ - Type: 'Action', - S: 'Named', - N: 'Print' - }); - this.pdfKitDoc._root.data.OpenAction = printActionRef; - printActionRef.end(); - } - return this.pdfKitDoc; - }; - - function fixPageMargins(margin) { - if (!margin) return null; - - if (typeof margin === 'number' || margin instanceof Number) { - margin = { left: margin, right: margin, top: margin, bottom: margin }; - } else if (margin instanceof Array) { - if (margin.length === 2) { - margin = { left: margin[0], top: margin[1], right: margin[0], bottom: margin[1] }; - } else if (margin.length === 4) { - margin = { left: margin[0], top: margin[1], right: margin[2], bottom: margin[3] }; - } else throw 'Invalid pageMargins definition'; - } - - return margin; - } - - function registerDefaultTableLayouts(layoutBuilder) { - layoutBuilder.registerTableLayouts({ - noBorders: { - hLineWidth: function(i) { return 0; }, - vLineWidth: function(i) { return 0; }, - paddingLeft: function(i) { return i && 4 || 0; }, - paddingRight: function(i, node) { return (i < node.table.widths.length - 1) ? 4 : 0; }, - }, - headerLineOnly: { - hLineWidth: function(i, node) { - if (i === 0 || i === node.table.body.length) return 0; - return (i === node.table.headerRows) ? 2 : 0; - }, - vLineWidth: function(i) { return 0; }, - paddingLeft: function(i) { - return i === 0 ? 0 : 8; - }, - paddingRight: function(i, node) { - return (i === node.table.widths.length - 1) ? 0 : 8; - } - }, - lightHorizontalLines: { - hLineWidth: function(i, node) { - if (i === 0 || i === node.table.body.length) return 0; - return (i === node.table.headerRows) ? 2 : 1; - }, - vLineWidth: function(i) { return 0; }, - hLineColor: function(i) { return i === 1 ? 'black' : '#aaa'; }, - paddingLeft: function(i) { - return i === 0 ? 0 : 8; - }, - paddingRight: function(i, node) { - return (i === node.table.widths.length - 1) ? 0 : 8; - } - } - }); - } - - var defaultLayout = { - hLineWidth: function(i, node) { return 1; }, //return node.table.headerRows && i === node.table.headerRows && 3 || 0; }, - vLineWidth: function(i, node) { return 1; }, - hLineColor: function(i, node) { return 'black'; }, - vLineColor: function(i, node) { return 'black'; }, - paddingLeft: function(i, node) { return 4; }, //i && 4 || 0; }, - paddingRight: function(i, node) { return 4; }, //(i < node.table.widths.length - 1) ? 4 : 0; }, - paddingTop: function(i, node) { return 2; }, - paddingBottom: function(i, node) { return 2; } - }; - - function pageSize2widthAndHeight(pageSize) { - if (typeof pageSize == 'string' || pageSize instanceof String) { - var size = sizes[pageSize.toUpperCase()]; - if (!size) throw ('Page size ' + pageSize + ' not recognized'); - return { width: size[0], height: size[1] }; - } - - return pageSize; - } - - function StringObject(str){ - this.isString = true; - this.toString = function(){ - return str; - }; - } - - function updatePageOrientationInOptions(currentPage, pdfKitDoc) { - var previousPageOrientation = pdfKitDoc.options.size[0] > pdfKitDoc.options.size[1] ? 'landscape' : 'portrait'; - - if(currentPage.pageSize.orientation !== previousPageOrientation) { - var width = pdfKitDoc.options.size[0]; - var height = pdfKitDoc.options.size[1]; - pdfKitDoc.options.size = [height, width]; - } - } - - function renderPages(pages, fontProvider, pdfKitDoc) { - pdfKitDoc._pdfMakePages = pages; - for (var i = 0; i < pages.length; i++) { - if (i > 0) { - updatePageOrientationInOptions(pages[i], pdfKitDoc); - pdfKitDoc.addPage(pdfKitDoc.options); - } - - var page = pages[i]; - for(var ii = 0, il = page.items.length; ii < il; ii++) { - var item = page.items[ii]; - switch(item.type) { - case 'vector': - renderVector(item.item, pdfKitDoc); - break; - case 'line': - renderLine(item.item, item.item.x, item.item.y, pdfKitDoc); - break; - case 'image': - renderImage(item.item, item.item.x, item.item.y, pdfKitDoc); - break; - } - } - if(page.watermark){ - renderWatermark(page, pdfKitDoc); - } - - fontProvider.setFontRefsToPdfDoc(); - } - } - - function renderLine(line, x, y, pdfKitDoc) { - x = x || 0; - y = y || 0; - - var lineHeight = line.getHeight(); - var ascenderHeight = line.getAscenderHeight(); - - textDecorator.drawBackground(line, x, y, pdfKitDoc); - - //TODO: line.optimizeInlines(); - for(var i = 0, l = line.inlines.length; i < l; i++) { - var inline = line.inlines[i]; - - pdfKitDoc.fill(inline.color || 'black'); - - pdfKitDoc.save(); - pdfKitDoc.transform(1, 0, 0, -1, 0, pdfKitDoc.page.height); - - - var encoded = inline.font.encode(inline.text); - pdfKitDoc.addContent('BT'); - - pdfKitDoc.addContent('' + (x + inline.x) + ' ' + (pdfKitDoc.page.height - y - ascenderHeight) + ' Td'); - pdfKitDoc.addContent('/' + encoded.fontId + ' ' + inline.fontSize + ' Tf'); - - pdfKitDoc.addContent('<' + encoded.encodedText + '> Tj'); - - pdfKitDoc.addContent('ET'); - - if (inline.link) { - pdfKitDoc.link(x + inline.x, pdfKitDoc.page.height - y - lineHeight, inline.width, lineHeight, inline.link); - } - - pdfKitDoc.restore(); - } - - textDecorator.drawDecorations(line, x, y, pdfKitDoc); - - } - - function renderWatermark(page, pdfKitDoc){ - var watermark = page.watermark; - - pdfKitDoc.fill('black'); - pdfKitDoc.opacity(0.6); - - pdfKitDoc.save(); - pdfKitDoc.transform(1, 0, 0, -1, 0, pdfKitDoc.page.height); - - var angle = Math.atan2(pdfKitDoc.page.height, pdfKitDoc.page.width) * 180/Math.PI; - pdfKitDoc.rotate(angle, {origin: [pdfKitDoc.page.width/2, pdfKitDoc.page.height/2]}); - - var encoded = watermark.font.encode(watermark.text); - pdfKitDoc.addContent('BT'); - pdfKitDoc.addContent('' + (pdfKitDoc.page.width/2 - watermark.size.size.width/2) + ' ' + (pdfKitDoc.page.height/2 - watermark.size.size.height/4) + ' Td'); - pdfKitDoc.addContent('/' + encoded.fontId + ' ' + watermark.size.fontSize + ' Tf'); - pdfKitDoc.addContent('<' + encoded.encodedText + '> Tj'); - pdfKitDoc.addContent('ET'); - pdfKitDoc.restore(); - } - - function renderVector(vector, pdfDoc) { - //TODO: pdf optimization (there's no need to write all properties everytime) - pdfDoc.lineWidth(vector.lineWidth || 1); - if (vector.dash) { - pdfDoc.dash(vector.dash.length, { space: vector.dash.space || vector.dash.length }); - } else { - pdfDoc.undash(); - } - pdfDoc.fillOpacity(vector.fillOpacity || 1); - pdfDoc.strokeOpacity(vector.strokeOpacity || 1); - pdfDoc.lineJoin(vector.lineJoin || 'miter'); - - //TODO: clipping - - switch(vector.type) { - case 'ellipse': - pdfDoc.ellipse(vector.x, vector.y, vector.r1, vector.r2); - break; - case 'rect': - if (vector.r) { - pdfDoc.roundedRect(vector.x, vector.y, vector.w, vector.h, vector.r); - } else { - pdfDoc.rect(vector.x, vector.y, vector.w, vector.h); - } - break; - case 'line': - pdfDoc.moveTo(vector.x1, vector.y1); - pdfDoc.lineTo(vector.x2, vector.y2); - break; - case 'polyline': - if (vector.points.length === 0) break; - - pdfDoc.moveTo(vector.points[0].x, vector.points[0].y); - for(var i = 1, l = vector.points.length; i < l; i++) { - pdfDoc.lineTo(vector.points[i].x, vector.points[i].y); - } - - if (vector.points.length > 1) { - var p1 = vector.points[0]; - var pn = vector.points[vector.points.length - 1]; - - if (vector.closePath || p1.x === pn.x && p1.y === pn.y) { - pdfDoc.closePath(); - } - } - break; - } - - if (vector.color && vector.lineColor) { - pdfDoc.fillAndStroke(vector.color, vector.lineColor); - } else if (vector.color) { - pdfDoc.fill(vector.color); - } else { - pdfDoc.stroke(vector.lineColor || 'black'); - } - } - - function renderImage(image, x, y, pdfKitDoc) { - pdfKitDoc.image(image.image, image.x, image.y, { width: image._width, height: image._height }); - } - - module.exports = PdfPrinter; - - - /* temporary browser extension */ - PdfPrinter.prototype.fs = __webpack_require__(44); - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/** - * @license - * lodash 3.10.1 (Custom Build) - * Build: `lodash modern -d -o ./index.js` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ - ;(function() { - - /** Used as a safe reference for `undefined` in pre-ES5 environments. */ - var undefined; - - /** Used as the semantic version number. */ - var VERSION = '3.10.1'; - - /** Used to compose bitmasks for wrapper metadata. */ - var BIND_FLAG = 1, - BIND_KEY_FLAG = 2, - CURRY_BOUND_FLAG = 4, - CURRY_FLAG = 8, - CURRY_RIGHT_FLAG = 16, - PARTIAL_FLAG = 32, - PARTIAL_RIGHT_FLAG = 64, - ARY_FLAG = 128, - REARG_FLAG = 256; - - /** Used as default options for `_.trunc`. */ - var DEFAULT_TRUNC_LENGTH = 30, - DEFAULT_TRUNC_OMISSION = '...'; - - /** Used to detect when a function becomes hot. */ - var HOT_COUNT = 150, - HOT_SPAN = 16; - - /** Used as the size to enable large array optimizations. */ - var LARGE_ARRAY_SIZE = 200; - - /** Used to indicate the type of lazy iteratees. */ - var LAZY_FILTER_FLAG = 1, - LAZY_MAP_FLAG = 2; - - /** Used as the `TypeError` message for "Functions" methods. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /** Used as the internal argument placeholder. */ - var PLACEHOLDER = '__lodash_placeholder__'; - - /** `Object#toString` result references. */ - var argsTag = '[object Arguments]', - arrayTag = '[object Array]', - boolTag = '[object Boolean]', - dateTag = '[object Date]', - errorTag = '[object Error]', - funcTag = '[object Function]', - mapTag = '[object Map]', - numberTag = '[object Number]', - objectTag = '[object Object]', - regexpTag = '[object RegExp]', - setTag = '[object Set]', - stringTag = '[object String]', - weakMapTag = '[object WeakMap]'; - - var arrayBufferTag = '[object ArrayBuffer]', - float32Tag = '[object Float32Array]', - float64Tag = '[object Float64Array]', - int8Tag = '[object Int8Array]', - int16Tag = '[object Int16Array]', - int32Tag = '[object Int32Array]', - uint8Tag = '[object Uint8Array]', - uint8ClampedTag = '[object Uint8ClampedArray]', - uint16Tag = '[object Uint16Array]', - uint32Tag = '[object Uint32Array]'; - - /** Used to match empty string literals in compiled template source. */ - var reEmptyStringLeading = /\b__p \+= '';/g, - reEmptyStringMiddle = /\b(__p \+=) '' \+/g, - reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - - /** Used to match HTML entities and HTML characters. */ - var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g, - reUnescapedHtml = /[&<>"'`]/g, - reHasEscapedHtml = RegExp(reEscapedHtml.source), - reHasUnescapedHtml = RegExp(reUnescapedHtml.source); - - /** Used to match template delimiters. */ - var reEscape = /<%-([\s\S]+?)%>/g, - reEvaluate = /<%([\s\S]+?)%>/g, - reInterpolate = /<%=([\s\S]+?)%>/g; - - /** Used to match property names within property paths. */ - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/, - reIsPlainProp = /^\w*$/, - rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; - - /** - * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns) - * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern). - */ - var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, - reHasRegExpChars = RegExp(reRegExpChars.source); - - /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ - var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; - - /** Used to match backslashes in property paths. */ - var reEscapeChar = /\\(\\)?/g; - - /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect hexadecimal string values. */ - var reHasHexPrefix = /^0[xX]/; - - /** Used to detect host constructors (Safari > 5). */ - var reIsHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to detect unsigned integer values. */ - var reIsUint = /^\d+$/; - - /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ - var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; - - /** Used to ensure capturing order of template delimiters. */ - var reNoMatch = /($^)/; - - /** Used to match unescaped characters in compiled string literals. */ - var reUnescapedString = /['\n\r\u2028\u2029\\]/g; - - /** Used to match words to create compound words. */ - var reWords = (function() { - var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]', - lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+'; - - return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g'); - }()); - - /** Used to assign default `context` object properties. */ - var contextProps = [ - 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', - 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', - 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite', - 'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', - 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap' - ]; - - /** Used to make template sourceURLs easier to identify. */ - var templateCounter = -1; - - /** Used to identify `toStringTag` values of typed arrays. */ - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = - typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = - typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = - typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = - typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = - typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = - typedArrayTags[dateTag] = typedArrayTags[errorTag] = - typedArrayTags[funcTag] = typedArrayTags[mapTag] = - typedArrayTags[numberTag] = typedArrayTags[objectTag] = - typedArrayTags[regexpTag] = typedArrayTags[setTag] = - typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; - - /** Used to identify `toStringTag` values supported by `_.clone`. */ - var cloneableTags = {}; - cloneableTags[argsTag] = cloneableTags[arrayTag] = - cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = - cloneableTags[dateTag] = cloneableTags[float32Tag] = - cloneableTags[float64Tag] = cloneableTags[int8Tag] = - cloneableTags[int16Tag] = cloneableTags[int32Tag] = - cloneableTags[numberTag] = cloneableTags[objectTag] = - cloneableTags[regexpTag] = cloneableTags[stringTag] = - cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = - cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; - cloneableTags[errorTag] = cloneableTags[funcTag] = - cloneableTags[mapTag] = cloneableTags[setTag] = - cloneableTags[weakMapTag] = false; - - /** Used to map latin-1 supplementary letters to basic latin letters. */ - var deburredLetters = { - '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', - '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', - '\xc7': 'C', '\xe7': 'c', - '\xd0': 'D', '\xf0': 'd', - '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', - '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', - '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', - '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', - '\xd1': 'N', '\xf1': 'n', - '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', - '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', - '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', - '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', - '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', - '\xc6': 'Ae', '\xe6': 'ae', - '\xde': 'Th', '\xfe': 'th', - '\xdf': 'ss' - }; - - /** Used to map characters to HTML entities. */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`' - }; - - /** Used to map HTML entities to characters. */ - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'", - '`': '`' - }; - - /** Used to determine if values are of the language type `Object`. */ - var objectTypes = { - 'function': true, - 'object': true - }; - - /** Used to escape characters for inclusion in compiled regexes. */ - var regexpEscapes = { - '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34', - '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39', - 'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46', - 'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66', - 'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78' - }; - - /** Used to escape characters for inclusion in compiled string literals. */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - /** Detect free variable `exports`. */ - var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; - - /** Detect free variable `self`. */ - var freeSelf = objectTypes[typeof self] && self && self.Object && self; - - /** Detect free variable `window`. */ - var freeWindow = objectTypes[typeof window] && window && window.Object && window; - - /** Detect the popular CommonJS extension `module.exports`. */ - var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; - - /** - * Used as a reference to the global object. - * - * The `this` value is used if it's the global object to avoid Greasemonkey's - * restricted `window` object, otherwise the `window` object is used. - */ - var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this; - - /*--------------------------------------------------------------------------*/ - - /** - * The base implementation of `compareAscending` which compares values and - * sorts them in ascending order without guaranteeing a stable sort. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {number} Returns the sort order indicator for `value`. - */ - function baseCompareAscending(value, other) { - if (value !== other) { - var valIsNull = value === null, - valIsUndef = value === undefined, - valIsReflexive = value === value; - - var othIsNull = other === null, - othIsUndef = other === undefined, - othIsReflexive = other === other; - - if ((value > other && !othIsNull) || !valIsReflexive || - (valIsNull && !othIsUndef && othIsReflexive) || - (valIsUndef && othIsReflexive)) { - return 1; - } - if ((value < other && !valIsNull) || !othIsReflexive || - (othIsNull && !valIsUndef && valIsReflexive) || - (othIsUndef && valIsReflexive)) { - return -1; - } - } - return 0; - } - - /** - * The base implementation of `_.findIndex` and `_.findLastIndex` without - * support for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to search. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseFindIndex(array, predicate, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length)) { - if (predicate(array[index], index, array)) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.indexOf` without support for binary searches. - * - * @private - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseIndexOf(array, value, fromIndex) { - if (value !== value) { - return indexOfNaN(array, fromIndex); - } - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.isFunction` without support for environments - * with incorrect `typeof` results. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - */ - function baseIsFunction(value) { - // Avoid a Chakra JIT bug in compatibility modes of IE 11. - // See https://github.com/jashkenas/underscore/issues/1621 for more details. - return typeof value == 'function' || false; - } - - /** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ - function baseToString(value) { - return value == null ? '' : (value + ''); - } - - /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first character - * of `string` that is not found in `chars`. - * - * @private - * @param {string} string The string to inspect. - * @param {string} chars The characters to find. - * @returns {number} Returns the index of the first character not found in `chars`. - */ - function charsLeftIndex(string, chars) { - var index = -1, - length = string.length; - - while (++index < length && chars.indexOf(string.charAt(index)) > -1) {} - return index; - } - - /** - * Used by `_.trim` and `_.trimRight` to get the index of the last character - * of `string` that is not found in `chars`. - * - * @private - * @param {string} string The string to inspect. - * @param {string} chars The characters to find. - * @returns {number} Returns the index of the last character not found in `chars`. - */ - function charsRightIndex(string, chars) { - var index = string.length; - - while (index-- && chars.indexOf(string.charAt(index)) > -1) {} - return index; - } - - /** - * Used by `_.sortBy` to compare transformed elements of a collection and stable - * sort them in ascending order. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareAscending(object, other) { - return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index); - } - - /** - * Used by `_.sortByOrder` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise, - * a value is sorted in ascending order if its corresponding order is "asc", and - * descending if "desc". - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {boolean[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareMultiple(object, other, orders) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length, - ordersLength = orders.length; - - while (++index < length) { - var result = baseCompareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; - } - var order = orders[index]; - return result * ((order === 'asc' || order === true) ? 1 : -1); - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://code.google.com/p/v8/issues/detail?id=90 for more details. - return object.index - other.index; - } - - /** - * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. - * - * @private - * @param {string} letter The matched letter to deburr. - * @returns {string} Returns the deburred letter. - */ - function deburrLetter(letter) { - return deburredLetters[letter]; - } - - /** - * Used by `_.escape` to convert characters to HTML entities. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeHtmlChar(chr) { - return htmlEscapes[chr]; - } - - /** - * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes. - * - * @private - * @param {string} chr The matched character to escape. - * @param {string} leadingChar The capture group for a leading character. - * @param {string} whitespaceChar The capture group for a whitespace character. - * @returns {string} Returns the escaped character. - */ - function escapeRegExpChar(chr, leadingChar, whitespaceChar) { - if (leadingChar) { - chr = regexpEscapes[chr]; - } else if (whitespaceChar) { - chr = stringEscapes[chr]; - } - return '\\' + chr; - } - - /** - * Used by `_.template` to escape characters for inclusion in compiled string literals. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeStringChar(chr) { - return '\\' + stringEscapes[chr]; - } - - /** - * Gets the index at which the first occurrence of `NaN` is found in `array`. - * - * @private - * @param {Array} array The array to search. - * @param {number} fromIndex The index to search from. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched `NaN`, else `-1`. - */ - function indexOfNaN(array, fromIndex, fromRight) { - var length = array.length, - index = fromIndex + (fromRight ? 0 : -1); - - while ((fromRight ? index-- : ++index < length)) { - var other = array[index]; - if (other !== other) { - return index; - } - } - return -1; - } - - /** - * Checks if `value` is object-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - */ - function isObjectLike(value) { - return !!value && typeof value == 'object'; - } - - /** - * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a - * character code is whitespace. - * - * @private - * @param {number} charCode The character code to inspect. - * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`. - */ - function isSpace(charCode) { - return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 || - (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279))); - } - - /** - * Replaces all `placeholder` elements in `array` with an internal placeholder - * and returns an array of their indexes. - * - * @private - * @param {Array} array The array to modify. - * @param {*} placeholder The placeholder to replace. - * @returns {Array} Returns the new array of placeholder indexes. - */ - function replaceHolders(array, placeholder) { - var index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - if (array[index] === placeholder) { - array[index] = PLACEHOLDER; - result[++resIndex] = index; - } - } - return result; - } - - /** - * An implementation of `_.uniq` optimized for sorted arrays without support - * for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The function invoked per iteration. - * @returns {Array} Returns the new duplicate-value-free array. - */ - function sortedUniq(array, iteratee) { - var seen, - index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value, index, array) : value; - - if (!index || seen !== computed) { - seen = computed; - result[++resIndex] = value; - } - } - return result; - } - - /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace - * character of `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the index of the first non-whitespace character. - */ - function trimmedLeftIndex(string) { - var index = -1, - length = string.length; - - while (++index < length && isSpace(string.charCodeAt(index))) {} - return index; - } - - /** - * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace - * character of `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the index of the last non-whitespace character. - */ - function trimmedRightIndex(string) { - var index = string.length; - - while (index-- && isSpace(string.charCodeAt(index))) {} - return index; - } - - /** - * Used by `_.unescape` to convert HTML entities to characters. - * - * @private - * @param {string} chr The matched character to unescape. - * @returns {string} Returns the unescaped character. - */ - function unescapeHtmlChar(chr) { - return htmlUnescapes[chr]; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Create a new pristine `lodash` function using the given `context` object. - * - * @static - * @memberOf _ - * @category Utility - * @param {Object} [context=root] The context object. - * @returns {Function} Returns a new `lodash` function. - * @example - * - * _.mixin({ 'foo': _.constant('foo') }); - * - * var lodash = _.runInContext(); - * lodash.mixin({ 'bar': lodash.constant('bar') }); - * - * _.isFunction(_.foo); - * // => true - * _.isFunction(_.bar); - * // => false - * - * lodash.isFunction(lodash.foo); - * // => false - * lodash.isFunction(lodash.bar); - * // => true - * - * // using `context` to mock `Date#getTime` use in `_.now` - * var mock = _.runInContext({ - * 'Date': function() { - * return { 'getTime': getTimeMock }; - * } - * }); - * - * // or creating a suped-up `defer` in Node.js - * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; - */ - function runInContext(context) { - // Avoid issues with some ES3 environments that attempt to use values, named - // after built-in constructors like `Object`, for the creation of literals. - // ES5 clears this up by stating that literals must use built-in constructors. - // See https://es5.github.io/#x11.1.5 for more details. - context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; - - /** Native constructor references. */ - var Array = context.Array, - Date = context.Date, - Error = context.Error, - Function = context.Function, - Math = context.Math, - Number = context.Number, - Object = context.Object, - RegExp = context.RegExp, - String = context.String, - TypeError = context.TypeError; - - /** Used for native method references. */ - var arrayProto = Array.prototype, - objectProto = Object.prototype, - stringProto = String.prototype; - - /** Used to resolve the decompiled source of functions. */ - var fnToString = Function.prototype.toString; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto.hasOwnProperty; - - /** Used to generate unique IDs. */ - var idCounter = 0; - - /** - * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) - * of values. - */ - var objToString = objectProto.toString; - - /** Used to restore the original `_` reference in `_.noConflict`. */ - var oldDash = root._; - - /** Used to detect if a method is native. */ - var reIsNative = RegExp('^' + - fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' - ); - - /** Native method references. */ - var ArrayBuffer = context.ArrayBuffer, - clearTimeout = context.clearTimeout, - parseFloat = context.parseFloat, - pow = Math.pow, - propertyIsEnumerable = objectProto.propertyIsEnumerable, - Set = getNative(context, 'Set'), - setTimeout = context.setTimeout, - splice = arrayProto.splice, - Uint8Array = context.Uint8Array, - WeakMap = getNative(context, 'WeakMap'); - - /* Native method references for those with the same name as other `lodash` methods. */ - var nativeCeil = Math.ceil, - nativeCreate = getNative(Object, 'create'), - nativeFloor = Math.floor, - nativeIsArray = getNative(Array, 'isArray'), - nativeIsFinite = context.isFinite, - nativeKeys = getNative(Object, 'keys'), - nativeMax = Math.max, - nativeMin = Math.min, - nativeNow = getNative(Date, 'now'), - nativeParseInt = context.parseInt, - nativeRandom = Math.random; - - /** Used as references for `-Infinity` and `Infinity`. */ - var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY, - POSITIVE_INFINITY = Number.POSITIVE_INFINITY; - - /** Used as references for the maximum length and index of an array. */ - var MAX_ARRAY_LENGTH = 4294967295, - MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, - HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; - - /** - * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) - * of an array-like value. - */ - var MAX_SAFE_INTEGER = 9007199254740991; - - /** Used to store function metadata. */ - var metaMap = WeakMap && new WeakMap; - - /** Used to lookup unminified function names. */ - var realNames = {}; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object which wraps `value` to enable implicit chaining. - * Methods that operate on and return arrays, collections, and functions can - * be chained together. Methods that retrieve a single value or may return a - * primitive value will automatically end the chain returning the unwrapped - * value. Explicit chaining may be enabled using `_.chain`. The execution of - * chained methods is lazy, that is, execution is deferred until `_#value` - * is implicitly or explicitly called. - * - * Lazy evaluation allows several methods to support shortcut fusion. Shortcut - * fusion is an optimization strategy which merge iteratee calls; this can help - * to avoid the creation of intermediate data structures and greatly reduce the - * number of iteratee executions. - * - * Chaining is supported in custom builds as long as the `_#value` method is - * directly or indirectly included in the build. - * - * In addition to lodash methods, wrappers have `Array` and `String` methods. - * - * The wrapper `Array` methods are: - * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, - * `splice`, and `unshift` - * - * The wrapper `String` methods are: - * `replace` and `split` - * - * The wrapper methods that support shortcut fusion are: - * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, - * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, - * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, - * and `where` - * - * The chainable wrapper methods are: - * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, - * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, - * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, - * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, - * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, - * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, - * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, - * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, - * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, - * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, - * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, - * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, - * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, - * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, - * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, - * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, - * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` - * - * The wrapper methods that are **not** chainable by default are: - * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, - * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, - * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, - * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, - * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, - * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, - * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, - * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, - * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, - * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, - * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, - * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, - * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, - * `unescape`, `uniqueId`, `value`, and `words` - * - * The wrapper method `sample` will return a wrapped value when `n` is provided, - * otherwise an unwrapped value is returned. - * - * @name _ - * @constructor - * @category Chain - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var wrapped = _([1, 2, 3]); - * - * // returns an unwrapped value - * wrapped.reduce(function(total, n) { - * return total + n; - * }); - * // => 6 - * - * // returns a wrapped value - * var squares = wrapped.map(function(n) { - * return n * n; - * }); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ - function lodash(value) { - if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { - if (value instanceof LodashWrapper) { - return value; - } - if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) { - return wrapperClone(value); - } - } - return new LodashWrapper(value); - } - - /** - * The function whose prototype all chaining wrappers inherit from. - * - * @private - */ - function baseLodash() { - // No operation performed. - } - - /** - * The base constructor for creating `lodash` wrapper objects. - * - * @private - * @param {*} value The value to wrap. - * @param {boolean} [chainAll] Enable chaining for all wrapper methods. - * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. - */ - function LodashWrapper(value, chainAll, actions) { - this.__wrapped__ = value; - this.__actions__ = actions || []; - this.__chain__ = !!chainAll; - } - - /** - * An object environment feature flags. - * - * @static - * @memberOf _ - * @type Object - */ - var support = lodash.support = {}; - - /** - * By default, the template delimiters used by lodash are like those in - * embedded Ruby (ERB). Change the following template settings to use - * alternative delimiters. - * - * @static - * @memberOf _ - * @type Object - */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'escape': reEscape, - - /** - * Used to detect code to be evaluated. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'evaluate': reEvaluate, - - /** - * Used to detect `data` property values to inject. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'interpolate': reInterpolate, - - /** - * Used to reference the data object in the template text. - * - * @memberOf _.templateSettings - * @type string - */ - 'variable': '', - - /** - * Used to import variables into the compiled template. - * - * @memberOf _.templateSettings - * @type Object - */ - 'imports': { - - /** - * A reference to the `lodash` function. - * - * @memberOf _.templateSettings.imports - * @type Function - */ - '_': lodash - } - }; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. - * - * @private - * @param {*} value The value to wrap. - */ - function LazyWrapper(value) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__dir__ = 1; - this.__filtered__ = false; - this.__iteratees__ = []; - this.__takeCount__ = POSITIVE_INFINITY; - this.__views__ = []; - } - - /** - * Creates a clone of the lazy wrapper object. - * - * @private - * @name clone - * @memberOf LazyWrapper - * @returns {Object} Returns the cloned `LazyWrapper` object. - */ - function lazyClone() { - var result = new LazyWrapper(this.__wrapped__); - result.__actions__ = arrayCopy(this.__actions__); - result.__dir__ = this.__dir__; - result.__filtered__ = this.__filtered__; - result.__iteratees__ = arrayCopy(this.__iteratees__); - result.__takeCount__ = this.__takeCount__; - result.__views__ = arrayCopy(this.__views__); - return result; - } - - /** - * Reverses the direction of lazy iteration. - * - * @private - * @name reverse - * @memberOf LazyWrapper - * @returns {Object} Returns the new reversed `LazyWrapper` object. - */ - function lazyReverse() { - if (this.__filtered__) { - var result = new LazyWrapper(this); - result.__dir__ = -1; - result.__filtered__ = true; - } else { - result = this.clone(); - result.__dir__ *= -1; - } - return result; - } - - /** - * Extracts the unwrapped value from its lazy wrapper. - * - * @private - * @name value - * @memberOf LazyWrapper - * @returns {*} Returns the unwrapped value. - */ - function lazyValue() { - var array = this.__wrapped__.value(), - dir = this.__dir__, - isArr = isArray(array), - isRight = dir < 0, - arrLength = isArr ? array.length : 0, - view = getView(0, arrLength, this.__views__), - start = view.start, - end = view.end, - length = end - start, - index = isRight ? end : (start - 1), - iteratees = this.__iteratees__, - iterLength = iteratees.length, - resIndex = 0, - takeCount = nativeMin(length, this.__takeCount__); - - if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) { - return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__); - } - var result = []; - - outer: - while (length-- && resIndex < takeCount) { - index += dir; - - var iterIndex = -1, - value = array[index]; - - while (++iterIndex < iterLength) { - var data = iteratees[iterIndex], - iteratee = data.iteratee, - type = data.type, - computed = iteratee(value); - - if (type == LAZY_MAP_FLAG) { - value = computed; - } else if (!computed) { - if (type == LAZY_FILTER_FLAG) { - continue outer; - } else { - break outer; - } - } - } - result[resIndex++] = value; - } - return result; - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a cache object to store key/value pairs. - * - * @private - * @static - * @name Cache - * @memberOf _.memoize - */ - function MapCache() { - this.__data__ = {}; - } - - /** - * Removes `key` and its value from the cache. - * - * @private - * @name delete - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`. - */ - function mapDelete(key) { - return this.has(key) && delete this.__data__[key]; - } - - /** - * Gets the cached value for `key`. - * - * @private - * @name get - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to get. - * @returns {*} Returns the cached value. - */ - function mapGet(key) { - return key == '__proto__' ? undefined : this.__data__[key]; - } - - /** - * Checks if a cached value for `key` exists. - * - * @private - * @name has - * @memberOf _.memoize.Cache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function mapHas(key) { - return key != '__proto__' && hasOwnProperty.call(this.__data__, key); - } - - /** - * Sets `value` to `key` of the cache. - * - * @private - * @name set - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to cache. - * @param {*} value The value to cache. - * @returns {Object} Returns the cache object. - */ - function mapSet(key, value) { - if (key != '__proto__') { - this.__data__[key] = value; - } - return this; - } - - /*------------------------------------------------------------------------*/ - - /** - * - * Creates a cache object to store unique values. - * - * @private - * @param {Array} [values] The values to cache. - */ - function SetCache(values) { - var length = values ? values.length : 0; - - this.data = { 'hash': nativeCreate(null), 'set': new Set }; - while (length--) { - this.push(values[length]); - } - } - - /** - * Checks if `value` is in `cache` mimicking the return signature of - * `_.indexOf` by returning `0` if the value is found, else `-1`. - * - * @private - * @param {Object} cache The cache to search. - * @param {*} value The value to search for. - * @returns {number} Returns `0` if `value` is found, else `-1`. - */ - function cacheIndexOf(cache, value) { - var data = cache.data, - result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; - - return result ? 0 : -1; - } - - /** - * Adds `value` to the cache. - * - * @private - * @name push - * @memberOf SetCache - * @param {*} value The value to cache. - */ - function cachePush(value) { - var data = this.data; - if (typeof value == 'string' || isObject(value)) { - data.set.add(value); - } else { - data.hash[value] = true; - } - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a new array joining `array` with `other`. - * - * @private - * @param {Array} array The array to join. - * @param {Array} other The other array to join. - * @returns {Array} Returns the new concatenated array. - */ - function arrayConcat(array, other) { - var index = -1, - length = array.length, - othIndex = -1, - othLength = other.length, - result = Array(length + othLength); - - while (++index < length) { - result[index] = array[index]; - } - while (++othIndex < othLength) { - result[index++] = other[othIndex]; - } - return result; - } - - /** - * Copies the values of `source` to `array`. - * - * @private - * @param {Array} source The array to copy values from. - * @param {Array} [array=[]] The array to copy values to. - * @returns {Array} Returns `array`. - */ - function arrayCopy(source, array) { - var index = -1, - length = source.length; - - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; - } - - /** - * A specialized version of `_.forEach` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEach(array, iteratee) { - var index = -1, - length = array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.forEachRight` for arrays without support for - * callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEachRight(array, iteratee) { - var length = array.length; - - while (length--) { - if (iteratee(array[length], length, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.every` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - */ - function arrayEvery(array, predicate) { - var index = -1, - length = array.length; - - while (++index < length) { - if (!predicate(array[index], index, array)) { - return false; - } - } - return true; - } - - /** - * A specialized version of `baseExtremum` for arrays which invokes `iteratee` - * with one argument: (value). - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {*} Returns the extremum value. - */ - function arrayExtremum(array, iteratee, comparator, exValue) { - var index = -1, - length = array.length, - computed = exValue, - result = computed; - - while (++index < length) { - var value = array[index], - current = +iteratee(value); - - if (comparator(current, computed)) { - computed = current; - result = value; - } - } - return result; - } - - /** - * A specialized version of `_.filter` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function arrayFilter(array, predicate) { - var index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[++resIndex] = value; - } - } - return result; - } - - /** - * A specialized version of `_.map` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function arrayMap(array, iteratee) { - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; - } - - /** - * Appends the elements of `values` to `array`. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to append. - * @returns {Array} Returns `array`. - */ - function arrayPush(array, values) { - var index = -1, - length = values.length, - offset = array.length; - - while (++index < length) { - array[offset + index] = values[index]; - } - return array; - } - - /** - * A specialized version of `_.reduce` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initFromArray] Specify using the first element of `array` - * as the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduce(array, iteratee, accumulator, initFromArray) { - var index = -1, - length = array.length; - - if (initFromArray && length) { - accumulator = array[++index]; - } - while (++index < length) { - accumulator = iteratee(accumulator, array[index], index, array); - } - return accumulator; - } - - /** - * A specialized version of `_.reduceRight` for arrays without support for - * callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initFromArray] Specify using the last element of `array` - * as the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduceRight(array, iteratee, accumulator, initFromArray) { - var length = array.length; - if (initFromArray && length) { - accumulator = array[--length]; - } - while (length--) { - accumulator = iteratee(accumulator, array[length], length, array); - } - return accumulator; - } - - /** - * A specialized version of `_.some` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function arraySome(array, predicate) { - var index = -1, - length = array.length; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - - /** - * A specialized version of `_.sum` for arrays without support for callback - * shorthands and `this` binding.. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ - function arraySum(array, iteratee) { - var length = array.length, - result = 0; - - while (length--) { - result += +iteratee(array[length]) || 0; - } - return result; - } - - /** - * Used by `_.defaults` to customize its `_.assign` use. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @returns {*} Returns the value to assign to the destination object. - */ - function assignDefaults(objectValue, sourceValue) { - return objectValue === undefined ? sourceValue : objectValue; - } - - /** - * Used by `_.template` to customize its `_.assign` use. - * - * **Note:** This function is like `assignDefaults` except that it ignores - * inherited property values when checking if a property is `undefined`. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @param {string} key The key associated with the object and source values. - * @param {Object} object The destination object. - * @returns {*} Returns the value to assign to the destination object. - */ - function assignOwnDefaults(objectValue, sourceValue, key, object) { - return (objectValue === undefined || !hasOwnProperty.call(object, key)) - ? sourceValue - : objectValue; - } - - /** - * A specialized version of `_.assign` for customizing assigned values without - * support for argument juggling, multiple sources, and `this` binding `customizer` - * functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - */ - function assignWith(object, source, customizer) { - var index = -1, - props = keys(source), - length = props.length; - - while (++index < length) { - var key = props[index], - value = object[key], - result = customizer(value, source[key], key, object, source); - - if ((result === result ? (result !== value) : (value === value)) || - (value === undefined && !(key in object))) { - object[key] = result; - } - } - return object; - } - - /** - * The base implementation of `_.assign` without support for argument juggling, - * multiple sources, and `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssign(object, source) { - return source == null - ? object - : baseCopy(source, keys(source), object); - } - - /** - * The base implementation of `_.at` without support for string collections - * and individual key arguments. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {number[]|string[]} props The property names or indexes of elements to pick. - * @returns {Array} Returns the new array of picked elements. - */ - function baseAt(collection, props) { - var index = -1, - isNil = collection == null, - isArr = !isNil && isArrayLike(collection), - length = isArr ? collection.length : 0, - propsLength = props.length, - result = Array(propsLength); - - while(++index < propsLength) { - var key = props[index]; - if (isArr) { - result[index] = isIndex(key, length) ? collection[key] : undefined; - } else { - result[index] = isNil ? undefined : collection[key]; - } - } - return result; - } - - /** - * Copies properties of `source` to `object`. - * - * @private - * @param {Object} source The object to copy properties from. - * @param {Array} props The property names to copy. - * @param {Object} [object={}] The object to copy properties to. - * @returns {Object} Returns `object`. - */ - function baseCopy(source, props, object) { - object || (object = {}); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - object[key] = source[key]; - } - return object; - } - - /** - * The base implementation of `_.callback` which supports specifying the - * number of arguments to provide to `func`. - * - * @private - * @param {*} [func=_.identity] The value to convert to a callback. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ - function baseCallback(func, thisArg, argCount) { - var type = typeof func; - if (type == 'function') { - return thisArg === undefined - ? func - : bindCallback(func, thisArg, argCount); - } - if (func == null) { - return identity; - } - if (type == 'object') { - return baseMatches(func); - } - return thisArg === undefined - ? property(func) - : baseMatchesProperty(func, thisArg); - } - - /** - * The base implementation of `_.clone` without support for argument juggling - * and `this` binding `customizer` functions. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The object `value` belongs to. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates clones with source counterparts. - * @returns {*} Returns the cloned value. - */ - function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { - var result; - if (customizer) { - result = object ? customizer(value, key, object) : customizer(value); - } - if (result !== undefined) { - return result; - } - if (!isObject(value)) { - return value; - } - var isArr = isArray(value); - if (isArr) { - result = initCloneArray(value); - if (!isDeep) { - return arrayCopy(value, result); - } - } else { - var tag = objToString.call(value), - isFunc = tag == funcTag; - - if (tag == objectTag || tag == argsTag || (isFunc && !object)) { - result = initCloneObject(isFunc ? {} : value); - if (!isDeep) { - return baseAssign(result, value); - } - } else { - return cloneableTags[tag] - ? initCloneByTag(value, tag, isDeep) - : (object ? value : {}); - } - } - // Check for circular references and return its corresponding clone. - stackA || (stackA = []); - stackB || (stackB = []); - - var length = stackA.length; - while (length--) { - if (stackA[length] == value) { - return stackB[length]; - } - } - // Add the source value to the stack of traversed objects and associate it with its clone. - stackA.push(value); - stackB.push(result); - - // Recursively populate clone (susceptible to call stack limits). - (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { - result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); - }); - return result; - } - - /** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} prototype The object to inherit from. - * @returns {Object} Returns the new object. - */ - var baseCreate = (function() { - function object() {} - return function(prototype) { - if (isObject(prototype)) { - object.prototype = prototype; - var result = new object; - object.prototype = undefined; - } - return result || {}; - }; - }()); - - /** - * The base implementation of `_.delay` and `_.defer` which accepts an index - * of where to slice the arguments to provide to `func`. - * - * @private - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {Object} args The arguments provide to `func`. - * @returns {number} Returns the timer id. - */ - function baseDelay(func, wait, args) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return setTimeout(function() { func.apply(undefined, args); }, wait); - } - - /** - * The base implementation of `_.difference` which accepts a single array - * of values to exclude. - * - * @private - * @param {Array} array The array to inspect. - * @param {Array} values The values to exclude. - * @returns {Array} Returns the new array of filtered values. - */ - function baseDifference(array, values) { - var length = array ? array.length : 0, - result = []; - - if (!length) { - return result; - } - var index = -1, - indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf, - cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null, - valuesLength = values.length; - - if (cache) { - indexOf = cacheIndexOf; - isCommon = false; - values = cache; - } - outer: - while (++index < length) { - var value = array[index]; - - if (isCommon && value === value) { - var valuesIndex = valuesLength; - while (valuesIndex--) { - if (values[valuesIndex] === value) { - continue outer; - } - } - result.push(value); - } - else if (indexOf(values, value, 0) < 0) { - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.forEach` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object|string} Returns `collection`. - */ - var baseEach = createBaseEach(baseForOwn); - - /** - * The base implementation of `_.forEachRight` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object|string} Returns `collection`. - */ - var baseEachRight = createBaseEach(baseForOwnRight, true); - - /** - * The base implementation of `_.every` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false` - */ - function baseEvery(collection, predicate) { - var result = true; - baseEach(collection, function(value, index, collection) { - result = !!predicate(value, index, collection); - return result; - }); - return result; - } - - /** - * Gets the extremum value of `collection` invoking `iteratee` for each value - * in `collection` to generate the criterion by which the value is ranked. - * The `iteratee` is invoked with three arguments: (value, index|key, collection). - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {*} Returns the extremum value. - */ - function baseExtremum(collection, iteratee, comparator, exValue) { - var computed = exValue, - result = computed; - - baseEach(collection, function(value, index, collection) { - var current = +iteratee(value, index, collection); - if (comparator(current, computed) || (current === exValue && current === result)) { - computed = current; - result = value; - } - }); - return result; - } - - /** - * The base implementation of `_.fill` without an iteratee call guard. - * - * @private - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - */ - function baseFill(array, value, start, end) { - var length = array.length; - - start = start == null ? 0 : (+start || 0); - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = (end === undefined || end > length) ? length : (+end || 0); - if (end < 0) { - end += length; - } - length = start > end ? 0 : (end >>> 0); - start >>>= 0; - - while (start < length) { - array[start++] = value; - } - return array; - } - - /** - * The base implementation of `_.filter` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function baseFilter(collection, predicate) { - var result = []; - baseEach(collection, function(value, index, collection) { - if (predicate(value, index, collection)) { - result.push(value); - } - }); - return result; - } - - /** - * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, - * without support for callback shorthands and `this` binding, which iterates - * over `collection` using the provided `eachFunc`. - * - * @private - * @param {Array|Object|string} collection The collection to search. - * @param {Function} predicate The function invoked per iteration. - * @param {Function} eachFunc The function to iterate over `collection`. - * @param {boolean} [retKey] Specify returning the key of the found element - * instead of the element itself. - * @returns {*} Returns the found element or its key, else `undefined`. - */ - function baseFind(collection, predicate, eachFunc, retKey) { - var result; - eachFunc(collection, function(value, key, collection) { - if (predicate(value, key, collection)) { - result = retKey ? key : value; - return false; - } - }); - return result; - } - - /** - * The base implementation of `_.flatten` with added support for restricting - * flattening and specifying the start index. - * - * @private - * @param {Array} array The array to flatten. - * @param {boolean} [isDeep] Specify a deep flatten. - * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. - * @param {Array} [result=[]] The initial result value. - * @returns {Array} Returns the new flattened array. - */ - function baseFlatten(array, isDeep, isStrict, result) { - result || (result = []); - - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index]; - if (isObjectLike(value) && isArrayLike(value) && - (isStrict || isArray(value) || isArguments(value))) { - if (isDeep) { - // Recursively flatten arrays (susceptible to call stack limits). - baseFlatten(value, isDeep, isStrict, result); - } else { - arrayPush(result, value); - } - } else if (!isStrict) { - result[result.length] = value; - } - } - return result; - } - - /** - * The base implementation of `baseForIn` and `baseForOwn` which iterates - * over `object` properties returned by `keysFunc` invoking `iteratee` for - * each property. Iteratee functions may exit iteration early by explicitly - * returning `false`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseFor = createBaseFor(); - - /** - * This function is like `baseFor` except that it iterates over properties - * in the opposite order. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseForRight = createBaseFor(true); - - /** - * The base implementation of `_.forIn` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForIn(object, iteratee) { - return baseFor(object, iteratee, keysIn); - } - - /** - * The base implementation of `_.forOwn` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwn(object, iteratee) { - return baseFor(object, iteratee, keys); - } - - /** - * The base implementation of `_.forOwnRight` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwnRight(object, iteratee) { - return baseForRight(object, iteratee, keys); - } - - /** - * The base implementation of `_.functions` which creates an array of - * `object` function property names filtered from those provided. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} props The property names to filter. - * @returns {Array} Returns the new array of filtered property names. - */ - function baseFunctions(object, props) { - var index = -1, - length = props.length, - resIndex = -1, - result = []; - - while (++index < length) { - var key = props[index]; - if (isFunction(object[key])) { - result[++resIndex] = key; - } - } - return result; - } - - /** - * The base implementation of `get` without support for string paths - * and default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array} path The path of the property to get. - * @param {string} [pathKey] The key representation of path. - * @returns {*} Returns the resolved value. - */ - function baseGet(object, path, pathKey) { - if (object == null) { - return; - } - if (pathKey !== undefined && pathKey in toObject(object)) { - path = [pathKey]; - } - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[path[index++]]; - } - return (index && index == length) ? object : undefined; - } - - /** - * The base implementation of `_.isEqual` without support for `this` binding - * `customizer` functions. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparing values. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) { - if (value === other) { - return true; - } - if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB); - } - - /** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing objects. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA=[]] Tracks traversed `value` objects. - * @param {Array} [stackB=[]] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = arrayTag, - othTag = arrayTag; - - if (!objIsArr) { - objTag = objToString.call(object); - if (objTag == argsTag) { - objTag = objectTag; - } else if (objTag != objectTag) { - objIsArr = isTypedArray(object); - } - } - if (!othIsArr) { - othTag = objToString.call(other); - if (othTag == argsTag) { - othTag = objectTag; - } else if (othTag != objectTag) { - othIsArr = isTypedArray(other); - } - } - var objIsObj = objTag == objectTag, - othIsObj = othTag == objectTag, - isSameTag = objTag == othTag; - - if (isSameTag && !(objIsArr || objIsObj)) { - return equalByTag(object, other, objTag); - } - if (!isLoose) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); - - if (objIsWrapped || othIsWrapped) { - return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); - } - } - if (!isSameTag) { - return false; - } - // Assume cyclic values are equal. - // For more information on detecting circular references see https://es5.github.io/#JO. - stackA || (stackA = []); - stackB || (stackB = []); - - var length = stackA.length; - while (length--) { - if (stackA[length] == object) { - return stackB[length] == other; - } - } - // Add `object` and `other` to the stack of traversed objects. - stackA.push(object); - stackB.push(other); - - var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB); - - stackA.pop(); - stackB.pop(); - - return result; - } - - /** - * The base implementation of `_.isMatch` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} matchData The propery names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparing objects. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ - function baseIsMatch(object, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; - } - object = toObject(object); - while (index--) { - var data = matchData[index]; - if ((noCustomizer && data[2]) - ? data[1] !== object[data[0]] - : !(data[0] in object) - ) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var result = customizer ? customizer(objValue, srcValue, key) : undefined; - if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) { - return false; - } - } - } - return true; - } - - /** - * The base implementation of `_.map` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function baseMap(collection, iteratee) { - var index = -1, - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value, key, collection) { - result[++index] = iteratee(value, key, collection); - }); - return result; - } - - /** - * The base implementation of `_.matches` which does not clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new function. - */ - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - var key = matchData[0][0], - value = matchData[0][1]; - - return function(object) { - if (object == null) { - return false; - } - return object[key] === value && (value !== undefined || (key in toObject(object))); - }; - } - return function(object) { - return baseIsMatch(object, matchData); - }; - } - - /** - * The base implementation of `_.matchesProperty` which does not clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to compare. - * @returns {Function} Returns the new function. - */ - function baseMatchesProperty(path, srcValue) { - var isArr = isArray(path), - isCommon = isKey(path) && isStrictComparable(srcValue), - pathKey = (path + ''); - - path = toPath(path); - return function(object) { - if (object == null) { - return false; - } - var key = pathKey; - object = toObject(object); - if ((isArr || !isCommon) && !(key in object)) { - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - if (object == null) { - return false; - } - key = last(path); - object = toObject(object); - } - return object[key] === srcValue - ? (srcValue !== undefined || (key in object)) - : baseIsEqual(srcValue, object[key], undefined, true); - }; - } - - /** - * The base implementation of `_.merge` without support for argument juggling, - * multiple sources, and `this` binding `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {Function} [customizer] The function to customize merged values. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {Object} Returns `object`. - */ - function baseMerge(object, source, customizer, stackA, stackB) { - if (!isObject(object)) { - return object; - } - var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), - props = isSrcArr ? undefined : keys(source); - - arrayEach(props || source, function(srcValue, key) { - if (props) { - key = srcValue; - srcValue = source[key]; - } - if (isObjectLike(srcValue)) { - stackA || (stackA = []); - stackB || (stackB = []); - baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); - } - else { - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = result === undefined; - - if (isCommon) { - result = srcValue; - } - if ((result !== undefined || (isSrcArr && !(key in object))) && - (isCommon || (result === result ? (result !== value) : (value === value)))) { - object[key] = result; - } - } - }); - return object; - } - - /** - * A specialized version of `baseMerge` for arrays and objects which performs - * deep merges and tracks traversed objects enabling objects with circular - * references to be merged. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {string} key The key of the value to merge. - * @param {Function} mergeFunc The function to merge values. - * @param {Function} [customizer] The function to customize merged values. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { - var length = stackA.length, - srcValue = source[key]; - - while (length--) { - if (stackA[length] == srcValue) { - object[key] = stackB[length]; - return; - } - } - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = result === undefined; - - if (isCommon) { - result = srcValue; - if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { - result = isArray(value) - ? value - : (isArrayLike(value) ? arrayCopy(value) : []); - } - else if (isPlainObject(srcValue) || isArguments(srcValue)) { - result = isArguments(value) - ? toPlainObject(value) - : (isPlainObject(value) ? value : {}); - } - else { - isCommon = false; - } - } - // Add the source value to the stack of traversed objects and associate - // it with its merged value. - stackA.push(srcValue); - stackB.push(result); - - if (isCommon) { - // Recursively merge objects and arrays (susceptible to call stack limits). - object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); - } else if (result === result ? (result !== value) : (value === value)) { - object[key] = result; - } - } - - /** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new function. - */ - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - - /** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new function. - */ - function basePropertyDeep(path) { - var pathKey = (path + ''); - path = toPath(path); - return function(object) { - return baseGet(object, path, pathKey); - }; - } - - /** - * The base implementation of `_.pullAt` without support for individual - * index arguments and capturing the removed elements. - * - * @private - * @param {Array} array The array to modify. - * @param {number[]} indexes The indexes of elements to remove. - * @returns {Array} Returns `array`. - */ - function basePullAt(array, indexes) { - var length = array ? indexes.length : 0; - while (length--) { - var index = indexes[length]; - if (index != previous && isIndex(index)) { - var previous = index; - splice.call(array, index, 1); - } - } - return array; - } - - /** - * The base implementation of `_.random` without support for argument juggling - * and returning floating-point numbers. - * - * @private - * @param {number} min The minimum possible value. - * @param {number} max The maximum possible value. - * @returns {number} Returns the random number. - */ - function baseRandom(min, max) { - return min + nativeFloor(nativeRandom() * (max - min + 1)); - } - - /** - * The base implementation of `_.reduce` and `_.reduceRight` without support - * for callback shorthands and `this` binding, which iterates over `collection` - * using the provided `eachFunc`. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} accumulator The initial value. - * @param {boolean} initFromCollection Specify using the first or last element - * of `collection` as the initial value. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the accumulated value. - */ - function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { - eachFunc(collection, function(value, index, collection) { - accumulator = initFromCollection - ? (initFromCollection = false, value) - : iteratee(accumulator, value, index, collection); - }); - return accumulator; - } - - /** - * The base implementation of `setData` without support for hot loop detection. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var baseSetData = !metaMap ? identity : function(func, data) { - metaMap.set(func, data); - return func; - }; - - /** - * The base implementation of `_.slice` without an iteratee call guard. - * - * @private - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function baseSlice(array, start, end) { - var index = -1, - length = array.length; - - start = start == null ? 0 : (+start || 0); - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = (end === undefined || end > length) ? length : (+end || 0); - if (end < 0) { - end += length; - } - length = start > end ? 0 : ((end - start) >>> 0); - start >>>= 0; - - var result = Array(length); - while (++index < length) { - result[index] = array[index + start]; - } - return result; - } - - /** - * The base implementation of `_.some` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function baseSome(collection, predicate) { - var result; - - baseEach(collection, function(value, index, collection) { - result = predicate(value, index, collection); - return !result; - }); - return !!result; - } - - /** - * The base implementation of `_.sortBy` which uses `comparer` to define - * the sort order of `array` and replaces criteria objects with their - * corresponding values. - * - * @private - * @param {Array} array The array to sort. - * @param {Function} comparer The function to define sort order. - * @returns {Array} Returns `array`. - */ - function baseSortBy(array, comparer) { - var length = array.length; - - array.sort(comparer); - while (length--) { - array[length] = array[length].value; - } - return array; - } - - /** - * The base implementation of `_.sortByOrder` without param guards. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {boolean[]} orders The sort orders of `iteratees`. - * @returns {Array} Returns the new sorted array. - */ - function baseSortByOrder(collection, iteratees, orders) { - var callback = getCallback(), - index = -1; - - iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); - - var result = baseMap(collection, function(value) { - var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); - return { 'criteria': criteria, 'index': ++index, 'value': value }; - }); - - return baseSortBy(result, function(object, other) { - return compareMultiple(object, other, orders); - }); - } - - /** - * The base implementation of `_.sum` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ - function baseSum(collection, iteratee) { - var result = 0; - baseEach(collection, function(value, index, collection) { - result += +iteratee(value, index, collection) || 0; - }); - return result; - } - - /** - * The base implementation of `_.uniq` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The function invoked per iteration. - * @returns {Array} Returns the new duplicate-value-free array. - */ - function baseUniq(array, iteratee) { - var index = -1, - indexOf = getIndexOf(), - length = array.length, - isCommon = indexOf == baseIndexOf, - isLarge = isCommon && length >= LARGE_ARRAY_SIZE, - seen = isLarge ? createCache() : null, - result = []; - - if (seen) { - indexOf = cacheIndexOf; - isCommon = false; - } else { - isLarge = false; - seen = iteratee ? [] : result; - } - outer: - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value, index, array) : value; - - if (isCommon && value === value) { - var seenIndex = seen.length; - while (seenIndex--) { - if (seen[seenIndex] === computed) { - continue outer; - } - } - if (iteratee) { - seen.push(computed); - } - result.push(value); - } - else if (indexOf(seen, computed, 0) < 0) { - if (iteratee || isLarge) { - seen.push(computed); - } - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.values` and `_.valuesIn` which creates an - * array of `object` property values corresponding to the property names - * of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the array of property values. - */ - function baseValues(object, props) { - var index = -1, - length = props.length, - result = Array(length); - - while (++index < length) { - result[index] = object[props[index]]; - } - return result; - } - - /** - * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`, - * and `_.takeWhile` without support for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [isDrop] Specify dropping elements instead of taking them. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the slice of `array`. - */ - function baseWhile(array, predicate, isDrop, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} - return isDrop - ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) - : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); - } - - /** - * The base implementation of `wrapperValue` which returns the result of - * performing a sequence of actions on the unwrapped `value`, where each - * successive action is supplied the return value of the previous. - * - * @private - * @param {*} value The unwrapped value. - * @param {Array} actions Actions to peform to resolve the unwrapped value. - * @returns {*} Returns the resolved value. - */ - function baseWrapperValue(value, actions) { - var result = value; - if (result instanceof LazyWrapper) { - result = result.value(); - } - var index = -1, - length = actions.length; - - while (++index < length) { - var action = actions[index]; - result = action.func.apply(action.thisArg, arrayPush([result], action.args)); - } - return result; - } - - /** - * Performs a binary search of `array` to determine the index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function binaryIndex(array, value, retHighest) { - var low = 0, - high = array ? array.length : low; - - if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { - while (low < high) { - var mid = (low + high) >>> 1, - computed = array[mid]; - - if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { - low = mid + 1; - } else { - high = mid; - } - } - return high; - } - return binaryIndexBy(array, value, identity, retHighest); - } - - /** - * This function is like `binaryIndex` except that it invokes `iteratee` for - * `value` and each element of `array` to compute their sort ranking. The - * iteratee is invoked with one argument; (value). - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The function invoked per iteration. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function binaryIndexBy(array, value, iteratee, retHighest) { - value = iteratee(value); - - var low = 0, - high = array ? array.length : 0, - valIsNaN = value !== value, - valIsNull = value === null, - valIsUndef = value === undefined; - - while (low < high) { - var mid = nativeFloor((low + high) / 2), - computed = iteratee(array[mid]), - isDef = computed !== undefined, - isReflexive = computed === computed; - - if (valIsNaN) { - var setLow = isReflexive || retHighest; - } else if (valIsNull) { - setLow = isReflexive && isDef && (retHighest || computed != null); - } else if (valIsUndef) { - setLow = isReflexive && (retHighest || isDef); - } else if (computed == null) { - setLow = false; - } else { - setLow = retHighest ? (computed <= value) : (computed < value); - } - if (setLow) { - low = mid + 1; - } else { - high = mid; - } - } - return nativeMin(high, MAX_ARRAY_INDEX); - } - - /** - * A specialized version of `baseCallback` which only supports `this` binding - * and specifying the number of arguments to provide to `func`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ - function bindCallback(func, thisArg, argCount) { - if (typeof func != 'function') { - return identity; - } - if (thisArg === undefined) { - return func; - } - switch (argCount) { - case 1: return function(value) { - return func.call(thisArg, value); - }; - case 3: return function(value, index, collection) { - return func.call(thisArg, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - case 5: return function(value, other, key, object, source) { - return func.call(thisArg, value, other, key, object, source); - }; - } - return function() { - return func.apply(thisArg, arguments); - }; - } - - /** - * Creates a clone of the given array buffer. - * - * @private - * @param {ArrayBuffer} buffer The array buffer to clone. - * @returns {ArrayBuffer} Returns the cloned array buffer. - */ - function bufferClone(buffer) { - var result = new ArrayBuffer(buffer.byteLength), - view = new Uint8Array(result); - - view.set(new Uint8Array(buffer)); - return result; - } - - /** - * Creates an array that is the composition of partially applied arguments, - * placeholders, and provided arguments into a single array of arguments. - * - * @private - * @param {Array|Object} args The provided arguments. - * @param {Array} partials The arguments to prepend to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgs(args, partials, holders) { - var holdersLength = holders.length, - argsIndex = -1, - argsLength = nativeMax(args.length - holdersLength, 0), - leftIndex = -1, - leftLength = partials.length, - result = Array(leftLength + argsLength); - - while (++leftIndex < leftLength) { - result[leftIndex] = partials[leftIndex]; - } - while (++argsIndex < holdersLength) { - result[holders[argsIndex]] = args[argsIndex]; - } - while (argsLength--) { - result[leftIndex++] = args[argsIndex++]; - } - return result; - } - - /** - * This function is like `composeArgs` except that the arguments composition - * is tailored for `_.partialRight`. - * - * @private - * @param {Array|Object} args The provided arguments. - * @param {Array} partials The arguments to append to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgsRight(args, partials, holders) { - var holdersIndex = -1, - holdersLength = holders.length, - argsIndex = -1, - argsLength = nativeMax(args.length - holdersLength, 0), - rightIndex = -1, - rightLength = partials.length, - result = Array(argsLength + rightLength); - - while (++argsIndex < argsLength) { - result[argsIndex] = args[argsIndex]; - } - var offset = argsIndex; - while (++rightIndex < rightLength) { - result[offset + rightIndex] = partials[rightIndex]; - } - while (++holdersIndex < holdersLength) { - result[offset + holders[holdersIndex]] = args[argsIndex++]; - } - return result; - } - - /** - * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function. - * - * @private - * @param {Function} setter The function to set keys and values of the accumulator object. - * @param {Function} [initializer] The function to initialize the accumulator object. - * @returns {Function} Returns the new aggregator function. - */ - function createAggregator(setter, initializer) { - return function(collection, iteratee, thisArg) { - var result = initializer ? initializer() : {}; - iteratee = getCallback(iteratee, thisArg, 3); - - if (isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - var value = collection[index]; - setter(result, value, iteratee(value, index, collection), collection); - } - } else { - baseEach(collection, function(value, key, collection) { - setter(result, value, iteratee(value, key, collection), collection); - }); - } - return result; - }; - } - - /** - * Creates a `_.assign`, `_.defaults`, or `_.merge` function. - * - * @private - * @param {Function} assigner The function to assign values. - * @returns {Function} Returns the new assigner function. - */ - function createAssigner(assigner) { - return restParam(function(object, sources) { - var index = -1, - length = object == null ? 0 : sources.length, - customizer = length > 2 ? sources[length - 2] : undefined, - guard = length > 2 ? sources[2] : undefined, - thisArg = length > 1 ? sources[length - 1] : undefined; - - if (typeof customizer == 'function') { - customizer = bindCallback(customizer, thisArg, 5); - length -= 2; - } else { - customizer = typeof thisArg == 'function' ? thisArg : undefined; - length -= (customizer ? 1 : 0); - } - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - customizer = length < 3 ? undefined : customizer; - length = 1; - } - while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, customizer); - } - } - return object; - }); - } - - /** - * Creates a `baseEach` or `baseEachRight` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseEach(eachFunc, fromRight) { - return function(collection, iteratee) { - var length = collection ? getLength(collection) : 0; - if (!isLength(length)) { - return eachFunc(collection, iteratee); - } - var index = fromRight ? length : -1, - iterable = toObject(collection); - - while ((fromRight ? index-- : ++index < length)) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; - } - - /** - * Creates a base function for `_.forIn` or `_.forInRight`. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var iterable = toObject(object), - props = keysFunc(object), - length = props.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length)) { - var key = props[index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; - } - - /** - * Creates a function that wraps `func` and invokes it with the `this` - * binding of `thisArg`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} [thisArg] The `this` binding of `func`. - * @returns {Function} Returns the new bound function. - */ - function createBindWrapper(func, thisArg) { - var Ctor = createCtorWrapper(func); - - function wrapper() { - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return fn.apply(thisArg, arguments); - } - return wrapper; - } - - /** - * Creates a `Set` cache object to optimize linear searches of large arrays. - * - * @private - * @param {Array} [values] The values to cache. - * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. - */ - function createCache(values) { - return (nativeCreate && Set) ? new SetCache(values) : null; - } - - /** - * Creates a function that produces compound words out of the words in a - * given string. - * - * @private - * @param {Function} callback The function to combine each word. - * @returns {Function} Returns the new compounder function. - */ - function createCompounder(callback) { - return function(string) { - var index = -1, - array = words(deburr(string)), - length = array.length, - result = ''; - - while (++index < length) { - result = callback(result, array[index], index); - } - return result; - }; - } - - /** - * Creates a function that produces an instance of `Ctor` regardless of - * whether it was invoked as part of a `new` expression or by `call` or `apply`. - * - * @private - * @param {Function} Ctor The constructor to wrap. - * @returns {Function} Returns the new wrapped function. - */ - function createCtorWrapper(Ctor) { - return function() { - // Use a `switch` statement to work with class constructors. - // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist - // for more details. - var args = arguments; - switch (args.length) { - case 0: return new Ctor; - case 1: return new Ctor(args[0]); - case 2: return new Ctor(args[0], args[1]); - case 3: return new Ctor(args[0], args[1], args[2]); - case 4: return new Ctor(args[0], args[1], args[2], args[3]); - case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); - case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - } - var thisBinding = baseCreate(Ctor.prototype), - result = Ctor.apply(thisBinding, args); - - // Mimic the constructor's `return` behavior. - // See https://es5.github.io/#x13.2.2 for more details. - return isObject(result) ? result : thisBinding; - }; - } - - /** - * Creates a `_.curry` or `_.curryRight` function. - * - * @private - * @param {boolean} flag The curry bit flag. - * @returns {Function} Returns the new curry function. - */ - function createCurry(flag) { - function curryFunc(func, arity, guard) { - if (guard && isIterateeCall(func, arity, guard)) { - arity = undefined; - } - var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curryFunc.placeholder; - return result; - } - return curryFunc; - } - - /** - * Creates a `_.defaults` or `_.defaultsDeep` function. - * - * @private - * @param {Function} assigner The function to assign values. - * @param {Function} customizer The function to customize assigned values. - * @returns {Function} Returns the new defaults function. - */ - function createDefaults(assigner, customizer) { - return restParam(function(args) { - var object = args[0]; - if (object == null) { - return object; - } - args.push(customizer); - return assigner.apply(undefined, args); - }); - } - - /** - * Creates a `_.max` or `_.min` function. - * - * @private - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {Function} Returns the new extremum function. - */ - function createExtremum(comparator, exValue) { - return function(collection, iteratee, thisArg) { - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { - iteratee = undefined; - } - iteratee = getCallback(iteratee, thisArg, 3); - if (iteratee.length == 1) { - collection = isArray(collection) ? collection : toIterable(collection); - var result = arrayExtremum(collection, iteratee, comparator, exValue); - if (!(collection.length && result === exValue)) { - return result; - } - } - return baseExtremum(collection, iteratee, comparator, exValue); - }; - } - - /** - * Creates a `_.find` or `_.findLast` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new find function. - */ - function createFind(eachFunc, fromRight) { - return function(collection, predicate, thisArg) { - predicate = getCallback(predicate, thisArg, 3); - if (isArray(collection)) { - var index = baseFindIndex(collection, predicate, fromRight); - return index > -1 ? collection[index] : undefined; - } - return baseFind(collection, predicate, eachFunc); - }; - } - - /** - * Creates a `_.findIndex` or `_.findLastIndex` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new find function. - */ - function createFindIndex(fromRight) { - return function(array, predicate, thisArg) { - if (!(array && array.length)) { - return -1; - } - predicate = getCallback(predicate, thisArg, 3); - return baseFindIndex(array, predicate, fromRight); - }; - } - - /** - * Creates a `_.findKey` or `_.findLastKey` function. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new find function. - */ - function createFindKey(objectFunc) { - return function(object, predicate, thisArg) { - predicate = getCallback(predicate, thisArg, 3); - return baseFind(object, predicate, objectFunc, true); - }; - } - - /** - * Creates a `_.flow` or `_.flowRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new flow function. - */ - function createFlow(fromRight) { - return function() { - var wrapper, - length = arguments.length, - index = fromRight ? length : -1, - leftIndex = 0, - funcs = Array(length); - - while ((fromRight ? index-- : ++index < length)) { - var func = funcs[leftIndex++] = arguments[index]; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') { - wrapper = new LodashWrapper([], true); - } - } - index = wrapper ? -1 : length; - while (++index < length) { - func = funcs[index]; - - var funcName = getFuncName(func), - data = funcName == 'wrapper' ? getData(func) : undefined; - - if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) { - wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); - } else { - wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); - } - } - return function() { - var args = arguments, - value = args[0]; - - if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) { - return wrapper.plant(value).value(); - } - var index = 0, - result = length ? funcs[index].apply(this, args) : value; - - while (++index < length) { - result = funcs[index].call(this, result); - } - return result; - }; - }; - } - - /** - * Creates a function for `_.forEach` or `_.forEachRight`. - * - * @private - * @param {Function} arrayFunc The function to iterate over an array. - * @param {Function} eachFunc The function to iterate over a collection. - * @returns {Function} Returns the new each function. - */ - function createForEach(arrayFunc, eachFunc) { - return function(collection, iteratee, thisArg) { - return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) - ? arrayFunc(collection, iteratee) - : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); - }; - } - - /** - * Creates a function for `_.forIn` or `_.forInRight`. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new each function. - */ - function createForIn(objectFunc) { - return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || thisArg !== undefined) { - iteratee = bindCallback(iteratee, thisArg, 3); - } - return objectFunc(object, iteratee, keysIn); - }; - } - - /** - * Creates a function for `_.forOwn` or `_.forOwnRight`. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new each function. - */ - function createForOwn(objectFunc) { - return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || thisArg !== undefined) { - iteratee = bindCallback(iteratee, thisArg, 3); - } - return objectFunc(object, iteratee); - }; - } - - /** - * Creates a function for `_.mapKeys` or `_.mapValues`. - * - * @private - * @param {boolean} [isMapKeys] Specify mapping keys instead of values. - * @returns {Function} Returns the new map function. - */ - function createObjectMapper(isMapKeys) { - return function(object, iteratee, thisArg) { - var result = {}; - iteratee = getCallback(iteratee, thisArg, 3); - - baseForOwn(object, function(value, key, object) { - var mapped = iteratee(value, key, object); - key = isMapKeys ? mapped : key; - value = isMapKeys ? value : mapped; - result[key] = value; - }); - return result; - }; - } - - /** - * Creates a function for `_.padLeft` or `_.padRight`. - * - * @private - * @param {boolean} [fromRight] Specify padding from the right. - * @returns {Function} Returns the new pad function. - */ - function createPadDir(fromRight) { - return function(string, length, chars) { - string = baseToString(string); - return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string); - }; - } - - /** - * Creates a `_.partial` or `_.partialRight` function. - * - * @private - * @param {boolean} flag The partial bit flag. - * @returns {Function} Returns the new partial function. - */ - function createPartial(flag) { - var partialFunc = restParam(function(func, partials) { - var holders = replaceHolders(partials, partialFunc.placeholder); - return createWrapper(func, flag, undefined, partials, holders); - }); - return partialFunc; - } - - /** - * Creates a function for `_.reduce` or `_.reduceRight`. - * - * @private - * @param {Function} arrayFunc The function to iterate over an array. - * @param {Function} eachFunc The function to iterate over a collection. - * @returns {Function} Returns the new each function. - */ - function createReduce(arrayFunc, eachFunc) { - return function(collection, iteratee, accumulator, thisArg) { - var initFromArray = arguments.length < 3; - return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) - ? arrayFunc(collection, iteratee, accumulator, initFromArray) - : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); - }; - } - - /** - * Creates a function that wraps `func` and invokes it with optional `this` - * binding of, partial application, and currying. - * - * @private - * @param {Function|string} func The function or method name to reference. - * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [partialsRight] The arguments to append to those provided to the new function. - * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { - var isAry = bitmask & ARY_FLAG, - isBind = bitmask & BIND_FLAG, - isBindKey = bitmask & BIND_KEY_FLAG, - isCurry = bitmask & CURRY_FLAG, - isCurryBound = bitmask & CURRY_BOUND_FLAG, - isCurryRight = bitmask & CURRY_RIGHT_FLAG, - Ctor = isBindKey ? undefined : createCtorWrapper(func); - - function wrapper() { - // Avoid `arguments` object use disqualifying optimizations by - // converting it to an array before providing it to other functions. - var length = arguments.length, - index = length, - args = Array(length); - - while (index--) { - args[index] = arguments[index]; - } - if (partials) { - args = composeArgs(args, partials, holders); - } - if (partialsRight) { - args = composeArgsRight(args, partialsRight, holdersRight); - } - if (isCurry || isCurryRight) { - var placeholder = wrapper.placeholder, - argsHolders = replaceHolders(args, placeholder); - - length -= argsHolders.length; - if (length < arity) { - var newArgPos = argPos ? arrayCopy(argPos) : undefined, - newArity = nativeMax(arity - length, 0), - newsHolders = isCurry ? argsHolders : undefined, - newHoldersRight = isCurry ? undefined : argsHolders, - newPartials = isCurry ? args : undefined, - newPartialsRight = isCurry ? undefined : args; - - bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); - bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); - - if (!isCurryBound) { - bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); - } - var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity], - result = createHybridWrapper.apply(undefined, newData); - - if (isLaziable(func)) { - setData(result, newData); - } - result.placeholder = placeholder; - return result; - } - } - var thisBinding = isBind ? thisArg : this, - fn = isBindKey ? thisBinding[func] : func; - - if (argPos) { - args = reorder(args, argPos); - } - if (isAry && ary < args.length) { - args.length = ary; - } - if (this && this !== root && this instanceof wrapper) { - fn = Ctor || createCtorWrapper(func); - } - return fn.apply(thisBinding, args); - } - return wrapper; - } - - /** - * Creates the padding required for `string` based on the given `length`. - * The `chars` string is truncated if the number of characters exceeds `length`. - * - * @private - * @param {string} string The string to create padding for. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the pad for `string`. - */ - function createPadding(string, length, chars) { - var strLength = string.length; - length = +length; - - if (strLength >= length || !nativeIsFinite(length)) { - return ''; - } - var padLength = length - strLength; - chars = chars == null ? ' ' : (chars + ''); - return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength); - } - - /** - * Creates a function that wraps `func` and invokes it with the optional `this` - * binding of `thisArg` and the `partials` prepended to those provided to - * the wrapper. - * - * @private - * @param {Function} func The function to partially apply arguments to. - * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} partials The arguments to prepend to those provided to the new function. - * @returns {Function} Returns the new bound function. - */ - function createPartialWrapper(func, bitmask, thisArg, partials) { - var isBind = bitmask & BIND_FLAG, - Ctor = createCtorWrapper(func); - - function wrapper() { - // Avoid `arguments` object use disqualifying optimizations by - // converting it to an array before providing it `func`. - var argsIndex = -1, - argsLength = arguments.length, - leftIndex = -1, - leftLength = partials.length, - args = Array(leftLength + argsLength); - - while (++leftIndex < leftLength) { - args[leftIndex] = partials[leftIndex]; - } - while (argsLength--) { - args[leftIndex++] = arguments[++argsIndex]; - } - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return fn.apply(isBind ? thisArg : this, args); - } - return wrapper; - } - - /** - * Creates a `_.ceil`, `_.floor`, or `_.round` function. - * - * @private - * @param {string} methodName The name of the `Math` method to use when rounding. - * @returns {Function} Returns the new round function. - */ - function createRound(methodName) { - var func = Math[methodName]; - return function(number, precision) { - precision = precision === undefined ? 0 : (+precision || 0); - if (precision) { - precision = pow(10, precision); - return func(number * precision) / precision; - } - return func(number); - }; - } - - /** - * Creates a `_.sortedIndex` or `_.sortedLastIndex` function. - * - * @private - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {Function} Returns the new index function. - */ - function createSortedIndex(retHighest) { - return function(array, value, iteratee, thisArg) { - var callback = getCallback(iteratee); - return (iteratee == null && callback === baseCallback) - ? binaryIndex(array, value, retHighest) - : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest); - }; - } - - /** - * Creates a function that either curries or invokes `func` with optional - * `this` binding and partially applied arguments. - * - * @private - * @param {Function|string} func The function or method name to reference. - * @param {number} bitmask The bitmask of flags. - * The bitmask may be composed of the following flags: - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` or `_.curryRight` of a bound function - * 8 - `_.curry` - * 16 - `_.curryRight` - * 32 - `_.partial` - * 64 - `_.partialRight` - * 128 - `_.rearg` - * 256 - `_.ary` - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to be partially applied. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { - var isBindKey = bitmask & BIND_KEY_FLAG; - if (!isBindKey && typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - var length = partials ? partials.length : 0; - if (!length) { - bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); - partials = holders = undefined; - } - length -= (holders ? holders.length : 0); - if (bitmask & PARTIAL_RIGHT_FLAG) { - var partialsRight = partials, - holdersRight = holders; - - partials = holders = undefined; - } - var data = isBindKey ? undefined : getData(func), - newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; - - if (data) { - mergeData(newData, data); - bitmask = newData[1]; - arity = newData[9]; - } - newData[9] = arity == null - ? (isBindKey ? 0 : func.length) - : (nativeMax(arity - length, 0) || 0); - - if (bitmask == BIND_FLAG) { - var result = createBindWrapper(newData[0], newData[2]); - } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { - result = createPartialWrapper.apply(undefined, newData); - } else { - result = createHybridWrapper.apply(undefined, newData); - } - var setter = data ? baseSetData : setData; - return setter(result, newData); - } - - /** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing arrays. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ - function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) { - var index = -1, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isLoose && othLength > arrLength)) { - return false; - } - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index], - result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined; - - if (result !== undefined) { - if (result) { - continue; - } - return false; - } - // Recursively compare arrays (susceptible to call stack limits). - if (isLoose) { - if (!arraySome(other, function(othValue) { - return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB); - })) { - return false; - } - } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) { - return false; - } - } - return true; - } - - /** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalByTag(object, other, tag) { - switch (tag) { - case boolTag: - case dateTag: - // Coerce dates and booleans to numbers, dates to milliseconds and booleans - // to `1` or `0` treating invalid dates coerced to `NaN` as not equal. - return +object == +other; - - case errorTag: - return object.name == other.name && object.message == other.message; - - case numberTag: - // Treat `NaN` vs. `NaN` as equal. - return (object != +object) - ? other != +other - : object == +other; - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings primitives and string - // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. - return object == (other + ''); - } - return false; - } - - /** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing values. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) { - var objProps = keys(object), - objLength = objProps.length, - othProps = keys(other), - othLength = othProps.length; - - if (objLength != othLength && !isLoose) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - var skipCtor = isLoose; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key], - result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined; - - // Recursively compare objects (susceptible to call stack limits). - if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) { - return false; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (!skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor == 'function' && objCtor instanceof objCtor && - typeof othCtor == 'function' && othCtor instanceof othCtor)) { - return false; - } - } - return true; - } - - /** - * Gets the appropriate "callback" function. If the `_.callback` method is - * customized this function returns the custom method, otherwise it returns - * the `baseCallback` function. If arguments are provided the chosen function - * is invoked with them and its result is returned. - * - * @private - * @returns {Function} Returns the chosen function or its result. - */ - function getCallback(func, thisArg, argCount) { - var result = lodash.callback || callback; - result = result === callback ? baseCallback : result; - return argCount ? result(func, thisArg, argCount) : result; - } - - /** - * Gets metadata for `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {*} Returns the metadata for `func`. - */ - var getData = !metaMap ? noop : function(func) { - return metaMap.get(func); - }; - - /** - * Gets the name of `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {string} Returns the function name. - */ - function getFuncName(func) { - var result = func.name, - array = realNames[result], - length = array ? array.length : 0; - - while (length--) { - var data = array[length], - otherFunc = data.func; - if (otherFunc == null || otherFunc == func) { - return data.name; - } - } - return result; - } - - /** - * Gets the appropriate "indexOf" function. If the `_.indexOf` method is - * customized this function returns the custom method, otherwise it returns - * the `baseIndexOf` function. If arguments are provided the chosen function - * is invoked with them and its result is returned. - * - * @private - * @returns {Function|number} Returns the chosen function or its result. - */ - function getIndexOf(collection, target, fromIndex) { - var result = lodash.indexOf || indexOf; - result = result === indexOf ? baseIndexOf : result; - return collection ? result(collection, target, fromIndex) : result; - } - - /** - * Gets the "length" property value of `object`. - * - * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) - * that affects Safari on at least iOS 8.1-8.3 ARM64. - * - * @private - * @param {Object} object The object to query. - * @returns {*} Returns the "length" value. - */ - var getLength = baseProperty('length'); - - /** - * Gets the propery names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ - function getMatchData(object) { - var result = pairs(object), - length = result.length; - - while (length--) { - result[length][2] = isStrictComparable(result[length][1]); - } - return result; - } - - /** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ - function getNative(object, key) { - var value = object == null ? undefined : object[key]; - return isNative(value) ? value : undefined; - } - - /** - * Gets the view, applying any `transforms` to the `start` and `end` positions. - * - * @private - * @param {number} start The start of the view. - * @param {number} end The end of the view. - * @param {Array} transforms The transformations to apply to the view. - * @returns {Object} Returns an object containing the `start` and `end` - * positions of the view. - */ - function getView(start, end, transforms) { - var index = -1, - length = transforms.length; - - while (++index < length) { - var data = transforms[index], - size = data.size; - - switch (data.type) { - case 'drop': start += size; break; - case 'dropRight': end -= size; break; - case 'take': end = nativeMin(end, start + size); break; - case 'takeRight': start = nativeMax(start, end - size); break; - } - } - return { 'start': start, 'end': end }; - } - - /** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ - function initCloneArray(array) { - var length = array.length, - result = new array.constructor(length); - - // Add array properties assigned by `RegExp#exec`. - if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { - result.index = array.index; - result.input = array.input; - } - return result; - } - - /** - * Initializes an object clone. - * - * @private - * @param {Object} object The object to clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneObject(object) { - var Ctor = object.constructor; - if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { - Ctor = Object; - } - return new Ctor; - } - - /** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneByTag(object, tag, isDeep) { - var Ctor = object.constructor; - switch (tag) { - case arrayBufferTag: - return bufferClone(object); - - case boolTag: - case dateTag: - return new Ctor(+object); - - case float32Tag: case float64Tag: - case int8Tag: case int16Tag: case int32Tag: - case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: - var buffer = object.buffer; - return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); - - case numberTag: - case stringTag: - return new Ctor(object); - - case regexpTag: - var result = new Ctor(object.source, reFlags.exec(object)); - result.lastIndex = object.lastIndex; - } - return result; - } - - /** - * Invokes the method at `path` on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {Array} args The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - */ - function invokePath(object, path, args) { - if (object != null && !isKey(path, object)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - path = last(path); - } - var func = object == null ? object : object[path]; - return func == null ? undefined : func.apply(object, args); - } - - /** - * Checks if `value` is array-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - */ - function isArrayLike(value) { - return value != null && isLength(getLength(value)); - } - - /** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ - function isIndex(value, length) { - value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; - length = length == null ? MAX_SAFE_INTEGER : length; - return value > -1 && value % 1 == 0 && value < length; - } - - /** - * Checks if the provided arguments are from an iteratee call. - * - * @private - * @param {*} value The potential iteratee value argument. - * @param {*} index The potential iteratee index or key argument. - * @param {*} object The potential iteratee object argument. - * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. - */ - function isIterateeCall(value, index, object) { - if (!isObject(object)) { - return false; - } - var type = typeof index; - if (type == 'number' - ? (isArrayLike(object) && isIndex(index, object.length)) - : (type == 'string' && index in object)) { - var other = object[index]; - return value === value ? (value === other) : (other !== other); - } - return false; - } - - /** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ - function isKey(value, object) { - var type = typeof value; - if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { - return true; - } - if (isArray(value)) { - return false; - } - var result = !reIsDeepProp.test(value); - return result || (object != null && value in toObject(object)); - } - - /** - * Checks if `func` has a lazy counterpart. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`. - */ - function isLaziable(func) { - var funcName = getFuncName(func); - if (!(funcName in LazyWrapper.prototype)) { - return false; - } - var other = lodash[funcName]; - if (func === other) { - return true; - } - var data = getData(other); - return !!data && func === data[0]; - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - */ - function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ - function isStrictComparable(value) { - return value === value && !isObject(value); - } - - /** - * Merges the function metadata of `source` into `data`. - * - * Merging metadata reduces the number of wrappers required to invoke a function. - * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` - * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` - * augment function arguments, making the order in which they are executed important, - * preventing the merging of metadata. However, we make an exception for a safe - * common case where curried functions have `_.ary` and or `_.rearg` applied. - * - * @private - * @param {Array} data The destination metadata. - * @param {Array} source The source metadata. - * @returns {Array} Returns `data`. - */ - function mergeData(data, source) { - var bitmask = data[1], - srcBitmask = source[1], - newBitmask = bitmask | srcBitmask, - isCommon = newBitmask < ARY_FLAG; - - var isCombo = - (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) || - (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) || - (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG); - - // Exit early if metadata can't be merged. - if (!(isCommon || isCombo)) { - return data; - } - // Use source `thisArg` if available. - if (srcBitmask & BIND_FLAG) { - data[2] = source[2]; - // Set when currying a bound function. - newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; - } - // Compose partial arguments. - var value = source[3]; - if (value) { - var partials = data[3]; - data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); - data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); - } - // Compose partial right arguments. - value = source[5]; - if (value) { - partials = data[5]; - data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); - data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); - } - // Use source `argPos` if available. - value = source[7]; - if (value) { - data[7] = arrayCopy(value); - } - // Use source `ary` if it's smaller. - if (srcBitmask & ARY_FLAG) { - data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); - } - // Use source `arity` if one is not provided. - if (data[9] == null) { - data[9] = source[9]; - } - // Use source `func` and merge bitmasks. - data[0] = source[0]; - data[1] = newBitmask; - - return data; - } - - /** - * Used by `_.defaultsDeep` to customize its `_.merge` use. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @returns {*} Returns the value to assign to the destination object. - */ - function mergeDefaults(objectValue, sourceValue) { - return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults); - } - - /** - * A specialized version of `_.pick` which picks `object` properties specified - * by `props`. - * - * @private - * @param {Object} object The source object. - * @param {string[]} props The property names to pick. - * @returns {Object} Returns the new object. - */ - function pickByArray(object, props) { - object = toObject(object); - - var index = -1, - length = props.length, - result = {}; - - while (++index < length) { - var key = props[index]; - if (key in object) { - result[key] = object[key]; - } - } - return result; - } - - /** - * A specialized version of `_.pick` which picks `object` properties `predicate` - * returns truthy for. - * - * @private - * @param {Object} object The source object. - * @param {Function} predicate The function invoked per iteration. - * @returns {Object} Returns the new object. - */ - function pickByCallback(object, predicate) { - var result = {}; - baseForIn(object, function(value, key, object) { - if (predicate(value, key, object)) { - result[key] = value; - } - }); - return result; - } - - /** - * Reorder `array` according to the specified indexes where the element at - * the first index is assigned as the first element, the element at - * the second index is assigned as the second element, and so on. - * - * @private - * @param {Array} array The array to reorder. - * @param {Array} indexes The arranged array indexes. - * @returns {Array} Returns `array`. - */ - function reorder(array, indexes) { - var arrLength = array.length, - length = nativeMin(indexes.length, arrLength), - oldArray = arrayCopy(array); - - while (length--) { - var index = indexes[length]; - array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; - } - return array; - } - - /** - * Sets metadata for `func`. - * - * **Note:** If this function becomes hot, i.e. is invoked a lot in a short - * period of time, it will trip its breaker and transition to an identity function - * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) - * for more details. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var setData = (function() { - var count = 0, - lastCalled = 0; - - return function(key, value) { - var stamp = now(), - remaining = HOT_SPAN - (stamp - lastCalled); - - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return key; - } - } else { - count = 0; - } - return baseSetData(key, value); - }; - }()); - - /** - * A fallback implementation of `Object.keys` which creates an array of the - * own enumerable property names of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function shimKeys(object) { - var props = keysIn(object), - propsLength = props.length, - length = propsLength && object.length; - - var allowIndexes = !!length && isLength(length) && - (isArray(object) || isArguments(object)); - - var index = -1, - result = []; - - while (++index < propsLength) { - var key = props[index]; - if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { - result.push(key); - } - } - return result; - } - - /** - * Converts `value` to an array-like object if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Array|Object} Returns the array-like object. - */ - function toIterable(value) { - if (value == null) { - return []; - } - if (!isArrayLike(value)) { - return values(value); - } - return isObject(value) ? value : Object(value); - } - - /** - * Converts `value` to an object if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Object} Returns the object. - */ - function toObject(value) { - return isObject(value) ? value : Object(value); - } - - /** - * Converts `value` to property path array if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Array} Returns the property path array. - */ - function toPath(value) { - if (isArray(value)) { - return value; - } - var result = []; - baseToString(value).replace(rePropName, function(match, number, quote, string) { - result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; - } - - /** - * Creates a clone of `wrapper`. - * - * @private - * @param {Object} wrapper The wrapper to clone. - * @returns {Object} Returns the cloned wrapper. - */ - function wrapperClone(wrapper) { - return wrapper instanceof LazyWrapper - ? wrapper.clone() - : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of elements split into groups the length of `size`. - * If `collection` can't be split evenly, the final chunk will be the remaining - * elements. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to process. - * @param {number} [size=1] The length of each chunk. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the new array containing chunks. - * @example - * - * _.chunk(['a', 'b', 'c', 'd'], 2); - * // => [['a', 'b'], ['c', 'd']] - * - * _.chunk(['a', 'b', 'c', 'd'], 3); - * // => [['a', 'b', 'c'], ['d']] - */ - function chunk(array, size, guard) { - if (guard ? isIterateeCall(array, size, guard) : size == null) { - size = 1; - } else { - size = nativeMax(nativeFloor(size) || 1, 1); - } - var index = 0, - length = array ? array.length : 0, - resIndex = -1, - result = Array(nativeCeil(length / size)); - - while (index < length) { - result[++resIndex] = baseSlice(array, index, (index += size)); - } - return result; - } - - /** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are falsey. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ - function compact(array) { - var index = -1, - length = array ? array.length : 0, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { - result[++resIndex] = value; - } - } - return result; - } - - /** - * Creates an array of unique `array` values not included in the other - * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The arrays of values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.difference([1, 2, 3], [4, 2]); - * // => [1, 3] - */ - var difference = restParam(function(array, values) { - return (isObjectLike(array) && isArrayLike(array)) - ? baseDifference(array, baseFlatten(values, false, true)) - : []; - }); - - /** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.drop([1, 2, 3]); - * // => [2, 3] - * - * _.drop([1, 2, 3], 2); - * // => [3] - * - * _.drop([1, 2, 3], 5); - * // => [] - * - * _.drop([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function drop(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - return baseSlice(array, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRight([1, 2, 3]); - * // => [1, 2] - * - * _.dropRight([1, 2, 3], 2); - * // => [1] - * - * _.dropRight([1, 2, 3], 5); - * // => [] - * - * _.dropRight([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function dropRight(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - n = length - (+n || 0); - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements are dropped until `predicate` returns falsey. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that match the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRightWhile([1, 2, 3], function(n) { - * return n > 1; - * }); - * // => [1] - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); - * // => ['barney', 'fred'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); - * // => ['barney'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.dropRightWhile(users, 'active'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function dropRightWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true) - : []; - } - - /** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements are dropped until `predicate` returns falsey. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropWhile([1, 2, 3], function(n) { - * return n < 3; - * }); - * // => [3] - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); - * // => ['fred', 'pebbles'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.dropWhile(users, 'active', false), 'user'); - * // => ['pebbles'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.dropWhile(users, 'active'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function dropWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), true) - : []; - } - - /** - * Fills elements of `array` with `value` from `start` up to, but not - * including, `end`. - * - * **Note:** This method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.fill(array, 'a'); - * console.log(array); - * // => ['a', 'a', 'a'] - * - * _.fill(Array(3), 2); - * // => [2, 2, 2] - * - * _.fill([4, 6, 8], '*', 1, 2); - * // => [4, '*', 8] - */ - function fill(array, value, start, end) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { - start = 0; - end = length; - } - return baseFill(array, value, start, end); - } - - /** - * This method is like `_.find` except that it returns the index of the first - * element `predicate` returns truthy for instead of the element itself. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.findIndex(users, function(chr) { - * return chr.user == 'barney'; - * }); - * // => 0 - * - * // using the `_.matches` callback shorthand - * _.findIndex(users, { 'user': 'fred', 'active': false }); - * // => 1 - * - * // using the `_.matchesProperty` callback shorthand - * _.findIndex(users, 'active', false); - * // => 0 - * - * // using the `_.property` callback shorthand - * _.findIndex(users, 'active'); - * // => 2 - */ - var findIndex = createFindIndex(); - - /** - * This method is like `_.findIndex` except that it iterates over elements - * of `collection` from right to left. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.findLastIndex(users, function(chr) { - * return chr.user == 'pebbles'; - * }); - * // => 2 - * - * // using the `_.matches` callback shorthand - * _.findLastIndex(users, { 'user': 'barney', 'active': true }); - * // => 0 - * - * // using the `_.matchesProperty` callback shorthand - * _.findLastIndex(users, 'active', false); - * // => 2 - * - * // using the `_.property` callback shorthand - * _.findLastIndex(users, 'active'); - * // => 0 - */ - var findLastIndex = createFindIndex(true); - - /** - * Gets the first element of `array`. - * - * @static - * @memberOf _ - * @alias head - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @example - * - * _.first([1, 2, 3]); - * // => 1 - * - * _.first([]); - * // => undefined - */ - function first(array) { - return array ? array[0] : undefined; - } - - /** - * Flattens a nested array. If `isDeep` is `true` the array is recursively - * flattened, otherwise it is only flattened a single level. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to flatten. - * @param {boolean} [isDeep] Specify a deep flatten. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flatten([1, [2, 3, [4]]]); - * // => [1, 2, 3, [4]] - * - * // using `isDeep` - * _.flatten([1, [2, 3, [4]]], true); - * // => [1, 2, 3, 4] - */ - function flatten(array, isDeep, guard) { - var length = array ? array.length : 0; - if (guard && isIterateeCall(array, isDeep, guard)) { - isDeep = false; - } - return length ? baseFlatten(array, isDeep) : []; - } - - /** - * Recursively flattens a nested array. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to recursively flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flattenDeep([1, [2, 3, [4]]]); - * // => [1, 2, 3, 4] - */ - function flattenDeep(array) { - var length = array ? array.length : 0; - return length ? baseFlatten(array, true) : []; - } - - /** - * Gets the index at which the first occurrence of `value` is found in `array` - * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it is used as the offset - * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` - * performs a faster binary search. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {boolean|number} [fromIndex=0] The index to search from or `true` - * to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.indexOf([1, 2, 1, 2], 2); - * // => 1 - * - * // using `fromIndex` - * _.indexOf([1, 2, 1, 2], 2, 2); - * // => 3 - * - * // performing a binary search - * _.indexOf([1, 1, 2, 2], 2, true); - * // => 2 - */ - function indexOf(array, value, fromIndex) { - var length = array ? array.length : 0; - if (!length) { - return -1; - } - if (typeof fromIndex == 'number') { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; - } else if (fromIndex) { - var index = binaryIndex(array, value); - if (index < length && - (value === value ? (value === array[index]) : (array[index] !== array[index]))) { - return index; - } - return -1; - } - return baseIndexOf(array, value, fromIndex || 0); - } - - /** - * Gets all but the last element of `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.initial([1, 2, 3]); - * // => [1, 2] - */ - function initial(array) { - return dropRight(array, 1); - } - - /** - * Creates an array of unique values that are included in all of the provided - * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of shared values. - * @example - * _.intersection([1, 2], [4, 2], [2, 1]); - * // => [2] - */ - var intersection = restParam(function(arrays) { - var othLength = arrays.length, - othIndex = othLength, - caches = Array(length), - indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf, - result = []; - - while (othIndex--) { - var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; - caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; - } - var array = arrays[0], - index = -1, - length = array ? array.length : 0, - seen = caches[0]; - - outer: - while (++index < length) { - value = array[index]; - if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { - var othIndex = othLength; - while (--othIndex) { - var cache = caches[othIndex]; - if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { - continue outer; - } - } - if (seen) { - seen.push(value); - } - result.push(value); - } - } - return result; - }); - - /** - * Gets the last element of `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. - * @example - * - * _.last([1, 2, 3]); - * // => 3 - */ - function last(array) { - var length = array ? array.length : 0; - return length ? array[length - 1] : undefined; - } - - /** - * This method is like `_.indexOf` except that it iterates over elements of - * `array` from right to left. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {boolean|number} [fromIndex=array.length-1] The index to search from - * or `true` to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.lastIndexOf([1, 2, 1, 2], 2); - * // => 3 - * - * // using `fromIndex` - * _.lastIndexOf([1, 2, 1, 2], 2, 2); - * // => 1 - * - * // performing a binary search - * _.lastIndexOf([1, 1, 2, 2], 2, true); - * // => 3 - */ - function lastIndexOf(array, value, fromIndex) { - var length = array ? array.length : 0; - if (!length) { - return -1; - } - var index = length; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; - } else if (fromIndex) { - index = binaryIndex(array, value, true) - 1; - var other = array[index]; - if (value === value ? (value === other) : (other !== other)) { - return index; - } - return -1; - } - if (value !== value) { - return indexOfNaN(array, index, true); - } - while (index--) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * Removes all provided values from `array` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `_.without`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3, 1, 2, 3]; - * - * _.pull(array, 2, 3); - * console.log(array); - * // => [1, 1] - */ - function pull() { - var args = arguments, - array = args[0]; - - if (!(array && array.length)) { - return array; - } - var index = 0, - indexOf = getIndexOf(), - length = args.length; - - while (++index < length) { - var fromIndex = 0, - value = args[index]; - - while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { - splice.call(array, fromIndex, 1); - } - } - return array; - } - - /** - * Removes elements from `array` corresponding to the given indexes and returns - * an array of the removed elements. Indexes may be specified as an array of - * indexes or as individual arguments. - * - * **Note:** Unlike `_.at`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {...(number|number[])} [indexes] The indexes of elements to remove, - * specified as individual indexes or arrays of indexes. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [5, 10, 15, 20]; - * var evens = _.pullAt(array, 1, 3); - * - * console.log(array); - * // => [5, 15] - * - * console.log(evens); - * // => [10, 20] - */ - var pullAt = restParam(function(array, indexes) { - indexes = baseFlatten(indexes); - - var result = baseAt(array, indexes); - basePullAt(array, indexes.sort(baseCompareAscending)); - return result; - }); - - /** - * Removes all elements from `array` that `predicate` returns truthy for - * and returns an array of the removed elements. The predicate is bound to - * `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * **Note:** Unlike `_.filter`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [1, 2, 3, 4]; - * var evens = _.remove(array, function(n) { - * return n % 2 == 0; - * }); - * - * console.log(array); - * // => [1, 3] - * - * console.log(evens); - * // => [2, 4] - */ - function remove(array, predicate, thisArg) { - var result = []; - if (!(array && array.length)) { - return result; - } - var index = -1, - indexes = [], - length = array.length; - - predicate = getCallback(predicate, thisArg, 3); - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result.push(value); - indexes.push(index); - } - } - basePullAt(array, indexes); - return result; - } - - /** - * Gets all but the first element of `array`. - * - * @static - * @memberOf _ - * @alias tail - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.rest([1, 2, 3]); - * // => [2, 3] - */ - function rest(array) { - return drop(array, 1); - } - - /** - * Creates a slice of `array` from `start` up to, but not including, `end`. - * - * **Note:** This method is used instead of `Array#slice` to support node - * lists in IE < 9 and to ensure dense arrays are returned. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function slice(array, start, end) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { - start = 0; - end = length; - } - return baseSlice(array, start, end); - } - - /** - * Uses a binary search to determine the lowest index at which `value` should - * be inserted into `array` in order to maintain its sort order. If an iteratee - * function is provided it is invoked for `value` and each element of `array` - * to compute their sort ranking. The iteratee is bound to `thisArg` and - * invoked with one argument; (value). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedIndex([30, 50], 40); - * // => 1 - * - * _.sortedIndex([4, 4, 5, 5], 5); - * // => 2 - * - * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; - * - * // using an iteratee function - * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { - * return this.data[word]; - * }, dict); - * // => 1 - * - * // using the `_.property` callback shorthand - * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); - * // => 1 - */ - var sortedIndex = createSortedIndex(); - - /** - * This method is like `_.sortedIndex` except that it returns the highest - * index at which `value` should be inserted into `array` in order to - * maintain its sort order. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedLastIndex([4, 4, 5, 5], 5); - * // => 4 - */ - var sortedLastIndex = createSortedIndex(true); - - /** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.take([1, 2, 3]); - * // => [1] - * - * _.take([1, 2, 3], 2); - * // => [1, 2] - * - * _.take([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.take([1, 2, 3], 0); - * // => [] - */ - function take(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRight([1, 2, 3]); - * // => [3] - * - * _.takeRight([1, 2, 3], 2); - * // => [2, 3] - * - * _.takeRight([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.takeRight([1, 2, 3], 0); - * // => [] - */ - function takeRight(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - n = length - (+n || 0); - return baseSlice(array, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with elements taken from the end. Elements are - * taken until `predicate` returns falsey. The predicate is bound to `thisArg` - * and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRightWhile([1, 2, 3], function(n) { - * return n > 1; - * }); - * // => [2, 3] - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); - * // => ['pebbles'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); - * // => ['fred', 'pebbles'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.takeRightWhile(users, 'active'), 'user'); - * // => [] - */ - function takeRightWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true) - : []; - } - - /** - * Creates a slice of `array` with elements taken from the beginning. Elements - * are taken until `predicate` returns falsey. The predicate is bound to - * `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeWhile([1, 2, 3], function(n) { - * return n < 3; - * }); - * // => [1, 2] - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false}, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.takeWhile(users, 'active', false), 'user'); - * // => ['barney', 'fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.takeWhile(users, 'active'), 'user'); - * // => [] - */ - function takeWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3)) - : []; - } - - /** - * Creates an array of unique values, in order, from all of the provided arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of combined values. - * @example - * - * _.union([1, 2], [4, 2], [2, 1]); - * // => [1, 2, 4] - */ - var union = restParam(function(arrays) { - return baseUniq(baseFlatten(arrays, false, true)); - }); - - /** - * Creates a duplicate-free version of an array, using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons, in which only the first occurence of each element - * is kept. Providing `true` for `isSorted` performs a faster search algorithm - * for sorted arrays. If an iteratee function is provided it is invoked for - * each element in the array to generate the criterion by which uniqueness - * is computed. The `iteratee` is bound to `thisArg` and invoked with three - * arguments: (value, index, array). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias unique - * @category Array - * @param {Array} array The array to inspect. - * @param {boolean} [isSorted] Specify the array is sorted. - * @param {Function|Object|string} [iteratee] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new duplicate-value-free array. - * @example - * - * _.uniq([2, 1, 2]); - * // => [2, 1] - * - * // using `isSorted` - * _.uniq([1, 1, 2], true); - * // => [1, 2] - * - * // using an iteratee function - * _.uniq([1, 2.5, 1.5, 2], function(n) { - * return this.floor(n); - * }, Math); - * // => [1, 2.5] - * - * // using the `_.property` callback shorthand - * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }, { 'x': 2 }] - */ - function uniq(array, isSorted, iteratee, thisArg) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (isSorted != null && typeof isSorted != 'boolean') { - thisArg = iteratee; - iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted; - isSorted = false; - } - var callback = getCallback(); - if (!(iteratee == null && callback === baseCallback)) { - iteratee = callback(iteratee, thisArg, 3); - } - return (isSorted && getIndexOf() == baseIndexOf) - ? sortedUniq(array, iteratee) - : baseUniq(array, iteratee); - } - - /** - * This method is like `_.zip` except that it accepts an array of grouped - * elements and creates an array regrouping the elements to their pre-zip - * configuration. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array of grouped elements to process. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); - * // => [['fred', 30, true], ['barney', 40, false]] - * - * _.unzip(zipped); - * // => [['fred', 'barney'], [30, 40], [true, false]] - */ - function unzip(array) { - if (!(array && array.length)) { - return []; - } - var index = -1, - length = 0; - - array = arrayFilter(array, function(group) { - if (isArrayLike(group)) { - length = nativeMax(group.length, length); - return true; - } - }); - var result = Array(length); - while (++index < length) { - result[index] = arrayMap(array, baseProperty(index)); - } - return result; - } - - /** - * This method is like `_.unzip` except that it accepts an iteratee to specify - * how regrouped values should be combined. The `iteratee` is bound to `thisArg` - * and invoked with four arguments: (accumulator, value, index, group). - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array of grouped elements to process. - * @param {Function} [iteratee] The function to combine regrouped values. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip([1, 2], [10, 20], [100, 200]); - * // => [[1, 10, 100], [2, 20, 200]] - * - * _.unzipWith(zipped, _.add); - * // => [3, 30, 300] - */ - function unzipWith(array, iteratee, thisArg) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - var result = unzip(array); - if (iteratee == null) { - return result; - } - iteratee = bindCallback(iteratee, thisArg, 4); - return arrayMap(result, function(group) { - return arrayReduce(group, iteratee, undefined, true); - }); - } - - /** - * Creates an array excluding all provided values using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to filter. - * @param {...*} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.without([1, 2, 1, 3], 1, 2); - * // => [3] - */ - var without = restParam(function(array, values) { - return isArrayLike(array) - ? baseDifference(array, values) - : []; - }); - - /** - * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) - * of the provided arrays. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of values. - * @example - * - * _.xor([1, 2], [4, 2]); - * // => [1, 4] - */ - function xor() { - var index = -1, - length = arguments.length; - - while (++index < length) { - var array = arguments[index]; - if (isArrayLike(array)) { - var result = result - ? arrayPush(baseDifference(result, array), baseDifference(array, result)) - : array; - } - } - return result ? baseUniq(result) : []; - } - - /** - * Creates an array of grouped elements, the first of which contains the first - * elements of the given arrays, the second of which contains the second elements - * of the given arrays, and so on. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zip(['fred', 'barney'], [30, 40], [true, false]); - * // => [['fred', 30, true], ['barney', 40, false]] - */ - var zip = restParam(unzip); - - /** - * The inverse of `_.pairs`; this method returns an object composed from arrays - * of property names and values. Provide either a single two dimensional array, - * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names - * and one of corresponding values. - * - * @static - * @memberOf _ - * @alias object - * @category Array - * @param {Array} props The property names. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @example - * - * _.zipObject([['fred', 30], ['barney', 40]]); - * // => { 'fred': 30, 'barney': 40 } - * - * _.zipObject(['fred', 'barney'], [30, 40]); - * // => { 'fred': 30, 'barney': 40 } - */ - function zipObject(props, values) { - var index = -1, - length = props ? props.length : 0, - result = {}; - - if (length && !values && !isArray(props[0])) { - values = []; - } - while (++index < length) { - var key = props[index]; - if (values) { - result[key] = values[index]; - } else if (key) { - result[key[0]] = key[1]; - } - } - return result; - } - - /** - * This method is like `_.zip` except that it accepts an iteratee to specify - * how grouped values should be combined. The `iteratee` is bound to `thisArg` - * and invoked with four arguments: (accumulator, value, index, group). - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @param {Function} [iteratee] The function to combine grouped values. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zipWith([1, 2], [10, 20], [100, 200], _.add); - * // => [111, 222] - */ - var zipWith = restParam(function(arrays) { - var length = arrays.length, - iteratee = length > 2 ? arrays[length - 2] : undefined, - thisArg = length > 1 ? arrays[length - 1] : undefined; - - if (length > 2 && typeof iteratee == 'function') { - length -= 2; - } else { - iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined; - thisArg = undefined; - } - arrays.length = length; - return unzipWith(arrays, iteratee, thisArg); - }); - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object that wraps `value` with explicit method - * chaining enabled. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to wrap. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'pebbles', 'age': 1 } - * ]; - * - * var youngest = _.chain(users) - * .sortBy('age') - * .map(function(chr) { - * return chr.user + ' is ' + chr.age; - * }) - * .first() - * .value(); - * // => 'pebbles is 1' - */ - function chain(value) { - var result = lodash(value); - result.__chain__ = true; - return result; - } - - /** - * This method invokes `interceptor` and returns `value`. The interceptor is - * bound to `thisArg` and invoked with one argument; (value). The purpose of - * this method is to "tap into" a method chain in order to perform operations - * on intermediate results within the chain. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @param {*} [thisArg] The `this` binding of `interceptor`. - * @returns {*} Returns `value`. - * @example - * - * _([1, 2, 3]) - * .tap(function(array) { - * array.pop(); - * }) - * .reverse() - * .value(); - * // => [2, 1] - */ - function tap(value, interceptor, thisArg) { - interceptor.call(thisArg, value); - return value; - } - - /** - * This method is like `_.tap` except that it returns the result of `interceptor`. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @param {*} [thisArg] The `this` binding of `interceptor`. - * @returns {*} Returns the result of `interceptor`. - * @example - * - * _(' abc ') - * .chain() - * .trim() - * .thru(function(value) { - * return [value]; - * }) - * .value(); - * // => ['abc'] - */ - function thru(value, interceptor, thisArg) { - return interceptor.call(thisArg, value); - } - - /** - * Enables explicit method chaining on the wrapper object. - * - * @name chain - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 } - * ]; - * - * // without explicit chaining - * _(users).first(); - * // => { 'user': 'barney', 'age': 36 } - * - * // with explicit chaining - * _(users).chain() - * .first() - * .pick('user') - * .value(); - * // => { 'user': 'barney' } - */ - function wrapperChain() { - return chain(this); - } - - /** - * Executes the chained sequence and returns the wrapped result. - * - * @name commit - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).push(3); - * - * console.log(array); - * // => [1, 2] - * - * wrapped = wrapped.commit(); - * console.log(array); - * // => [1, 2, 3] - * - * wrapped.last(); - * // => 3 - * - * console.log(array); - * // => [1, 2, 3] - */ - function wrapperCommit() { - return new LodashWrapper(this.value(), this.__chain__); - } - - /** - * Creates a new array joining a wrapped array with any additional arrays - * and/or values. - * - * @name concat - * @memberOf _ - * @category Chain - * @param {...*} [values] The values to concatenate. - * @returns {Array} Returns the new concatenated array. - * @example - * - * var array = [1]; - * var wrapped = _(array).concat(2, [3], [[4]]); - * - * console.log(wrapped.value()); - * // => [1, 2, 3, [4]] - * - * console.log(array); - * // => [1] - */ - var wrapperConcat = restParam(function(values) { - values = baseFlatten(values); - return this.thru(function(array) { - return arrayConcat(isArray(array) ? array : [toObject(array)], values); - }); - }); - - /** - * Creates a clone of the chained sequence planting `value` as the wrapped value. - * - * @name plant - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).map(function(value) { - * return Math.pow(value, 2); - * }); - * - * var other = [3, 4]; - * var otherWrapped = wrapped.plant(other); - * - * otherWrapped.value(); - * // => [9, 16] - * - * wrapped.value(); - * // => [1, 4] - */ - function wrapperPlant(value) { - var result, - parent = this; - - while (parent instanceof baseLodash) { - var clone = wrapperClone(parent); - if (result) { - previous.__wrapped__ = clone; - } else { - result = clone; - } - var previous = clone; - parent = parent.__wrapped__; - } - previous.__wrapped__ = value; - return result; - } - - /** - * Reverses the wrapped array so the first element becomes the last, the - * second element becomes the second to last, and so on. - * - * **Note:** This method mutates the wrapped array. - * - * @name reverse - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new reversed `lodash` wrapper instance. - * @example - * - * var array = [1, 2, 3]; - * - * _(array).reverse().value() - * // => [3, 2, 1] - * - * console.log(array); - * // => [3, 2, 1] - */ - function wrapperReverse() { - var value = this.__wrapped__; - - var interceptor = function(value) { - return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse(); - }; - if (value instanceof LazyWrapper) { - var wrapped = value; - if (this.__actions__.length) { - wrapped = new LazyWrapper(this); - } - wrapped = wrapped.reverse(); - wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); - return new LodashWrapper(wrapped, this.__chain__); - } - return this.thru(interceptor); - } - - /** - * Produces the result of coercing the unwrapped value to a string. - * - * @name toString - * @memberOf _ - * @category Chain - * @returns {string} Returns the coerced string value. - * @example - * - * _([1, 2, 3]).toString(); - * // => '1,2,3' - */ - function wrapperToString() { - return (this.value() + ''); - } - - /** - * Executes the chained sequence to extract the unwrapped value. - * - * @name value - * @memberOf _ - * @alias run, toJSON, valueOf - * @category Chain - * @returns {*} Returns the resolved unwrapped value. - * @example - * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] - */ - function wrapperValue() { - return baseWrapperValue(this.__wrapped__, this.__actions__); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of elements corresponding to the given keys, or indexes, - * of `collection`. Keys may be specified as individual arguments or as arrays - * of keys. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(number|number[]|string|string[])} [props] The property names - * or indexes of elements to pick, specified individually or in arrays. - * @returns {Array} Returns the new array of picked elements. - * @example - * - * _.at(['a', 'b', 'c'], [0, 2]); - * // => ['a', 'c'] - * - * _.at(['barney', 'fred', 'pebbles'], 0, 2); - * // => ['barney', 'pebbles'] - */ - var at = restParam(function(collection, props) { - return baseAt(collection, baseFlatten(props)); - }); - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is the number of times the key was returned by `iteratee`. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([4.3, 6.1, 6.4], function(n) { - * return Math.floor(n); - * }); - * // => { '4': 1, '6': 2 } - * - * _.countBy([4.3, 6.1, 6.4], function(n) { - * return this.floor(n); - * }, Math); - * // => { '4': 1, '6': 2 } - * - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ - var countBy = createAggregator(function(result, value, key) { - hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); - }); - - /** - * Checks if `predicate` returns truthy for **all** elements of `collection`. - * The predicate is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias all - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - * @example - * - * _.every([true, 1, null, 'yes'], Boolean); - * // => false - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.every(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // using the `_.matchesProperty` callback shorthand - * _.every(users, 'active', false); - * // => true - * - * // using the `_.property` callback shorthand - * _.every(users, 'active'); - * // => false - */ - function every(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayEvery : baseEvery; - if (thisArg && isIterateeCall(collection, predicate, thisArg)) { - predicate = undefined; - } - if (typeof predicate != 'function' || thisArg !== undefined) { - predicate = getCallback(predicate, thisArg, 3); - } - return func(collection, predicate); - } - - /** - * Iterates over elements of `collection`, returning an array of all elements - * `predicate` returns truthy for. The predicate is bound to `thisArg` and - * invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias select - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. - * @example - * - * _.filter([4, 5, 6], function(n) { - * return n % 2 == 0; - * }); - * // => [4, 6] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.filter(users, 'active', false), 'user'); - * // => ['fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.filter(users, 'active'), 'user'); - * // => ['barney'] - */ - function filter(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayFilter : baseFilter; - predicate = getCallback(predicate, thisArg, 3); - return func(collection, predicate); - } - - /** - * Iterates over elements of `collection`, returning the first element - * `predicate` returns truthy for. The predicate is bound to `thisArg` and - * invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias detect - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false }, - * { 'user': 'pebbles', 'age': 1, 'active': true } - * ]; - * - * _.result(_.find(users, function(chr) { - * return chr.age < 40; - * }), 'user'); - * // => 'barney' - * - * // using the `_.matches` callback shorthand - * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); - * // => 'pebbles' - * - * // using the `_.matchesProperty` callback shorthand - * _.result(_.find(users, 'active', false), 'user'); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.result(_.find(users, 'active'), 'user'); - * // => 'barney' - */ - var find = createFind(baseEach); - - /** - * This method is like `_.find` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * _.findLast([1, 2, 3, 4], function(n) { - * return n % 2 == 1; - * }); - * // => 3 - */ - var findLast = createFind(baseEachRight, true); - - /** - * Performs a deep comparison between each element in `collection` and the - * source object, returning the first element that has equivalent property - * values. - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. For comparing a single - * own or inherited property value see `_.matchesProperty`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); - * // => 'barney' - * - * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); - * // => 'fred' - */ - function findWhere(collection, source) { - return find(collection, baseMatches(source)); - } - - /** - * Iterates over elements of `collection` invoking `iteratee` for each element. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). Iteratee functions may exit iteration early - * by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" property - * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` - * may be used for object iteration. - * - * @static - * @memberOf _ - * @alias each - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2]).forEach(function(n) { - * console.log(n); - * }).value(); - * // => logs each value from left to right and returns the array - * - * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { - * console.log(n, key); - * }); - * // => logs each value-key pair and returns the object (iteration order is not guaranteed) - */ - var forEach = createForEach(arrayEach, baseEach); - - /** - * This method is like `_.forEach` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @alias eachRight - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2]).forEachRight(function(n) { - * console.log(n); - * }).value(); - * // => logs each value from right to left and returns the array - */ - var forEachRight = createForEach(arrayEachRight, baseEachRight); - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is an array of the elements responsible for generating the key. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([4.2, 6.1, 6.4], function(n) { - * return Math.floor(n); - * }); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * _.groupBy([4.2, 6.1, 6.4], function(n) { - * return this.floor(n); - * }, Math); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * // using the `_.property` callback shorthand - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - var groupBy = createAggregator(function(result, value, key) { - if (hasOwnProperty.call(result, key)) { - result[key].push(value); - } else { - result[key] = [value]; - } - }); - - /** - * Checks if `value` is in `collection` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it is used as the offset - * from the end of `collection`. - * - * @static - * @memberOf _ - * @alias contains, include - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {*} target The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. - * @returns {boolean} Returns `true` if a matching element is found, else `false`. - * @example - * - * _.includes([1, 2, 3], 1); - * // => true - * - * _.includes([1, 2, 3], 1, 2); - * // => false - * - * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); - * // => true - * - * _.includes('pebbles', 'eb'); - * // => true - */ - function includes(collection, target, fromIndex, guard) { - var length = collection ? getLength(collection) : 0; - if (!isLength(length)) { - collection = values(collection); - length = collection.length; - } - if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { - fromIndex = 0; - } else { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); - } - return (typeof collection == 'string' || !isArray(collection) && isString(collection)) - ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) - : (!!length && getIndexOf(collection, target, fromIndex) > -1); - } - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is the last element responsible for generating the key. The - * iteratee function is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * var keyData = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ]; - * - * _.indexBy(keyData, 'dir'); - * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(keyData, function(object) { - * return String.fromCharCode(object.code); - * }); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(keyData, function(object) { - * return this.fromCharCode(object.code); - * }, String); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - */ - var indexBy = createAggregator(function(result, value, key) { - result[key] = value; - }); - - /** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `methodName` is a function it is - * invoked for, and `this` bound to, each element in `collection`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {Array} Returns the array of results. - * @example - * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - var invoke = restParam(function(collection, path, args) { - var index = -1, - isFunc = typeof path == 'function', - isProp = isKey(path), - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value) { - var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); - result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); - }); - return result; - }); - - /** - * Creates an array of values by running each element in `collection` through - * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three - * arguments: (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. - * - * The guarded methods are: - * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, - * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, - * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, - * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, - * `sum`, `uniq`, and `words` - * - * @static - * @memberOf _ - * @alias collect - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new mapped array. - * @example - * - * function timesThree(n) { - * return n * 3; - * } - * - * _.map([1, 2], timesThree); - * // => [3, 6] - * - * _.map({ 'a': 1, 'b': 2 }, timesThree); - * // => [3, 6] (iteration order is not guaranteed) - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * // using the `_.property` callback shorthand - * _.map(users, 'user'); - * // => ['barney', 'fred'] - */ - function map(collection, iteratee, thisArg) { - var func = isArray(collection) ? arrayMap : baseMap; - iteratee = getCallback(iteratee, thisArg, 3); - return func(collection, iteratee); - } - - /** - * Creates an array of elements split into two groups, the first of which - * contains elements `predicate` returns truthy for, while the second of which - * contains elements `predicate` returns falsey for. The predicate is bound - * to `thisArg` and invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the array of grouped elements. - * @example - * - * _.partition([1, 2, 3], function(n) { - * return n % 2; - * }); - * // => [[1, 3], [2]] - * - * _.partition([1.2, 2.3, 3.4], function(n) { - * return this.floor(n) % 2; - * }, Math); - * // => [[1.2, 3.4], [2.3]] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true }, - * { 'user': 'pebbles', 'age': 1, 'active': false } - * ]; - * - * var mapper = function(array) { - * return _.pluck(array, 'user'); - * }; - * - * // using the `_.matches` callback shorthand - * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); - * // => [['pebbles'], ['barney', 'fred']] - * - * // using the `_.matchesProperty` callback shorthand - * _.map(_.partition(users, 'active', false), mapper); - * // => [['barney', 'pebbles'], ['fred']] - * - * // using the `_.property` callback shorthand - * _.map(_.partition(users, 'active'), mapper); - * // => [['fred'], ['barney', 'pebbles']] - */ - var partition = createAggregator(function(result, value, key) { - result[key ? 0 : 1].push(value); - }, function() { return [[], []]; }); - - /** - * Gets the property value of `path` from all elements in `collection`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|string} path The path of the property to pluck. - * @returns {Array} Returns the property values. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 } - * ]; - * - * _.pluck(users, 'user'); - * // => ['barney', 'fred'] - * - * var userIndex = _.indexBy(users, 'user'); - * _.pluck(userIndex, 'age'); - * // => [36, 40] (iteration order is not guaranteed) - */ - function pluck(collection, path) { - return map(collection, property(path)); - } - - /** - * Reduces `collection` to a value which is the accumulated result of running - * each element in `collection` through `iteratee`, where each successive - * invocation is supplied the return value of the previous. If `accumulator` - * is not provided the first element of `collection` is used as the initial - * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: - * (accumulator, value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.reduce`, `_.reduceRight`, and `_.transform`. - * - * The guarded methods are: - * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, - * and `sortByOrder` - * - * @static - * @memberOf _ - * @alias foldl, inject - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * _.reduce([1, 2], function(total, n) { - * return total + n; - * }); - * // => 3 - * - * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { - * result[key] = n * 3; - * return result; - * }, {}); - * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) - */ - var reduce = createReduce(arrayReduce, baseEach); - - /** - * This method is like `_.reduce` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @alias foldr - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * var array = [[0, 1], [2, 3], [4, 5]]; - * - * _.reduceRight(array, function(flattened, other) { - * return flattened.concat(other); - * }, []); - * // => [4, 5, 2, 3, 0, 1] - */ - var reduceRight = createReduce(arrayReduceRight, baseEachRight); - - /** - * The opposite of `_.filter`; this method returns the elements of `collection` - * that `predicate` does **not** return truthy for. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. - * @example - * - * _.reject([1, 2, 3, 4], function(n) { - * return n % 2 == 0; - * }); - * // => [1, 3] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.reject(users, 'active', false), 'user'); - * // => ['fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.reject(users, 'active'), 'user'); - * // => ['barney'] - */ - function reject(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayFilter : baseFilter; - predicate = getCallback(predicate, thisArg, 3); - return func(collection, function(value, index, collection) { - return !predicate(value, index, collection); - }); - } - - /** - * Gets a random element or `n` random elements from a collection. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to sample. - * @param {number} [n] The number of elements to sample. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {*} Returns the random sample(s). - * @example - * - * _.sample([1, 2, 3, 4]); - * // => 2 - * - * _.sample([1, 2, 3, 4], 2); - * // => [3, 1] - */ - function sample(collection, n, guard) { - if (guard ? isIterateeCall(collection, n, guard) : n == null) { - collection = toIterable(collection); - var length = collection.length; - return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; - } - var index = -1, - result = toArray(collection), - length = result.length, - lastIndex = length - 1; - - n = nativeMin(n < 0 ? 0 : (+n || 0), length); - while (++index < n) { - var rand = baseRandom(index, lastIndex), - value = result[rand]; - - result[rand] = result[index]; - result[index] = value; - } - result.length = n; - return result; - } - - /** - * Creates an array of shuffled values, using a version of the - * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - * @example - * - * _.shuffle([1, 2, 3, 4]); - * // => [4, 1, 3, 2] - */ - function shuffle(collection) { - return sample(collection, POSITIVE_INFINITY); - } - - /** - * Gets the size of `collection` by returning its length for array-like - * values or the number of own enumerable properties for objects. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns the size of `collection`. - * @example - * - * _.size([1, 2, 3]); - * // => 3 - * - * _.size({ 'a': 1, 'b': 2 }); - * // => 2 - * - * _.size('pebbles'); - * // => 7 - */ - function size(collection) { - var length = collection ? getLength(collection) : 0; - return isLength(length) ? length : keys(collection).length; - } - - /** - * Checks if `predicate` returns truthy for **any** element of `collection`. - * The function returns as soon as it finds a passing value and does not iterate - * over the entire collection. The predicate is bound to `thisArg` and invoked - * with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias any - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * _.some([null, 0, 'yes', false], Boolean); - * // => true - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.some(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // using the `_.matchesProperty` callback shorthand - * _.some(users, 'active', false); - * // => true - * - * // using the `_.property` callback shorthand - * _.some(users, 'active'); - * // => true - */ - function some(collection, predicate, thisArg) { - var func = isArray(collection) ? arraySome : baseSome; - if (thisArg && isIterateeCall(collection, predicate, thisArg)) { - predicate = undefined; - } - if (typeof predicate != 'function' || thisArg !== undefined) { - predicate = getCallback(predicate, thisArg, 3); - } - return func(collection, predicate); - } - - /** - * Creates an array of elements, sorted in ascending order by the results of - * running each element in a collection through `iteratee`. This method performs - * a stable sort, that is, it preserves the original sort order of equal elements. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new sorted array. - * @example - * - * _.sortBy([1, 2, 3], function(n) { - * return Math.sin(n); - * }); - * // => [3, 1, 2] - * - * _.sortBy([1, 2, 3], function(n) { - * return this.sin(n); - * }, Math); - * // => [3, 1, 2] - * - * var users = [ - * { 'user': 'fred' }, - * { 'user': 'pebbles' }, - * { 'user': 'barney' } - * ]; - * - * // using the `_.property` callback shorthand - * _.pluck(_.sortBy(users, 'user'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function sortBy(collection, iteratee, thisArg) { - if (collection == null) { - return []; - } - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { - iteratee = undefined; - } - var index = -1; - iteratee = getCallback(iteratee, thisArg, 3); - - var result = baseMap(collection, function(value, key, collection) { - return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; - }); - return baseSortBy(result, compareAscending); - } - - /** - * This method is like `_.sortBy` except that it can sort by multiple iteratees - * or property names. - * - * If a property name is provided for an iteratee the created `_.property` - * style callback returns the property value of the given element. - * - * If an object is provided for an iteratee the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees - * The iteratees to sort by, specified as individual values or arrays of values. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 42 }, - * { 'user': 'barney', 'age': 34 } - * ]; - * - * _.map(_.sortByAll(users, ['user', 'age']), _.values); - * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] - * - * _.map(_.sortByAll(users, 'user', function(chr) { - * return Math.floor(chr.age / 10); - * }), _.values); - * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] - */ - var sortByAll = restParam(function(collection, iteratees) { - if (collection == null) { - return []; - } - var guard = iteratees[2]; - if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { - iteratees.length = 1; - } - return baseSortByOrder(collection, baseFlatten(iteratees), []); - }); - - /** - * This method is like `_.sortByAll` except that it allows specifying the - * sort orders of the iteratees to sort by. If `orders` is unspecified, all - * values are sorted in ascending order. Otherwise, a value is sorted in - * ascending order if its corresponding order is "asc", and descending if "desc". - * - * If a property name is provided for an iteratee the created `_.property` - * style callback returns the property value of the given element. - * - * If an object is provided for an iteratee the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {boolean[]} [orders] The sort orders of `iteratees`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 34 }, - * { 'user': 'fred', 'age': 42 }, - * { 'user': 'barney', 'age': 36 } - * ]; - * - * // sort by `user` in ascending order and by `age` in descending order - * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); - * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] - */ - function sortByOrder(collection, iteratees, orders, guard) { - if (collection == null) { - return []; - } - if (guard && isIterateeCall(iteratees, orders, guard)) { - orders = undefined; - } - if (!isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees]; - } - if (!isArray(orders)) { - orders = orders == null ? [] : [orders]; - } - return baseSortByOrder(collection, iteratees, orders); - } - - /** - * Performs a deep comparison between each element in `collection` and the - * source object, returning an array of all elements that have equivalent - * property values. - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. For comparing a single - * own or inherited property value see `_.matchesProperty`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {Array} Returns the new filtered array. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, - * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } - * ]; - * - * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); - * // => ['barney'] - * - * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); - * // => ['fred'] - */ - function where(collection, source) { - return filter(collection, baseMatches(source)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Gets the number of milliseconds that have elapsed since the Unix epoch - * (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @category Date - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => logs the number of milliseconds it took for the deferred function to be invoked - */ - var now = nativeNow || function() { - return new Date().getTime(); - }; - - /*------------------------------------------------------------------------*/ - - /** - * The opposite of `_.before`; this method creates a function that invokes - * `func` once it is called `n` or more times. - * - * @static - * @memberOf _ - * @category Function - * @param {number} n The number of calls before `func` is invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var saves = ['profile', 'settings']; - * - * var done = _.after(saves.length, function() { - * console.log('done saving!'); - * }); - * - * _.forEach(saves, function(type) { - * asyncSave({ 'type': type, 'complete': done }); - * }); - * // => logs 'done saving!' after the two async saves have completed - */ - function after(n, func) { - if (typeof func != 'function') { - if (typeof n == 'function') { - var temp = n; - n = func; - func = temp; - } else { - throw new TypeError(FUNC_ERROR_TEXT); - } - } - n = nativeIsFinite(n = +n) ? n : 0; - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } - - /** - * Creates a function that accepts up to `n` arguments ignoring any - * additional arguments. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to cap arguments for. - * @param {number} [n=func.length] The arity cap. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new function. - * @example - * - * _.map(['6', '8', '10'], _.ary(parseInt, 1)); - * // => [6, 8, 10] - */ - function ary(func, n, guard) { - if (guard && isIterateeCall(func, n, guard)) { - n = undefined; - } - n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); - return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); - } - - /** - * Creates a function that invokes `func`, with the `this` binding and arguments - * of the created function, while it is called less than `n` times. Subsequent - * calls to the created function return the result of the last `func` invocation. - * - * @static - * @memberOf _ - * @category Function - * @param {number} n The number of calls at which `func` is no longer invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * jQuery('#add').on('click', _.before(5, addContactToList)); - * // => allows adding up to 4 contacts to the list - */ - function before(n, func) { - var result; - if (typeof func != 'function') { - if (typeof n == 'function') { - var temp = n; - n = func; - func = temp; - } else { - throw new TypeError(FUNC_ERROR_TEXT); - } - } - return function() { - if (--n > 0) { - result = func.apply(this, arguments); - } - if (n <= 1) { - func = undefined; - } - return result; - }; - } - - /** - * Creates a function that invokes `func` with the `this` binding of `thisArg` - * and prepends any additional `_.bind` arguments to those provided to the - * bound function. - * - * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for partially applied arguments. - * - * **Note:** Unlike native `Function#bind` this method does not set the "length" - * property of bound functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var greet = function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * }; - * - * var object = { 'user': 'fred' }; - * - * var bound = _.bind(greet, object, 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * // using placeholders - * var bound = _.bind(greet, object, _, '!'); - * bound('hi'); - * // => 'hi fred!' - */ - var bind = restParam(function(func, thisArg, partials) { - var bitmask = BIND_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, bind.placeholder); - bitmask |= PARTIAL_FLAG; - } - return createWrapper(func, bitmask, thisArg, partials, holders); - }); - - /** - * Binds methods of an object to the object itself, overwriting the existing - * method. Method names may be specified as individual arguments or as arrays - * of method names. If no method names are provided all enumerable function - * properties, own and inherited, of `object` are bound. - * - * **Note:** This method does not set the "length" property of bound functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Object} object The object to bind and assign the bound methods to. - * @param {...(string|string[])} [methodNames] The object method names to bind, - * specified as individual method names or arrays of method names. - * @returns {Object} Returns `object`. - * @example - * - * var view = { - * 'label': 'docs', - * 'onClick': function() { - * console.log('clicked ' + this.label); - * } - * }; - * - * _.bindAll(view); - * jQuery('#docs').on('click', view.onClick); - * // => logs 'clicked docs' when the element is clicked - */ - var bindAll = restParam(function(object, methodNames) { - methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object); - - var index = -1, - length = methodNames.length; - - while (++index < length) { - var key = methodNames[index]; - object[key] = createWrapper(object[key], BIND_FLAG, object); - } - return object; - }); - - /** - * Creates a function that invokes the method at `object[key]` and prepends - * any additional `_.bindKey` arguments to those provided to the bound function. - * - * This method differs from `_.bind` by allowing bound functions to reference - * methods that may be redefined or don't yet exist. - * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) - * for more details. - * - * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * @static - * @memberOf _ - * @category Function - * @param {Object} object The object the method belongs to. - * @param {string} key The key of the method. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'user': 'fred', - * 'greet': function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * }; - * - * var bound = _.bindKey(object, 'greet', 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * object.greet = function(greeting, punctuation) { - * return greeting + 'ya ' + this.user + punctuation; - * }; - * - * bound('!'); - * // => 'hiya fred!' - * - * // using placeholders - * var bound = _.bindKey(object, 'greet', _, '!'); - * bound('hi'); - * // => 'hiya fred!' - */ - var bindKey = restParam(function(object, key, partials) { - var bitmask = BIND_FLAG | BIND_KEY_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, bindKey.placeholder); - bitmask |= PARTIAL_FLAG; - } - return createWrapper(key, bitmask, object, partials, holders); - }); - - /** - * Creates a function that accepts one or more arguments of `func` that when - * called either invokes `func` returning its result, if all `func` arguments - * have been provided, or returns a function that accepts one or more of the - * remaining `func` arguments, and so on. The arity of `func` may be specified - * if `func.length` is not sufficient. - * - * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for provided arguments. - * - * **Note:** This method does not set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curry(abc); - * - * curried(1)(2)(3); - * // => [1, 2, 3] - * - * curried(1, 2)(3); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // using placeholders - * curried(1)(_, 3)(2); - * // => [1, 2, 3] - */ - var curry = createCurry(CURRY_FLAG); - - /** - * This method is like `_.curry` except that arguments are applied to `func` - * in the manner of `_.partialRight` instead of `_.partial`. - * - * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for provided arguments. - * - * **Note:** This method does not set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curryRight(abc); - * - * curried(3)(2)(1); - * // => [1, 2, 3] - * - * curried(2, 3)(1); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // using placeholders - * curried(3)(1, _)(2); - * // => [1, 2, 3] - */ - var curryRight = createCurry(CURRY_RIGHT_FLAG); - - /** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed invocations. Provide an options object to indicate that `func` - * should be invoked on the leading and/or trailing edge of the `wait` timeout. - * Subsequent calls to the debounced function return the result of the last - * `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked - * on the trailing edge of the timeout only if the the debounced function is - * invoked more than once during the `wait` timeout. - * - * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=false] Specify invoking on the leading - * edge of the timeout. - * @param {number} [options.maxWait] The maximum time `func` is allowed to be - * delayed before it is invoked. - * @param {boolean} [options.trailing=true] Specify invoking on the trailing - * edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // avoid costly calculations while the window size is in flux - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // invoke `sendMail` when the click event is fired, debouncing subsequent calls - * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // ensure `batchLog` is invoked once after 1 second of debounced calls - * var source = new EventSource('/stream'); - * jQuery(source).on('message', _.debounce(batchLog, 250, { - * 'maxWait': 1000 - * })); - * - * // cancel a debounced call - * var todoChanges = _.debounce(batchLog, 1000); - * Object.observe(models.todo, todoChanges); - * - * Object.observe(models, function(changes) { - * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { - * todoChanges.cancel(); - * } - * }, ['delete']); - * - * // ...at some point `models.todo` is changed - * models.todo.completed = true; - * - * // ...before 1 second has passed `models.todo` is deleted - * // which cancels the debounced `todoChanges` call - * delete models.todo; - */ - function debounce(func, wait, options) { - var args, - maxTimeoutId, - result, - stamp, - thisArg, - timeoutId, - trailingCall, - lastCalled = 0, - maxWait = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - wait = wait < 0 ? 0 : (+wait || 0); - if (options === true) { - var leading = true; - trailing = false; - } else if (isObject(options)) { - leading = !!options.leading; - maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function cancel() { - if (timeoutId) { - clearTimeout(timeoutId); - } - if (maxTimeoutId) { - clearTimeout(maxTimeoutId); - } - lastCalled = 0; - maxTimeoutId = timeoutId = trailingCall = undefined; - } - - function complete(isCalled, id) { - if (id) { - clearTimeout(id); - } - maxTimeoutId = timeoutId = trailingCall = undefined; - if (isCalled) { - lastCalled = now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = undefined; - } - } - } - - function delayed() { - var remaining = wait - (now() - stamp); - if (remaining <= 0 || remaining > wait) { - complete(trailingCall, maxTimeoutId); - } else { - timeoutId = setTimeout(delayed, remaining); - } - } - - function maxDelayed() { - complete(trailing, timeoutId); - } - - function debounced() { - args = arguments; - stamp = now(); - thisArg = this; - trailingCall = trailing && (timeoutId || !leading); - - if (maxWait === false) { - var leadingCall = leading && !timeoutId; - } else { - if (!maxTimeoutId && !leading) { - lastCalled = stamp; - } - var remaining = maxWait - (stamp - lastCalled), - isCalled = remaining <= 0 || remaining > maxWait; - - if (isCalled) { - if (maxTimeoutId) { - maxTimeoutId = clearTimeout(maxTimeoutId); - } - lastCalled = stamp; - result = func.apply(thisArg, args); - } - else if (!maxTimeoutId) { - maxTimeoutId = setTimeout(maxDelayed, remaining); - } - } - if (isCalled && timeoutId) { - timeoutId = clearTimeout(timeoutId); - } - else if (!timeoutId && wait !== maxWait) { - timeoutId = setTimeout(delayed, wait); - } - if (leadingCall) { - isCalled = true; - result = func.apply(thisArg, args); - } - if (isCalled && !timeoutId && !maxTimeoutId) { - args = thisArg = undefined; - } - return result; - } - debounced.cancel = cancel; - return debounced; - } - - /** - * Defers invoking the `func` until the current call stack has cleared. Any - * additional arguments are provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to defer. - * @param {...*} [args] The arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.defer(function(text) { - * console.log(text); - * }, 'deferred'); - * // logs 'deferred' after one or more milliseconds - */ - var defer = restParam(function(func, args) { - return baseDelay(func, 1, args); - }); - - /** - * Invokes `func` after `wait` milliseconds. Any additional arguments are - * provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {...*} [args] The arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.delay(function(text) { - * console.log(text); - * }, 1000, 'later'); - * // => logs 'later' after one second - */ - var delay = restParam(function(func, wait, args) { - return baseDelay(func, wait, args); - }); - - /** - * Creates a function that returns the result of invoking the provided - * functions with the `this` binding of the created function, where each - * successive invocation is supplied the return value of the previous. - * - * @static - * @memberOf _ - * @category Function - * @param {...Function} [funcs] Functions to invoke. - * @returns {Function} Returns the new function. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flow(_.add, square); - * addSquare(1, 2); - * // => 9 - */ - var flow = createFlow(); - - /** - * This method is like `_.flow` except that it creates a function that - * invokes the provided functions from right to left. - * - * @static - * @memberOf _ - * @alias backflow, compose - * @category Function - * @param {...Function} [funcs] Functions to invoke. - * @returns {Function} Returns the new function. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flowRight(square, _.add); - * addSquare(1, 2); - * // => 9 - */ - var flowRight = createFlow(true); - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is coerced to a string and used as the - * cache key. The `func` is invoked with the `this` binding of the memoized - * function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) - * method interface of `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoizing function. - * @example - * - * var upperCase = _.memoize(function(string) { - * return string.toUpperCase(); - * }); - * - * upperCase('fred'); - * // => 'FRED' - * - * // modifying the result cache - * upperCase.cache.set('fred', 'BARNEY'); - * upperCase('fred'); - * // => 'BARNEY' - * - * // replacing `_.memoize.Cache` - * var object = { 'user': 'fred' }; - * var other = { 'user': 'barney' }; - * var identity = _.memoize(_.identity); - * - * identity(object); - * // => { 'user': 'fred' } - * identity(other); - * // => { 'user': 'fred' } - * - * _.memoize.Cache = WeakMap; - * var identity = _.memoize(_.identity); - * - * identity(object); - * // => { 'user': 'fred' } - * identity(other); - * // => { 'user': 'barney' } - */ - function memoize(func, resolver) { - if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; - - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result); - return result; - }; - memoized.cache = new memoize.Cache; - return memoized; - } - - /** - * Creates a function that runs each argument through a corresponding - * transform function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to wrap. - * @param {...(Function|Function[])} [transforms] The functions to transform - * arguments, specified as individual functions or arrays of functions. - * @returns {Function} Returns the new function. - * @example - * - * function doubled(n) { - * return n * 2; - * } - * - * function square(n) { - * return n * n; - * } - * - * var modded = _.modArgs(function(x, y) { - * return [x, y]; - * }, square, doubled); - * - * modded(1, 2); - * // => [1, 4] - * - * modded(5, 10); - * // => [25, 20] - */ - var modArgs = restParam(function(func, transforms) { - transforms = baseFlatten(transforms); - if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var length = transforms.length; - return restParam(function(args) { - var index = nativeMin(args.length, length); - while (index--) { - args[index] = transforms[index](args[index]); - } - return func.apply(this, args); - }); - }); - - /** - * Creates a function that negates the result of the predicate `func`. The - * `func` predicate is invoked with the `this` binding and arguments of the - * created function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new function. - * @example - * - * function isEven(n) { - * return n % 2 == 0; - * } - * - * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); - * // => [1, 3, 5] - */ - function negate(predicate) { - if (typeof predicate != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return function() { - return !predicate.apply(this, arguments); - }; - } - - /** - * Creates a function that is restricted to invoking `func` once. Repeat calls - * to the function return the value of the first call. The `func` is invoked - * with the `this` binding and arguments of the created function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // `initialize` invokes `createApplication` once - */ - function once(func) { - return before(2, func); - } - - /** - * Creates a function that invokes `func` with `partial` arguments prepended - * to those provided to the new function. This method is like `_.bind` except - * it does **not** alter the `this` binding. - * - * The `_.partial.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method does not set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { - * return greeting + ' ' + name; - * }; - * - * var sayHelloTo = _.partial(greet, 'hello'); - * sayHelloTo('fred'); - * // => 'hello fred' - * - * // using placeholders - * var greetFred = _.partial(greet, _, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - */ - var partial = createPartial(PARTIAL_FLAG); - - /** - * This method is like `_.partial` except that partially applied arguments - * are appended to those provided to the new function. - * - * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method does not set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { - * return greeting + ' ' + name; - * }; - * - * var greetFred = _.partialRight(greet, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - * - * // using placeholders - * var sayHelloTo = _.partialRight(greet, 'hello', _); - * sayHelloTo('fred'); - * // => 'hello fred' - */ - var partialRight = createPartial(PARTIAL_RIGHT_FLAG); - - /** - * Creates a function that invokes `func` with arguments arranged according - * to the specified indexes where the argument value at the first index is - * provided as the first argument, the argument value at the second index is - * provided as the second argument, and so on. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to rearrange arguments for. - * @param {...(number|number[])} indexes The arranged argument indexes, - * specified as individual indexes or arrays of indexes. - * @returns {Function} Returns the new function. - * @example - * - * var rearged = _.rearg(function(a, b, c) { - * return [a, b, c]; - * }, 2, 0, 1); - * - * rearged('b', 'c', 'a') - * // => ['a', 'b', 'c'] - * - * var map = _.rearg(_.map, [1, 0]); - * map(function(n) { - * return n * 3; - * }, [1, 2, 3]); - * // => [3, 6, 9] - */ - var rearg = restParam(function(func, indexes) { - return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes)); - }); - - /** - * Creates a function that invokes `func` with the `this` binding of the - * created function and arguments from `start` and beyond provided as an array. - * - * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.restParam(function(what, names) { - * return what + ' ' + _.initial(names).join(', ') + - * (_.size(names) > 1 ? ', & ' : '') + _.last(names); - * }); - * - * say('hello', 'fred', 'barney', 'pebbles'); - * // => 'hello fred, barney, & pebbles' - */ - function restParam(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); - return function() { - var args = arguments, - index = -1, - length = nativeMax(args.length - start, 0), - rest = Array(length); - - while (++index < length) { - rest[index] = args[start + index]; - } - switch (start) { - case 0: return func.call(this, rest); - case 1: return func.call(this, args[0], rest); - case 2: return func.call(this, args[0], args[1], rest); - } - var otherArgs = Array(start + 1); - index = -1; - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = rest; - return func.apply(this, otherArgs); - }; - } - - /** - * Creates a function that invokes `func` with the `this` binding of the created - * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3). - * - * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to spread arguments over. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.spread(function(who, what) { - * return who + ' says ' + what; - * }); - * - * say(['fred', 'hello']); - * // => 'fred says hello' - * - * // with a Promise - * var numbers = Promise.all([ - * Promise.resolve(40), - * Promise.resolve(36) - * ]); - * - * numbers.then(_.spread(function(x, y) { - * return x + y; - * })); - * // => a Promise of 76 - */ - function spread(func) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return function(array) { - return func.apply(this, array); - }; - } - - /** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed invocations. Provide an options object to indicate - * that `func` should be invoked on the leading and/or trailing edge of the - * `wait` timeout. Subsequent calls to the throttled function return the - * result of the last `func` call. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked - * on the trailing edge of the timeout only if the the throttled function is - * invoked more than once during the `wait` timeout. - * - * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=true] Specify invoking on the leading - * edge of the timeout. - * @param {boolean} [options.trailing=true] Specify invoking on the trailing - * edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // avoid excessively updating the position while scrolling - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes - * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { - * 'trailing': false - * })); - * - * // cancel a trailing throttled call - * jQuery(window).on('popstate', throttled.cancel); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (options === false) { - leading = false; - } else if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing }); - } - - /** - * Creates a function that provides `value` to the wrapper function as its - * first argument. Any additional arguments provided to the function are - * appended to those provided to the wrapper function. The wrapper is invoked - * with the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Function - * @param {*} value The value to wrap. - * @param {Function} wrapper The wrapper function. - * @returns {Function} Returns the new function. - * @example - * - * var p = _.wrap(_.escape, function(func, text) { - * return '

    ' + func(text) + '

    '; - * }); - * - * p('fred, barney, & pebbles'); - * // => '

    fred, barney, & pebbles

    ' - */ - function wrap(value, wrapper) { - wrapper = wrapper == null ? identity : wrapper; - return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, - * otherwise they are assigned by reference. If `customizer` is provided it is - * invoked to produce the cloned values. If `customizer` returns `undefined` - * cloning is handled by the method instead. The `customizer` is bound to - * `thisArg` and invoked with two argument; (value [, index|key, object]). - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). - * The enumerable properties of `arguments` objects and objects created by - * constructors other than `Object` are cloned to plain `Object` objects. An - * empty object is returned for uncloneable values such as functions, DOM nodes, - * Maps, Sets, and WeakMaps. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {*} Returns the cloned value. - * @example - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * var shallow = _.clone(users); - * shallow[0] === users[0]; - * // => true - * - * var deep = _.clone(users, true); - * deep[0] === users[0]; - * // => false - * - * // using a customizer callback - * var el = _.clone(document.body, function(value) { - * if (_.isElement(value)) { - * return value.cloneNode(false); - * } - * }); - * - * el === document.body - * // => false - * el.nodeName - * // => BODY - * el.childNodes.length; - * // => 0 - */ - function clone(value, isDeep, customizer, thisArg) { - if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) { - isDeep = false; - } - else if (typeof isDeep == 'function') { - thisArg = customizer; - customizer = isDeep; - isDeep = false; - } - return typeof customizer == 'function' - ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1)) - : baseClone(value, isDeep); - } - - /** - * Creates a deep clone of `value`. If `customizer` is provided it is invoked - * to produce the cloned values. If `customizer` returns `undefined` cloning - * is handled by the method instead. The `customizer` is bound to `thisArg` - * and invoked with two argument; (value [, index|key, object]). - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). - * The enumerable properties of `arguments` objects and objects created by - * constructors other than `Object` are cloned to plain `Object` objects. An - * empty object is returned for uncloneable values such as functions, DOM nodes, - * Maps, Sets, and WeakMaps. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {*} Returns the deep cloned value. - * @example - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * var deep = _.cloneDeep(users); - * deep[0] === users[0]; - * // => false - * - * // using a customizer callback - * var el = _.cloneDeep(document.body, function(value) { - * if (_.isElement(value)) { - * return value.cloneNode(true); - * } - * }); - * - * el === document.body - * // => false - * el.nodeName - * // => BODY - * el.childNodes.length; - * // => 20 - */ - function cloneDeep(value, customizer, thisArg) { - return typeof customizer == 'function' - ? baseClone(value, true, bindCallback(customizer, thisArg, 1)) - : baseClone(value, true); - } - - /** - * Checks if `value` is greater than `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`. - * @example - * - * _.gt(3, 1); - * // => true - * - * _.gt(3, 3); - * // => false - * - * _.gt(1, 3); - * // => false - */ - function gt(value, other) { - return value > other; - } - - /** - * Checks if `value` is greater than or equal to `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`. - * @example - * - * _.gte(3, 1); - * // => true - * - * _.gte(3, 3); - * // => true - * - * _.gte(1, 3); - * // => false - */ - function gte(value, other) { - return value >= other; - } - - /** - * Checks if `value` is classified as an `arguments` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - return isObjectLike(value) && isArrayLike(value) && - hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); - } - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(function() { return arguments; }()); - * // => false - */ - var isArray = nativeIsArray || function(value) { - return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; - }; - - /** - * Checks if `value` is classified as a boolean primitive or object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isBoolean(false); - * // => true - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag); - } - - /** - * Checks if `value` is classified as a `Date` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false - */ - function isDate(value) { - return isObjectLike(value) && objToString.call(value) == dateTag; - } - - /** - * Checks if `value` is a DOM element. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - * - * _.isElement(''); - * // => false - */ - function isElement(value) { - return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value); - } - - /** - * Checks if `value` is empty. A value is considered empty unless it is an - * `arguments` object, array, string, or jQuery-like collection with a length - * greater than `0` or an object with own enumerable properties. - * - * @static - * @memberOf _ - * @category Lang - * @param {Array|Object|string} value The value to inspect. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({ 'a': 1 }); - * // => false - */ - function isEmpty(value) { - if (value == null) { - return true; - } - if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) || - (isObjectLike(value) && isFunction(value.splice)))) { - return !value.length; - } - return !keys(value).length; - } - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent. If `customizer` is provided it is invoked to compare values. - * If `customizer` returns `undefined` comparisons are handled by the method - * instead. The `customizer` is bound to `thisArg` and invoked with three - * arguments: (value, other [, index|key]). - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. Functions and DOM nodes - * are **not** supported. Provide a customizer function to extend support - * for comparing other values. - * - * @static - * @memberOf _ - * @alias eq - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize value comparisons. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'user': 'fred' }; - * var other = { 'user': 'fred' }; - * - * object == other; - * // => false - * - * _.isEqual(object, other); - * // => true - * - * // using a customizer callback - * var array = ['hello', 'goodbye']; - * var other = ['hi', 'goodbye']; - * - * _.isEqual(array, other, function(value, other) { - * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { - * return true; - * } - * }); - * // => true - */ - function isEqual(value, other, customizer, thisArg) { - customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; - var result = customizer ? customizer(value, other) : undefined; - return result === undefined ? baseIsEqual(value, other, customizer) : !!result; - } - - /** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ - function isError(value) { - return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag; - } - - /** - * Checks if `value` is a finite primitive number. - * - * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite). - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. - * @example - * - * _.isFinite(10); - * // => true - * - * _.isFinite('10'); - * // => false - * - * _.isFinite(true); - * // => false - * - * _.isFinite(Object(10)); - * // => false - * - * _.isFinite(Infinity); - * // => false - */ - function isFinite(value) { - return typeof value == 'number' && nativeIsFinite(value); - } - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - // The use of `Object#toString` avoids issues with the `typeof` operator - // in older versions of Chrome and Safari which return 'function' for regexes - // and Safari 8 equivalents which return 'object' for typed array constructors. - return isObject(value) && objToString.call(value) == funcTag; - } - - /** - * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(1); - * // => false - */ - function isObject(value) { - // Avoid a V8 JIT bug in Chrome 19-20. - // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); - } - - /** - * Performs a deep comparison between `object` and `source` to determine if - * `object` contains equivalent property values. If `customizer` is provided - * it is invoked to compare values. If `customizer` returns `undefined` - * comparisons are handled by the method instead. The `customizer` is bound - * to `thisArg` and invoked with three arguments: (value, other, index|key). - * - * **Note:** This method supports comparing properties of arrays, booleans, - * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions - * and DOM nodes are **not** supported. Provide a customizer function to extend - * support for comparing other values. - * - * @static - * @memberOf _ - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize value comparisons. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.isMatch(object, { 'age': 40 }); - * // => true - * - * _.isMatch(object, { 'age': 36 }); - * // => false - * - * // using a customizer callback - * var object = { 'greeting': 'hello' }; - * var source = { 'greeting': 'hi' }; - * - * _.isMatch(object, source, function(value, other) { - * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; - * }); - * // => true - */ - function isMatch(object, source, customizer, thisArg) { - customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; - return baseIsMatch(object, getMatchData(source), customizer); - } - - /** - * Checks if `value` is `NaN`. - * - * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4) - * which returns `true` for `undefined` and other non-numeric values. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - function isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some host objects in IE. - return isNumber(value) && value != +value; - } - - /** - * Checks if `value` is a native function. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ - function isNative(value) { - if (value == null) { - return false; - } - if (isFunction(value)) { - return reIsNative.test(fnToString.call(value)); - } - return isObjectLike(value) && reIsHostCtor.test(value); - } - - /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(void 0); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified - * as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isNumber(8.4); - * // => true - * - * _.isNumber(NaN); - * // => true - * - * _.isNumber('8.4'); - * // => false - */ - function isNumber(value) { - return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag); - } - - /** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * **Note:** This method assumes objects created by the `Object` constructor - * have no inherited enumerable properties. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ - function isPlainObject(value) { - var Ctor; - - // Exit early for non `Object` objects. - if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || - (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { - return false; - } - // IE < 9 iterates inherited properties before own properties. If the first - // iterated property is an object's own property then there are no inherited - // enumerable properties. - var result; - // In most environments an object's own properties are iterated before - // its inherited properties. If the last iterated property is an object's - // own property then there are no inherited enumerable properties. - baseForIn(value, function(subValue, key) { - result = key; - }); - return result === undefined || hasOwnProperty.call(value, result); - } - - /** - * Checks if `value` is classified as a `RegExp` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isRegExp(/abc/); - * // => true - * - * _.isRegExp('/abc/'); - * // => false - */ - function isRegExp(value) { - return isObject(value) && objToString.call(value) == regexpTag; - } - - /** - * Checks if `value` is classified as a `String` primitive or object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isString('abc'); - * // => true - * - * _.isString(1); - * // => false - */ - function isString(value) { - return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - function isTypedArray(value) { - return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; - } - - /** - * Checks if `value` is `undefined`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - * - * _.isUndefined(null); - * // => false - */ - function isUndefined(value) { - return value === undefined; - } - - /** - * Checks if `value` is less than `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`. - * @example - * - * _.lt(1, 3); - * // => true - * - * _.lt(3, 3); - * // => false - * - * _.lt(3, 1); - * // => false - */ - function lt(value, other) { - return value < other; - } - - /** - * Checks if `value` is less than or equal to `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`. - * @example - * - * _.lte(1, 3); - * // => true - * - * _.lte(3, 3); - * // => true - * - * _.lte(3, 1); - * // => false - */ - function lte(value, other) { - return value <= other; - } - - /** - * Converts `value` to an array. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * (function() { - * return _.toArray(arguments).slice(1); - * }(1, 2, 3)); - * // => [2, 3] - */ - function toArray(value) { - var length = value ? getLength(value) : 0; - if (!isLength(length)) { - return values(value); - } - if (!length) { - return []; - } - return arrayCopy(value); - } - - /** - * Converts `value` to a plain object flattening inherited enumerable - * properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ - function toPlainObject(value) { - return baseCopy(value, keysIn(value)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Recursively merges own enumerable properties of the source object(s), that - * don't resolve to `undefined` into the destination object. Subsequent sources - * overwrite property assignments of previous sources. If `customizer` is - * provided it is invoked to produce the merged values of the destination and - * source properties. If `customizer` returns `undefined` merging is handled - * by the method instead. The `customizer` is bound to `thisArg` and invoked - * with five arguments: (objectValue, sourceValue, key, object, source). - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {Object} Returns `object`. - * @example - * - * var users = { - * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] - * }; - * - * var ages = { - * 'data': [{ 'age': 36 }, { 'age': 40 }] - * }; - * - * _.merge(users, ages); - * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } - * - * // using a customizer callback - * var object = { - * 'fruits': ['apple'], - * 'vegetables': ['beet'] - * }; - * - * var other = { - * 'fruits': ['banana'], - * 'vegetables': ['carrot'] - * }; - * - * _.merge(object, other, function(a, b) { - * if (_.isArray(a)) { - * return a.concat(b); - * } - * }); - * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } - */ - var merge = createAssigner(baseMerge); - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object. Subsequent sources overwrite property assignments of previous sources. - * If `customizer` is provided it is invoked to produce the assigned values. - * The `customizer` is bound to `thisArg` and invoked with five arguments: - * (objectValue, sourceValue, key, object, source). - * - * **Note:** This method mutates `object` and is based on - * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). - * - * @static - * @memberOf _ - * @alias extend - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {Object} Returns `object`. - * @example - * - * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); - * // => { 'user': 'fred', 'age': 40 } - * - * // using a customizer callback - * var defaults = _.partialRight(_.assign, function(value, other) { - * return _.isUndefined(value) ? other : value; - * }); - * - * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); - * // => { 'user': 'barney', 'age': 36 } - */ - var assign = createAssigner(function(object, source, customizer) { - return customizer - ? assignWith(object, source, customizer) - : baseAssign(object, source); - }); - - /** - * Creates an object that inherits from the given `prototype` object. If a - * `properties` object is provided its own enumerable properties are assigned - * to the created object. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { - * 'constructor': Circle - * }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ - function create(prototype, properties, guard) { - var result = baseCreate(prototype); - if (guard && isIterateeCall(prototype, properties, guard)) { - properties = undefined; - } - return properties ? baseAssign(result, properties) : result; - } - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object for all destination properties that resolve to `undefined`. Once a - * property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); - * // => { 'user': 'barney', 'age': 36 } - */ - var defaults = createDefaults(assign, assignDefaults); - - /** - * This method is like `_.defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); - * // => { 'user': { 'name': 'barney', 'age': 36 } } - * - */ - var defaultsDeep = createDefaults(merge, mergeDefaults); - - /** - * This method is like `_.find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findKey(users, function(chr) { - * return chr.age < 40; - * }); - * // => 'barney' (iteration order is not guaranteed) - * - * // using the `_.matches` callback shorthand - * _.findKey(users, { 'age': 1, 'active': true }); - * // => 'pebbles' - * - * // using the `_.matchesProperty` callback shorthand - * _.findKey(users, 'active', false); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.findKey(users, 'active'); - * // => 'barney' - */ - var findKey = createFindKey(baseForOwn); - - /** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findLastKey(users, function(chr) { - * return chr.age < 40; - * }); - * // => returns `pebbles` assuming `_.findKey` returns `barney` - * - * // using the `_.matches` callback shorthand - * _.findLastKey(users, { 'age': 36, 'active': true }); - * // => 'barney' - * - * // using the `_.matchesProperty` callback shorthand - * _.findLastKey(users, 'active', false); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.findLastKey(users, 'active'); - * // => 'pebbles' - */ - var findLastKey = createFindKey(baseForOwnRight); - - /** - * Iterates over own and inherited enumerable properties of an object invoking - * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, key, object). Iteratee functions may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forIn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) - */ - var forIn = createForIn(baseFor); - - /** - * This method is like `_.forIn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forInRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' - */ - var forInRight = createForIn(baseForRight); - - /** - * Iterates over own enumerable properties of an object invoking `iteratee` - * for each property. The `iteratee` is bound to `thisArg` and invoked with - * three arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'a' and 'b' (iteration order is not guaranteed) - */ - var forOwn = createForOwn(baseForOwn); - - /** - * This method is like `_.forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwnRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b' - */ - var forOwnRight = createForOwn(baseForOwnRight); - - /** - * Creates an array of function property names from all enumerable properties, - * own and inherited, of `object`. - * - * @static - * @memberOf _ - * @alias methods - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the new array of property names. - * @example - * - * _.functions(_); - * // => ['after', 'ary', 'assign', ...] - */ - function functions(object) { - return baseFunctions(object, keysIn(object)); - } - - /** - * Gets the property value at `path` of `object`. If the resolved value is - * `undefined` the `defaultValue` is used in its place. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct property. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. - * @example - * - * var object = { 'a': { 'b': { 'c': 3 } } }; - * - * _.has(object, 'a'); - * // => true - * - * _.has(object, 'a.b.c'); - * // => true - * - * _.has(object, ['a', 'b', 'c']); - * // => true - */ - function has(object, path) { - if (object == null) { - return false; - } - var result = hasOwnProperty.call(object, path); - if (!result && !isKey(path)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - if (object == null) { - return false; - } - path = last(path); - result = hasOwnProperty.call(object, path); - } - return result || (isLength(object.length) && isIndex(path, object.length) && - (isArray(object) || isArguments(object))); - } - - /** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite property - * assignments of previous values unless `multiValue` is `true`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to invert. - * @param {boolean} [multiValue] Allow multiple values per key. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invert(object); - * // => { '1': 'c', '2': 'b' } - * - * // with `multiValue` - * _.invert(object, true); - * // => { '1': ['a', 'c'], '2': ['b'] } - */ - function invert(object, multiValue, guard) { - if (guard && isIterateeCall(object, multiValue, guard)) { - multiValue = undefined; - } - var index = -1, - props = keys(object), - length = props.length, - result = {}; - - while (++index < length) { - var key = props[index], - value = object[key]; - - if (multiValue) { - if (hasOwnProperty.call(result, value)) { - result[value].push(key); - } else { - result[value] = [key]; - } - } - else { - result[value] = key; - } - } - return result; - } - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) - * for more details. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - var keys = !nativeKeys ? shimKeys : function(object) { - var Ctor = object == null ? undefined : object.constructor; - if ((typeof Ctor == 'function' && Ctor.prototype === object) || - (typeof object != 'function' && isArrayLike(object))) { - return shimKeys(object); - } - return isObject(object) ? nativeKeys(object) : []; - }; - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ - function keysIn(object) { - if (object == null) { - return []; - } - if (!isObject(object)) { - object = Object(object); - } - var length = object.length; - length = (length && isLength(length) && - (isArray(object) || isArguments(object)) && length) || 0; - - var Ctor = object.constructor, - index = -1, - isProto = typeof Ctor == 'function' && Ctor.prototype === object, - result = Array(length), - skipIndexes = length > 0; - - while (++index < length) { - result[index] = (index + ''); - } - for (var key in object) { - if (!(skipIndexes && isIndex(key, length)) && - !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { - result.push(key); - } - } - return result; - } - - /** - * The opposite of `_.mapValues`; this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * property of `object` through `iteratee`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the new mapped object. - * @example - * - * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value; - * }); - * // => { 'a1': 1, 'b2': 2 } - */ - var mapKeys = createObjectMapper(true); - - /** - * Creates an object with the same keys as `object` and values generated by - * running each own enumerable property of `object` through `iteratee`. The - * iteratee function is bound to `thisArg` and invoked with three arguments: - * (value, key, object). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the new mapped object. - * @example - * - * _.mapValues({ 'a': 1, 'b': 2 }, function(n) { - * return n * 3; - * }); - * // => { 'a': 3, 'b': 6 } - * - * var users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * }; - * - * // using the `_.property` callback shorthand - * _.mapValues(users, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ - var mapValues = createObjectMapper(); - - /** - * The opposite of `_.pick`; this method creates an object composed of the - * own and inherited enumerable properties of `object` that are not omitted. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {Function|...(string|string[])} [predicate] The function invoked per - * iteration or property names to omit, specified as individual property - * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.omit(object, 'age'); - * // => { 'user': 'fred' } - * - * _.omit(object, _.isNumber); - * // => { 'user': 'fred' } - */ - var omit = restParam(function(object, props) { - if (object == null) { - return {}; - } - if (typeof props[0] != 'function') { - var props = arrayMap(baseFlatten(props), String); - return pickByArray(object, baseDifference(keysIn(object), props)); - } - var predicate = bindCallback(props[0], props[1], 3); - return pickByCallback(object, function(value, key, object) { - return !predicate(value, key, object); - }); - }); - - /** - * Creates a two dimensional array of the key-value pairs for `object`, - * e.g. `[[key1, value1], [key2, value2]]`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the new array of key-value pairs. - * @example - * - * _.pairs({ 'barney': 36, 'fred': 40 }); - * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) - */ - function pairs(object) { - object = toObject(object); - - var index = -1, - props = keys(object), - length = props.length, - result = Array(length); - - while (++index < length) { - var key = props[index]; - result[index] = [key, object[key]]; - } - return result; - } - - /** - * Creates an object composed of the picked `object` properties. Property - * names may be specified as individual arguments or as arrays of property - * names. If `predicate` is provided it is invoked for each property of `object` - * picking the properties `predicate` returns truthy for. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, key, object). - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {Function|...(string|string[])} [predicate] The function invoked per - * iteration or property names to pick, specified as individual property - * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.pick(object, 'user'); - * // => { 'user': 'fred' } - * - * _.pick(object, _.isString); - * // => { 'user': 'fred' } - */ - var pick = restParam(function(object, props) { - if (object == null) { - return {}; - } - return typeof props[0] == 'function' - ? pickByCallback(object, bindCallback(props[0], props[1], 3)) - : pickByArray(object, baseFlatten(props)); - }); - - /** - * This method is like `_.get` except that if the resolved value is a function - * it is invoked with the `this` binding of its parent object and its result - * is returned. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; - * - * _.result(object, 'a[0].b.c1'); - * // => 3 - * - * _.result(object, 'a[0].b.c2'); - * // => 4 - * - * _.result(object, 'a.b.c', 'default'); - * // => 'default' - * - * _.result(object, 'a.b.c', _.constant('default')); - * // => 'default' - */ - function result(object, path, defaultValue) { - var result = object == null ? undefined : object[path]; - if (result === undefined) { - if (object != null && !isKey(path, object)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - result = object == null ? undefined : object[last(path)]; - } - result = result === undefined ? defaultValue : result; - } - return isFunction(result) ? result.call(object) : result; - } - - /** - * Sets the property value of `path` on `object`. If a portion of `path` - * does not exist it is created. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to augment. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.set(object, 'a[0].b.c', 4); - * console.log(object.a[0].b.c); - * // => 4 - * - * _.set(object, 'x[0].y.z', 5); - * console.log(object.x[0].y.z); - * // => 5 - */ - function set(object, path, value) { - if (object == null) { - return object; - } - var pathKey = (path + ''); - path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); - - var index = -1, - length = path.length, - lastIndex = length - 1, - nested = object; - - while (nested != null && ++index < length) { - var key = path[index]; - if (isObject(nested)) { - if (index == lastIndex) { - nested[key] = value; - } else if (nested[key] == null) { - nested[key] = isIndex(path[index + 1]) ? [] : {}; - } - } - nested = nested[key]; - } - return object; - } - - /** - * An alternative to `_.reduce`; this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own enumerable - * properties through `iteratee`, with each invocation potentially mutating - * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked - * with four arguments: (accumulator, value, key, object). Iteratee functions - * may exit iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Array|Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * _.transform([2, 3, 4], function(result, n) { - * result.push(n *= n); - * return n % 2 == 0; - * }); - * // => [4, 9] - * - * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) { - * result[key] = n * 3; - * }); - * // => { 'a': 3, 'b': 6 } - */ - function transform(object, iteratee, accumulator, thisArg) { - var isArr = isArray(object) || isTypedArray(object); - iteratee = getCallback(iteratee, thisArg, 4); - - if (accumulator == null) { - if (isArr || isObject(object)) { - var Ctor = object.constructor; - if (isArr) { - accumulator = isArray(object) ? new Ctor : []; - } else { - accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined); - } - } else { - accumulator = {}; - } - } - (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { - return iteratee(accumulator, value, index, object); - }); - return accumulator; - } - - /** - * Creates an array of the own enumerable property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.values(new Foo); - * // => [1, 2] (iteration order is not guaranteed) - * - * _.values('hi'); - * // => ['h', 'i'] - */ - function values(object) { - return baseValues(object, keys(object)); - } - - /** - * Creates an array of the own and inherited enumerable property values - * of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.valuesIn(new Foo); - * // => [1, 2, 3] (iteration order is not guaranteed) - */ - function valuesIn(object) { - return baseValues(object, keysIn(object)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Checks if `n` is between `start` and up to but not including, `end`. If - * `end` is not specified it is set to `start` with `start` then set to `0`. - * - * @static - * @memberOf _ - * @category Number - * @param {number} n The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `n` is in the range, else `false`. - * @example - * - * _.inRange(3, 2, 4); - * // => true - * - * _.inRange(4, 8); - * // => true - * - * _.inRange(4, 2); - * // => false - * - * _.inRange(2, 2); - * // => false - * - * _.inRange(1.2, 2); - * // => true - * - * _.inRange(5.2, 4); - * // => false - */ - function inRange(value, start, end) { - start = +start || 0; - if (end === undefined) { - end = start; - start = 0; - } else { - end = +end || 0; - } - return value >= nativeMin(start, end) && value < nativeMax(start, end); - } - - /** - * Produces a random number between `min` and `max` (inclusive). If only one - * argument is provided a number between `0` and the given number is returned. - * If `floating` is `true`, or either `min` or `max` are floats, a floating-point - * number is returned instead of an integer. - * - * @static - * @memberOf _ - * @category Number - * @param {number} [min=0] The minimum possible value. - * @param {number} [max=1] The maximum possible value. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ - function random(min, max, floating) { - if (floating && isIterateeCall(min, max, floating)) { - max = floating = undefined; - } - var noMin = min == null, - noMax = max == null; - - if (floating == null) { - if (noMax && typeof min == 'boolean') { - floating = min; - min = 1; - } - else if (typeof max == 'boolean') { - floating = max; - noMax = true; - } - } - if (noMin && noMax) { - max = 1; - noMax = false; - } - min = +min || 0; - if (noMax) { - max = min; - min = 0; - } else { - max = +max || 0; - } - if (floating || min % 1 || max % 1) { - var rand = nativeRandom(); - return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max); - } - return baseRandom(min, max); - } - - /*------------------------------------------------------------------------*/ - - /** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Foo Bar'); - * // => 'fooBar' - * - * _.camelCase('--foo-bar'); - * // => 'fooBar' - * - * _.camelCase('__foo_bar__'); - * // => 'fooBar' - */ - var camelCase = createCompounder(function(result, word, index) { - word = word.toLowerCase(); - return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word); - }); - - /** - * Capitalizes the first character of `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * _.capitalize('fred'); - * // => 'Fred' - */ - function capitalize(string) { - string = baseToString(string); - return string && (string.charAt(0).toUpperCase() + string.slice(1)); - } - - /** - * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * _.deburr('déjà vu'); - * // => 'deja vu' - */ - function deburr(string) { - string = baseToString(string); - return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); - } - - /** - * Checks if `string` ends with the given target string. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search from. - * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ - function endsWith(string, target, position) { - string = baseToString(string); - target = (target + ''); - - var length = string.length; - position = position === undefined - ? length - : nativeMin(position < 0 ? 0 : (+position || 0), length); - - position -= target.length; - return position >= 0 && string.indexOf(target, position) == position; - } - - /** - * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to - * their corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional characters - * use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. - * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * Backticks are escaped because in Internet Explorer < 9, they can break out - * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), - * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and - * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) - * for more details. - * - * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) - * to reduce XSS vectors. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('fred, barney, & pebbles'); - * // => 'fred, barney, & pebbles' - */ - function escape(string) { - // Reset `lastIndex` because in IE < 9 `String#replace` does not. - string = baseToString(string); - return (string && reHasUnescapedHtml.test(string)) - ? string.replace(reUnescapedHtml, escapeHtmlChar) - : string; - } - - /** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' - */ - function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, escapeRegExpChar) - : (string || '(?:)'); - } - - /** - * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Foo Bar'); - * // => 'foo-bar' - * - * _.kebabCase('fooBar'); - * // => 'foo-bar' - * - * _.kebabCase('__foo_bar__'); - * // => 'foo-bar' - */ - var kebabCase = createCompounder(function(result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); - }); - - /** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ - function pad(string, length, chars) { - string = baseToString(string); - length = +length; - - var strLength = string.length; - if (strLength >= length || !nativeIsFinite(length)) { - return string; - } - var mid = (length - strLength) / 2, - leftLength = nativeFloor(mid), - rightLength = nativeCeil(mid); - - chars = createPadding('', rightLength, chars); - return chars.slice(0, leftLength) + string + chars; - } - - /** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padLeft('abc', 6); - * // => ' abc' - * - * _.padLeft('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padLeft('abc', 3); - * // => 'abc' - */ - var padLeft = createPadDir(); - - /** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padRight('abc', 6); - * // => 'abc ' - * - * _.padRight('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padRight('abc', 3); - * // => 'abc' - */ - var padRight = createPadDir(true); - - /** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, - * in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E) - * of `parseInt`. - * - * @static - * @memberOf _ - * @category String - * @param {string} string The string to convert. - * @param {number} [radix] The radix to interpret `value` by. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {number} Returns the converted integer. - * @example - * - * _.parseInt('08'); - * // => 8 - * - * _.map(['6', '08', '10'], _.parseInt); - * // => [6, 8, 10] - */ - function parseInt(string, radix, guard) { - // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. - // Chrome fails to trim leading whitespace characters. - // See https://code.google.com/p/v8/issues/detail?id=3109 for more details. - if (guard ? isIterateeCall(string, radix, guard) : radix == null) { - radix = 0; - } else if (radix) { - radix = +radix; - } - string = trim(string); - return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); - } - - /** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=0] The number of times to repeat the string. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ - function repeat(string, n) { - var result = ''; - string = baseToString(string); - n = +n; - if (n < 1 || !string || !nativeIsFinite(n)) { - return result; - } - // Leverage the exponentiation by squaring algorithm for a faster repeat. - // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. - do { - if (n % 2) { - result += string; - } - n = nativeFloor(n / 2); - string += string; - } while (n); - - return result; - } - - /** - * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Foo Bar'); - * // => 'foo_bar' - * - * _.snakeCase('fooBar'); - * // => 'foo_bar' - * - * _.snakeCase('--foo-bar'); - * // => 'foo_bar' - */ - var snakeCase = createCompounder(function(result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); - }); - - /** - * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @example - * - * _.startCase('--foo-bar'); - * // => 'Foo Bar' - * - * _.startCase('fooBar'); - * // => 'Foo Bar' - * - * _.startCase('__foo_bar__'); - * // => 'Foo Bar' - */ - var startCase = createCompounder(function(result, word, index) { - return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1)); - }); - - /** - * Checks if `string` starts with the given target string. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ - function startsWith(string, target, position) { - string = baseToString(string); - position = position == null - ? 0 - : nativeMin(position < 0 ? 0 : (+position || 0), string.length); - - return string.lastIndexOf(target, position) == position; - } - - /** - * Creates a compiled template function that can interpolate data properties - * in "interpolate" delimiters, HTML-escape interpolated data properties in - * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data - * properties may be accessed as free variables in the template. If a setting - * object is provided it takes precedence over `_.templateSettings` values. - * - * **Note:** In the development build `_.template` utilizes - * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) - * for easier debugging. - * - * For more information on precompiling templates see - * [lodash's custom builds documentation](https://lodash.com/custom-builds). - * - * For more information on Chrome extension sandboxes see - * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The template string. - * @param {Object} [options] The options object. - * @param {RegExp} [options.escape] The HTML "escape" delimiter. - * @param {RegExp} [options.evaluate] The "evaluate" delimiter. - * @param {Object} [options.imports] An object to import into the template as free variables. - * @param {RegExp} [options.interpolate] The "interpolate" delimiter. - * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. - * @param {string} [options.variable] The data object variable name. - * @param- {Object} [otherOptions] Enables the legacy `options` param signature. - * @returns {Function} Returns the compiled template function. - * @example - * - * // using the "interpolate" delimiter to create a compiled template - * var compiled = _.template('hello <%= user %>!'); - * compiled({ 'user': 'fred' }); - * // => 'hello fred!' - * - * // using the HTML "escape" delimiter to escape data property values - * var compiled = _.template('<%- value %>'); - * compiled({ 'value': ''; + + +// Part to create +if ($action == 'create') +{ + print load_fiche_titre($langs->trans("NewMyModule")); + + print '
    '; + print ''; + print ''; + + dol_fiche_head(); + + print ''."\n"; + // print ''; + // +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + + print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldfk_product").'
    '.$langs->trans("Fieldbatch").'
    '.$langs->trans("Fieldnote_public").'
    '.$langs->trans("Fieldnote_private").'
    '.$langs->trans("Fieldqty").'
    '.$langs->trans("Fieldimport_key").'
    '."\n"; + + dol_fiche_end(); + + print '
     
    '; + + print '
    '; +} + + + +// Part to edit record +if (($id || $ref) && $action == 'edit') +{ + print load_fiche_titre($langs->trans("MyModule")); + + print '
    '; + print ''; + print ''; + print ''; + + dol_fiche_head(); + + print ''."\n"; + // print ''; + // +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + + print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldfk_product").'
    '.$langs->trans("Fieldbatch").'
    '.$langs->trans("Fieldnote_public").'
    '.$langs->trans("Fieldnote_private").'
    '.$langs->trans("Fieldqty").'
    '.$langs->trans("Fieldimport_key").'
    '; + + dol_fiche_end(); + + print '
    '; + print '   '; + print '
    '; + + print '
    '; +} + + + +// Part to show record +if ($id && (empty($action) || $action == 'view' || $action == 'delete')) +{ + print load_fiche_titre($langs->trans("MyModule")); + + dol_fiche_head(); + + if ($action == 'delete') { + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('DeleteMyOjbect'), $langs->trans('ConfirmDeleteMyObject'), 'confirm_delete', '', 0, 1); + print $formconfirm; + } + + print ''."\n"; + // print ''; + // +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + + print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldfk_product").'$object->fk_product
    '.$langs->trans("Fieldbatch").'$object->batch
    '.$langs->trans("Fieldnote_public").'$object->note_public
    '.$langs->trans("Fieldnote_private").'$object->note_private
    '.$langs->trans("Fieldqty").'$object->qty
    '.$langs->trans("Fieldimport_key").'$object->import_key
    '; + + dol_fiche_end(); + + + // Buttons + print '
    '."\n"; + $parameters=array(); + $reshook=$hookmanager->executeHooks('addMoreActionsButtons',$parameters,$object,$action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + + if (empty($reshook)) + { + if ($user->rights->stock->write) + { + print ''."\n"; + } + + if ($user->rights->stock->delete) + { + print ''."\n"; + } + } + print '
    '."\n"; + + + // Example 2 : Adding links to objects + //$somethingshown=$form->showLinkedObjectBlock($object); + //$linktoelem = $form->showLinkToObjectBlock($object); + //if ($linktoelem) print '
    '.$linktoelem; + +} + + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php new file mode 100644 index 00000000000..ded98827e31 --- /dev/null +++ b/htdocs/product/stock/productlot_list.php @@ -0,0 +1,549 @@ + + * Copyright (C) ---Put here your own copyright and developer email--- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file stock/productlot_list.php + * \ingroup stock + * \brief This file is an example of a php page + * Initialy built by build_class_from_table on 2016-05-17 10:33 + */ + +//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); +//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); +//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); +//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK','1'); // Do not check anti CSRF attack test +//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK','1'); // Do not check style html tag into posted data +//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Do not check anti POST attack test +//if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1'); // If there is no need to load and show top and left menu +//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); // If we don't need to load the html.form.class.php +//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); +//if (! defined("NOLOGIN")) define("NOLOGIN",'1'); // If this page is public (can be called outside logged session) + +// Change this following line to use the correct relative path (../, ../../, etc) +$res=0; +if (! $res && file_exists("../main.inc.php")) $res=@include '../main.inc.php'; // to work if your module directory is into dolibarr root htdocs directory +if (! $res && file_exists("../../main.inc.php")) $res=@include '../../main.inc.php'; // to work if your module directory is into a subdir of root htdocs directory +if (! $res && file_exists("../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only +if (! $res && file_exists("../../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only +if (! $res) die("Include of main fails"); +// Change this following line to use the correct relative path from htdocs +require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'); +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +dol_include_once('/stock/class/productlot.class.php'); + +// Load traductions files requiredby by page +$langs->load("stock"); +$langs->load("other"); + +// Get parameters +$id = GETPOST('id','int'); +$action = GETPOST('action','alpha'); +$backtopage = GETPOST('backtopage'); +$myparam = GETPOST('myparam','alpha'); + + +$search_fk_product=GETPOST('search_fk_product','int'); +$search_batch=GETPOST('search_batch','alpha'); +$search_note_public=GETPOST('search_note_public','alpha'); +$search_note_private=GETPOST('search_note_private','alpha'); +$search_qty=GETPOST('search_qty','alpha'); +$search_import_key=GETPOST('search_import_key','alpha'); + + +$search_myfield=GETPOST('search_myfield'); +$optioncss = GETPOST('optioncss','alpha'); + +// Load variable for pagination +$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit; +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if ($page == -1) { $page = 0; } +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortfield) $sortfield="t.rowid"; // Set here default search field +if (! $sortorder) $sortorder="ASC"; + +// Protection if external user +$socid=0; +if ($user->societe_id > 0) +{ + $socid = $user->societe_id; + //accessforbidden(); +} + +// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array +$hookmanager->initHooks(array('productlotlist')); +$extrafields = new ExtraFields($db); + +// fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('stock'); +$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); + +// Load object if id or ref is provided as parameter +$object=new Productlot($db); +if (($id > 0 || ! empty($ref)) && $action != 'add') +{ + $result=$object->fetch($id,$ref); + if ($result < 0) dol_print_error($db); +} + +// Definition of fields for list +$arrayfields=array( + +'t.fk_product'=>array('label'=>$langs->trans("Fieldfk_product"), 'checked'=>1), +'t.batch'=>array('label'=>$langs->trans("Fieldbatch"), 'checked'=>1), +'t.note_public'=>array('label'=>$langs->trans("Fieldnote_public"), 'checked'=>1), +'t.note_private'=>array('label'=>$langs->trans("Fieldnote_private"), 'checked'=>1), +'t.qty'=>array('label'=>$langs->trans("Fieldqty"), 'checked'=>1), +'t.import_key'=>array('label'=>$langs->trans("Fieldimport_key"), 'checked'=>1), + + + //'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))), + 't.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500), + 't.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500), + //'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000), +); +// Extra fields +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +{ + foreach($extrafields->attribute_label as $key => $val) + { + $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]); + } +} + + + + +/******************************************************************* +* ACTIONS +* +* Put here all code to do according to value of "action" parameter +********************************************************************/ + +if (GETPOST('cancel')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction')) { $massaction=''; } + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers +{ + +$search_fk_product=''; +$search_batch=''; +$search_note_public=''; +$search_note_private=''; +$search_qty=''; +$search_import_key=''; + + + $search_date_creation=''; + $search_date_update=''; + $search_array_options=array(); +} + + +if (empty($reshook)) +{ + // Mass actions. Controls on number of lines checked + $maxformassaction=1000; + if (! empty($massaction) && count($toselect) < 1) + { + $error++; + setEventMessages($langs->trans("NoLineChecked"), null, "warnings"); + } + if (! $error && count($toselect) > $maxformassaction) + { + setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors'); + $error++; + } + + // Action to delete + if ($action == 'confirm_delete') + { + $result=$object->delete($user); + if ($result > 0) + { + // Delete OK + setEventMessages("RecordDeleted", null, 'mesgs'); + header("Location: ".dol_buildpath('/stock/list.php',1)); + exit; + } + else + { + if (! empty($object->errors)) setEventMessages(null,$object->errors,'errors'); + else setEventMessages($object->error,null,'errors'); + } + } +} + + + + +/*************************************************** +* VIEW +* +* Put here all code to build page +****************************************************/ + +llxHeader('','MyPageName',''); + +$form=new Form($db); + +// Put here content of your page +$title = $langs->trans('MyModuleListTitle'); + +// Example : Adding jquery code +print ''; + + +$sql = "SELECT"; +$sql.= " t.rowid,"; + + $sql .= " t.tms,"; + $sql .= " t.fk_product,"; + $sql .= " t.batch,"; + $sql .= " t.eatby,"; + $sql .= " t.sellby,"; + $sql .= " t.note_public,"; + $sql .= " t.note_private,"; + $sql .= " t.qty,"; + $sql .= " t.import_key"; + + +// Add fields for extrafields +foreach ($extrafields->attribute_list as $key => $val) $sql.=",ef.".$key.' as options_'.$key; +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +$sql.= " FROM ".MAIN_DB_PREFIX."product_lot as t"; +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot_extrafields as ef on (u.rowid = ef.fk_object)"; +$sql.= " WHERE 1 = 1"; +//$sql.= " WHERE u.entity IN (".getEntity('mytable',1).")"; + +if ($search_fk_product) $sql.= natural_search("fk_product",$search_fk_product); +if ($search_batch) $sql.= natural_search("batch",$search_batch); +if ($search_note_public) $sql.= natural_search("note_public",$search_note_public); +if ($search_note_private) $sql.= natural_search("note_private",$search_note_private); +if ($search_qty) $sql.= natural_search("qty",$search_qty); +if ($search_import_key) $sql.= natural_search("import_key",$search_import_key); + + +if ($sall) $sql.= natural_search(array_keys($fieldstosearchall), $sall); +// Add where from extra fields +foreach ($search_array_options as $key => $val) +{ + $crit=$val; + $tmpkey=preg_replace('/search_options_/','',$key); + $typ=$extrafields->attribute_type[$tmpkey]; + $mode=0; + if (in_array($typ, array('int','double'))) $mode=1; // Search on a numeric + if ($val && ( ($crit != '' && ! in_array($typ, array('select'))) || ! empty($crit))) + { + $sql .= natural_search('ef.'.$tmpkey, $crit, $mode); + } +} +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListWhere',$parameters); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +$sql.=$db->order($sortfield,$sortorder); +//$sql.= $db->plimit($conf->liste_limit+1, $offset); + +// Count total nb of records +$nbtotalofrecords = 0; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); +} + +$sql.= $db->plimit($limit+1, $offset); + + +dol_syslog($script_file, LOG_DEBUG); +$resql=$db->query($sql); +if ($resql) +{ + $num = $db->num_rows($resql); + + $params=''; + if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; + +if ($search_fk_product != '') $params.= '&search_fk_product='.urlencode($search_fk_product); +if ($search_batch != '') $params.= '&search_batch='.urlencode($search_batch); +if ($search_note_public != '') $params.= '&search_note_public='.urlencode($search_note_public); +if ($search_note_private != '') $params.= '&search_note_private='.urlencode($search_note_private); +if ($search_qty != '') $params.= '&search_qty='.urlencode($search_qty); +if ($search_import_key != '') $params.= '&search_import_key='.urlencode($search_import_key); + + + if ($optioncss != '') $param.='&optioncss='.$optioncss; + // Add $param from extra fields + foreach ($search_array_options as $key => $val) + { + $crit=$val; + $tmpkey=preg_replace('/search_options_/','',$key); + if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val); + } + + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit); + + + print '
    '; + if ($optioncss != '') print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + if ($sall) + { + foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); + print $langs->trans("FilterOnInto", $all) . join(', ',$fieldstosearchall); + } + + $moreforfilter = ''; + $moreforfilter.='
    '; + $moreforfilter.= $langs->trans('MyFilter') . ': '; + $moreforfilter.= '
    '; + + if (! empty($moreforfilter)) + { + print '
    '; + print $moreforfilter; + $parameters=array(); + $reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + print '
    '; + } + + $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; + $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields + + print ''; + + // Fields title + print ''; + if (! empty($arrayfields['t.field1']['checked'])) print_liste_field_titre($arrayfields['t.field1']['label'],$_SERVER['PHP_SELF'],'t.field1','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.field2']['checked'])) print_liste_field_titre($arrayfields['t.field2']['label'],$_SERVER['PHP_SELF'],'t.field2','',$param,'',$sortfield,$sortorder); + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) + { + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($arrayfields["ef.".$key]['checked'])) + { + $align=$extrafields->getAlignFlag($key); + print_liste_field_titre($extralabels[$key],$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder); + } + } + } + // Hook fields + $parameters=array('arrayfields'=>$arrayfields); + $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + if (! empty($arrayfields['t.datec']['checked'])) print_liste_field_titre($arrayfields['t.datec']['label'],$_SERVER["PHP_SELF"],"t.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); + if (! empty($arrayfields['t.tms']['checked'])) print_liste_field_titre($arrayfields['t.tms']['label'],$_SERVER["PHP_SELF"],"t.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); + //if (! empty($arrayfields['t.status']['checked'])) print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"t.status","",$param,'align="center"',$sortfield,$sortorder); + print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch '); + print ''."\n"; + + // Fields title search + print ''; + +if (! empty($arrayfields['t.fk_product']['checked'])) print ''; +if (! empty($arrayfields['t.batch']['checked'])) print ''; +if (! empty($arrayfields['t.note_public']['checked'])) print ''; +if (! empty($arrayfields['t.note_private']['checked'])) print ''; +if (! empty($arrayfields['t.qty']['checked'])) print ''; +if (! empty($arrayfields['t.import_key']['checked'])) print ''; + + + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) + { + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($arrayfields["ef.".$key]['checked'])) + { + $align=$extrafields->getAlignFlag($key); + $typeofextrafield=$extrafields->attribute_type[$key]; + print ''; + } + } + } + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields); + $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + if (! empty($arrayfields['t.datec']['checked'])) + { + // Date creation + print ''; + } + if (! empty($arrayfields['t.tms']['checked'])) + { + // Date modification + print ''; + } + /*if (! empty($arrayfields['u.statut']['checked'])) + { + // Status + print ''; + }*/ + // Action column + print ''; + print ''."\n"; + + + $i=0; + $var=true; + $totalarray=array(); + while ($i < min($num, $limit)) + { + $obj = $db->fetch_object($resql); + if ($obj) + { + // You can use here results + print ''; + if (! empty($arrayfields['t.field1']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + if (! empty($arrayfields['t.field2']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) + { + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($arrayfields["ef.".$key]['checked'])) + { + print 'getAlignFlag($key); + if ($align) print ' align="'.$align.'"'; + print '>'; + $tmpkey='options_'.$key; + print $extrafields->showOutputField($key, $obj->$tmpkey, '', 1); + print ''; + if (! $i) $totalarray['nbfield']++; + } + } + } + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Date creation + if (! empty($arrayfields['t.datec']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Date modification + if (! empty($arrayfields['t.tms']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Status + /* + if (! empty($arrayfields['u.statut']['checked'])) + { + $userstatic->statut=$obj->statut; + print ''; + }*/ + + // Action column + print ''; + if (! $i) $totalarray['nbfield']++; + + print ''; + } + $i++; + } + + $db->free($resql); + + $parameters=array('sql' => $sql); + $reshook=$hookmanager->executeHooks('printFieldListFooter',$parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + + print "
    '; + if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select'))) + { + $crit=$val; + $tmpkey=preg_replace('/search_options_/','',$key); + $searchclass=''; + if (in_array($typeofextrafield, array('varchar', 'select'))) $searchclass='searchstring'; + if (in_array($typeofextrafield, array('int', 'double'))) $searchclass='searchnum'; + print ''; + } + print ''; + print ''; + print ''; + print $form->selectarray('search_statut', array('-1'=>'','0'=>$langs->trans('Disabled'),'1'=>$langs->trans('Enabled')),$search_statut); + print ''; + $searchpitco=$form->showFilterAndCheckAddButtons(0); + print $searchpitco; + print '
    '.$obj->field1.''.$obj->field2.''; + print dol_print_date($db->jdate($obj->date_creation), 'dayhour'); + print ''; + print dol_print_date($db->jdate($obj->date_update), 'dayhour'); + print ''.$userstatic->getLibStatut(3).'
    \n"; + print "
    \n"; + + $db->free($result); +} +else +{ + $error++; + dol_print_error($db); +} + + +// End of page +llxFooter(); +$db->close(); From 668df6cb7d1c13da37c124e282b6bebb1473e7c9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 10:51:13 +0200 Subject: [PATCH 698/834] Debug resource module --- .../resource/class/html.formresource.class.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/htdocs/resource/class/html.formresource.class.php b/htdocs/resource/class/html.formresource.class.php index 82b0ebe7d1a..a086f3c7e07 100644 --- a/htdocs/resource/class/html.formresource.class.php +++ b/htdocs/resource/class/html.formresource.class.php @@ -89,15 +89,15 @@ class FormResource if ($resourcestat) { - if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && ! $forcecombo) + if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->RESOURCE_USE_SEARCH_TO_SELECT) && ! $forcecombo) { - //$minLength = (is_numeric($conf->global->COMPANY_USE_SEARCH_TO_SELECT)?$conf->global->COMPANY_USE_SEARCH_TO_SELECT:2); - $out.= ajax_combobox($htmlname, $event, $conf->global->COMPANY_USE_SEARCH_TO_SELECT); + //$minLength = (is_numeric($conf->global->RESOURCE_USE_SEARCH_TO_SELECT)?$conf->global->RESOURCE_USE_SEARCH_TO_SELECT:2); + $out.= ajax_combobox($htmlname, $event, $conf->global->RESOURCE_USE_SEARCH_TO_SELECT); } // Construct $out and $outarray - $out.= ''."\n"; + if ($showempty) $out.= ''."\n"; $num = count($resourcestat->lines); //var_dump($resourcestat->lines); @@ -106,9 +106,11 @@ class FormResource { while ( $i < $num) { - $label=$langs->trans(ucfirst($resourcestat->lines[$i]->element)).' : '; - $label.=$resourcestat->lines[$i]->ref?$resourcestat->lines[$i]->ref:''.$resourcestat->lines[$i]->label; - + $resourceclass=ucfirst($resourcestat->lines[$i]->element); + + $label=$resourcestat->lines[$i]->ref?$resourcestat->lines[$i]->ref:''.$resourcestat->lines[$i]->label; + if ($resourceclass != 'Dolresource') $label.=' ('.$langs->trans($resourceclass).')'; + if ($selected > 0 && $selected == $resourcestat->lines[$i]->id) { $out.= ''; From 0e040b166dbe69bd83710e78462b38caa31037c8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 17:55:41 +0200 Subject: [PATCH 699/834] Fix: When we edit a product to use lot/serial number, we initialize records for unknown lots to generic value 'Undefined'. --- htdocs/product/class/product.class.php | 35 ++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 3cc72ac9b97..fce31e31923 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -666,25 +666,45 @@ class Product extends CommonObject $org->fetch($this->id); $this->oldcopy=$org; } - // test if batch management is activated on existing product + + // Test if batch management is activated on existing product + // If yes, we create missing entries into product_batch if ($this->hasbatch() && !$this->oldcopy->hasbatch()) { + $valueforundefinedlot = 'Undefined'; + + dol_syslog("Flag batch of product id=".$this->id." is set to ON, so we will create missing records into product_batch"); + $this->load_stock(); - foreach ($this->stock_warehouse as $idW => $ObjW) + foreach ($this->stock_warehouse as $idW => $ObjW) // For each warehouse where we have stocks defined for this product (for each lines in product_stock) { $qty_batch = 0; - foreach ($ObjW->detail_batch as $detail) + foreach ($ObjW->detail_batch as $detail) // Each lines of detail in product_batch of the current $ObjW = product_stock { + if ($detail->batch == $valueforundefinedlot || $detail->batch == 'Undefined') + { + // We discard this line, we will create it later + $sqlclean="DELETE FROM ".MAIN_DB_PREFIX."product_batch WHERE batch in('Undefined', '".$valueforundefinedlot."') AND fk_product_stock = ".$ObjW->id; + $result = $this->db->query($sqlclean); + if (! $result) + { + dol_print_error($this->db); + exit; + } + continue; + } + $qty_batch += $detail->qty; } - // Quantities in batch details are not same same as stock quantity - // So we add a default batch record + // Quantities in batch details are not same as stock quantity, + // so we add a default batch record to complete and get same qty in parent and child table if ($ObjW->real <> $qty_batch) { $ObjBatch = new Productbatch($this->db); - $ObjBatch->batch = $langs->trans('BatchDefaultNumber'); - $ObjBatch->qty = $ObjW->real - $qty_batch; + $ObjBatch->batch = $valueforundefinedlot; + $ObjBatch->qty = ($ObjW->real - $qty_batch); $ObjBatch->fk_product_stock = $ObjW->id; + if ($ObjBatch->create($user,1) < 0) { $error++; @@ -693,6 +713,7 @@ class Product extends CommonObject } } } + // For automatic creation if ($this->barcode == -1) $this->barcode = $this->get_barcode($this,$this->barcode_type_code); From 79be93d998bee5295148dfe26501708bb8358d32 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 14:02:18 +0200 Subject: [PATCH 700/834] FIX The table llx_product_lot is now filled/used to store eatby/sellby dates. --- dev/skeletons/build_class_from_table.php | 2 +- .../install/mysql/migration/3.9.0-4.0.0.sql | 7 ++ htdocs/langs/en_US/productbatch.lang | 1 - htdocs/product/class/product.class.php | 42 +++++-- htdocs/product/class/productbatch.class.php | 58 +++++---- htdocs/product/reassortlot.php | 20 +-- .../stock/class/mouvementstock.class.php | 53 +++++++- .../product/stock/class/productlot.class.php | 118 ++++++++++-------- htdocs/product/stock/mouvement.php | 5 +- htdocs/product/stock/product.php | 68 +++++----- htdocs/product/stock/productlot_card.php | 44 +++---- htdocs/product/stock/productlot_list.php | 49 ++++---- .../product/stock/tpl/stocktransfer.tpl.php | 11 +- 13 files changed, 301 insertions(+), 177 deletions(-) diff --git a/dev/skeletons/build_class_from_table.php b/dev/skeletons/build_class_from_table.php index 800bafe6318..ecdbbd09dc4 100755 --- a/dev/skeletons/build_class_from_table.php +++ b/dev/skeletons/build_class_from_table.php @@ -259,7 +259,7 @@ foreach($property as $key => $prop) if ($addfield) { $varprop.="\t\t\$sql.= '".$prop['field']; - if ($i <= count($property)-$no_output_field) $varprop.=","; + if ($i < (count($property)-$no_output_field)) $varprop.=","; $varprop.="';"; $varprop.="\n"; } diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 93469ff5a8f..f34e52dafe6 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -54,6 +54,8 @@ CREATE TABLE llx_product_lot ( import_key integer ) ENGINE=InnoDB; +ALTER TABLE llx_product_lot ADD UNIQUE INDEX uk_product_lot(fk_product, batch); + DROP TABLE llx_stock_serial; ALTER TABLE llx_product ADD COLUMN note_public text; @@ -460,6 +462,11 @@ CREATE TABLE llx_advtargetemailing ALTER TABLE llx_advtargetemailing ADD UNIQUE INDEX uk_advtargetemailing_name (name); +update llx_product_batch set batch = '000000' where batch = 'Non défini'; +update llx_product_batch set batch = '000000' where batch = 'Non défini'; +update llx_product_batch set batch = '000000' where batch = 'Undefined'; +update llx_product_lot set batch = '000000' where batch = 'Undefined'; +update llx_stock_mouvement set batch = '000000' where batch = 'Undefined'; -- At end ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang index 37ceaa49b38..c3d78f7ff7f 100644 --- a/htdocs/langs/en_US/productbatch.lang +++ b/htdocs/langs/en_US/productbatch.lang @@ -17,6 +17,5 @@ printEatby=Eat-by: %s printSellby=Sell-by: %s printQty=Qty: %d AddDispatchBatchLine=Add a line for Shelf Life dispatching -BatchDefaultNumber=Undefined WhenProductBatchModuleOnOptionAreForced=When module Lot/Serial is on, increase/decrease stock mode is forced to last choice and can't be edited. Other options can be defined as you want. ProductDoesNotUseBatchSerial=This product does not use lot/serial number diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index e8b32f978cb..31aa35aba69 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -607,7 +607,8 @@ class Product extends CommonObject } /** - * Update a record into database + * Update a record into database. + * If batch flag is set to on, we create records into llx_product_batch * * @param int $id Id of product * @param User $user Object user making update @@ -672,25 +673,46 @@ class Product extends CommonObject $org->fetch($this->id); $this->oldcopy=$org; } - // test if batch management is activated on existing product + + // Test if batch management is activated on existing product + // If yes, we create missing entries into product_batch if ($this->hasbatch() && !$this->oldcopy->hasbatch()) { + //$valueforundefinedlot = 'Undefined'; // In previous version, 39 and lower + $valueforundefinedlot = '000000'; + + dol_syslog("Flag batch of product id=".$this->id." is set to ON, so we will create missing records into product_batch"); + $this->load_stock(); - foreach ($this->stock_warehouse as $idW => $ObjW) + foreach ($this->stock_warehouse as $idW => $ObjW) // For each warehouse where we have stocks defined for this product (for each lines in product_stock) { $qty_batch = 0; - foreach ($ObjW->detail_batch as $detail) + foreach ($ObjW->detail_batch as $detail) // Each lines of detail in product_batch of the current $ObjW = product_stock { + if ($detail->batch == $valueforundefinedlot || $detail->batch == 'Undefined') + { + // We discard this line, we will create it later + $sqlclean="DELETE FROM ".MAIN_DB_PREFIX."product_batch WHERE batch in('Undefined', '".$valueforundefinedlot."') AND fk_product_stock = ".$ObjW->id; + $result = $this->db->query($sqlclean); + if (! $result) + { + dol_print_error($this->db); + exit; + } + continue; + } + $qty_batch += $detail->qty; } - // Quantities in batch details are not same same as stock quantity - // So we add a default batch record + // Quantities in batch details are not same as stock quantity, + // so we add a default batch record to complete and get same qty in parent and child table if ($ObjW->real <> $qty_batch) { $ObjBatch = new Productbatch($this->db); - $ObjBatch->batch = $langs->trans('BatchDefaultNumber'); - $ObjBatch->qty = $ObjW->real - $qty_batch; + $ObjBatch->batch = $valueforundefinedlot; + $ObjBatch->qty = ($ObjW->real - $qty_batch); $ObjBatch->fk_product_stock = $ObjW->id; + if ($ObjBatch->create($user,1) < 0) { $error++; @@ -699,6 +721,7 @@ class Product extends CommonObject } } } + // For automatic creation if ($this->barcode == -1) $this->barcode = $this->get_barcode($this,$this->barcode_type_code); @@ -744,6 +767,7 @@ class Product extends CommonObject $sql.= ", price_autogen = " . (!$this->price_autogen ? 0 : 1); $sql.= ", fk_price_expression = ".($this->fk_price_expression != 0 ? $this->fk_price_expression : 'NULL'); $sql.= ", fk_user_modif = ".($user->id > 0 ? $user->id : 'NULL'); + // stock field is not here because it is a denormalized value from product_stock. $sql.= " WHERE rowid = " . $id; dol_syslog(get_class($this)."::update", LOG_DEBUG); @@ -1597,7 +1621,7 @@ class Product extends CommonObject * @param int $ignore_expression Ignores the math expression for calculating price and uses the db value instead * @return int <0 if KO, 0 if not found, >0 if OK */ - function fetch($id='', $ref='', $ref_ext='', $ignore_expression = 0) + function fetch($id='', $ref='', $ref_ext='', $ignore_expression=0) { include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php index d4440dac0f3..ed03c3e729e 100644 --- a/htdocs/product/class/productbatch.class.php +++ b/htdocs/product/class/productbatch.class.php @@ -40,7 +40,7 @@ class Productbatch extends CommonObject var $batch=''; var $qty; public $warehouseid; - + public $fk_product; @@ -75,7 +75,7 @@ class Productbatch extends CommonObject // Put here code to add control on parameters values // Insert request - $sql = "INSERT INTO ".MAIN_DB_PREFIX.self::$_table_element." ("; + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_batch ("; $sql.= "fk_product_stock,"; $sql.= "sellby,"; $sql.= "eatby,"; @@ -143,15 +143,18 @@ class Productbatch extends CommonObject $sql.= " t.tms,"; $sql.= " t.fk_product_stock,"; - $sql.= " t.sellby,"; - $sql.= " t.eatby,"; + $sql.= " t.sellby as oldsellby,"; + $sql.= " t.eatby as oldeatby,"; $sql.= " t.batch,"; $sql.= " t.qty,"; $sql.= " t.import_key,"; - $sql.= " w.fk_entrepot"; + $sql.= " w.fk_entrepot,"; + $sql.= " w.fk_product,"; + $sql.= " pl.eatby,"; + $sql.= " pl.sellby"; - $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t"; - $sql.= " INNER JOIN ".MAIN_DB_PREFIX."product_stock w on t.fk_product_stock=w.rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t INNER JOIN ".MAIN_DB_PREFIX."product_stock w on t.fk_product_stock = w.rowid"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl on pl.fk_product = w.fk_product and pl.batch = t.batch"; $sql.= " WHERE t.rowid = ".$id; dol_syslog(get_class($this)."::fetch", LOG_DEBUG); @@ -165,12 +168,13 @@ class Productbatch extends CommonObject $this->id = $obj->rowid; $this->tms = $this->db->jdate($obj->tms); $this->fk_product_stock = $obj->fk_product_stock; - $this->sellby = $this->db->jdate($obj->sellby); - $this->eatby = $this->db->jdate($obj->eatby); + $this->sellby = $this->db->jdate($obj->sellby?$obj->sellby:$obj->oldsellby); + $this->eatby = $this->db->jdate($obj->eatby?$obj->eatby:$obj->oldeatby); $this->batch = $obj->batch; $this->qty = $obj->qty; $this->import_key = $obj->import_key; $this->warehouseid= $obj->fk_entrepot; + $this->fk_product= $obj->fk_product; } $this->db->free($resql); @@ -408,9 +412,9 @@ class Productbatch extends CommonObject * Find first detail record that match eather eat-by or sell-by or batch within given warehouse * * @param int $fk_product_stock id product_stock for objet - * @param date $eatby eat-by date for objet - deprecated: a search must be done on batch number - * @param date $sellby sell-by date for objet - deprecated: a search must be done on batch number - * @param string $batch_number batch number for objet + * @param date $eatby eat-by date for object - deprecated: a search must be done on batch number + * @param date $sellby sell-by date for object - deprecated: a search must be done on batch number + * @param string $batch_number batch number for object * @return int <0 if KO, >0 if OK */ function find($fk_product_stock=0, $eatby='',$sellby='',$batch_number='') @@ -421,8 +425,8 @@ class Productbatch extends CommonObject $sql.= " t.rowid,"; $sql.= " t.tms,"; $sql.= " t.fk_product_stock,"; - $sql.= " t.sellby,"; - $sql.= " t.eatby,"; + $sql.= " t.sellby,"; // deprecated + $sql.= " t.eatby,"; // deprecated $sql.= " t.batch,"; $sql.= " t.qty,"; $sql.= " t.import_key"; @@ -431,6 +435,7 @@ class Productbatch extends CommonObject if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'"); // deprecated if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'"); // deprecated + if (! empty($batch_number)) $sql.= " AND batch = '".$this->db->escape($batch_number)."'"; if (! empty($where)) $sql.= " AND (".implode(" OR ",$where).")"; @@ -468,10 +473,11 @@ class Productbatch extends CommonObject * * @param DoliDB $db database object * @param int $fk_product_stock id product_stock for objet - * @param int $with_qty doesn't return line with 0 quantity + * @param int $with_qty 1 = doesn't return line with 0 quantity + * @param int $fk_product If set to a product id, get eatby and sellby from table llx_product_lot * @return array <0 if KO, array of batch */ - public static function findAll($db,$fk_product_stock,$with_qty=0) + public static function findAll($db, $fk_product_stock, $with_qty=0, $fk_product=0) { global $langs; $ret = array(); @@ -480,14 +486,22 @@ class Productbatch extends CommonObject $sql.= " t.rowid,"; $sql.= " t.tms,"; $sql.= " t.fk_product_stock,"; - $sql.= " t.sellby,"; - $sql.= " t.eatby,"; + $sql.= " t.sellby as oldsellby,"; // deprecated but may not be migrated into new table + $sql.= " t.eatby as oldeatby,"; // deprecated but may not be migrated into new table $sql.= " t.batch,"; $sql.= " t.qty,"; $sql.= " t.import_key"; + if ($fk_product > 0) + { + $sql.= ", pl.eatby as eatby, pl.sellby as sellby"; + } $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t"; + if ($fk_product > 0) + { + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON pl.fk_product = ".$fk_product." AND pl.batch = t.batch"; + } $sql.= " WHERE fk_product_stock=".$fk_product_stock; - if ($with_qty) $sql.= " AND qty<>0"; + if ($with_qty) $sql.= " AND t.qty <> 0"; dol_syslog("productbatch::findAll", LOG_DEBUG); $resql=$db->query($sql); @@ -499,12 +513,12 @@ class Productbatch extends CommonObject { $obj = $db->fetch_object($resql); - $tmp = new productbatch($db); + $tmp = new Productbatch($db); $tmp->id = $obj->rowid; $tmp->tms = $db->jdate($obj->tms); $tmp->fk_product_stock = $obj->fk_product_stock; - $tmp->sellby = $db->jdate($obj->sellby); - $tmp->eatby = $db->jdate($obj->eatby); + $tmp->sellby = $db->jdate($obj->sellby ? $obj->sellby : $obj->oldsellby); + $tmp->eatby = $db->jdate($obj->eatby ? $obj->eatby : $obj->oldeatby); $tmp->batch = $obj->batch; $tmp->qty = $obj->qty; $tmp->import_key = $obj->import_key; diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index b23a1cbf25c..f7d5e60ca72 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -105,15 +105,17 @@ $htmlother=new FormOther($db); $title=$langs->trans("ProductsAndServices"); -$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,p.entity,'; +$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,'; $sql.= ' p.fk_product_type, p.tms as datem,'; -$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock,'; +$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tobatch,'; $sql.= ' s.fk_entrepot,'; -$sql.= ' pb.batch, pb.eatby, pb.sellby,'; -$sql.= ' SUM(pb.qty) as stock_physique'; +$sql.= ' pb.batch, pb.eatby as oldeatby, pb.sellby as oldsellby,'; +$sql.= ' pl.eatby, pl.sellby,'; +$sql.= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable'; $sql.= ' FROM '.MAIN_DB_PREFIX.'product as p'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s on p.rowid = s.fk_product'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = s.rowid'; +$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch'; // We'll need this table joined to the select in order to filter by categ if ($search_categ) $sql.= ", ".MAIN_DB_PREFIX."categorie_product as cp"; $sql.= " WHERE p.entity IN (".getEntity('product', 1).")"; @@ -166,10 +168,12 @@ if ($search_batch) { $sql .= " AND pb.batch LIKE '%".$db->escape($search_batch)."%'"; } -$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,"; -$sql.= " p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock,"; +$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,"; +$sql.= " p.fk_product_type, p.tms,"; +$sql.= " p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tobatch,"; $sql.= " s.fk_entrepot,"; -$sql.= " pb.batch, pb.eatby, pb.sellby"; +$sql.= " pb.batch, pb.eatby, pb.sellby,"; +$sql.= " pl.eatby, pl.sellby"; if ($toolowstock) $sql.= " HAVING SUM(".$db->ifsql('s.reel IS NULL', '0', 's.reel').") < p.seuil_stock_alerte"; // Not used yet $sql.= $db->order($sortfield,$sortorder); $sql.= $db->plimit($limit + 1, $offset); @@ -364,7 +368,7 @@ if ($resql) //if ($objp->seuil_stock_alerte && ($objp->stock_physique < $objp->seuil_stock_alerte)) print img_warning($langs->trans("StockTooLow")).' '; print $objp->stock_physique; print ''; - print ''.$langs->trans("Movements").''; + print ''.$langs->trans("Movements").''; print ''.$product_static->LibStatut($objp->statut,5,0).''; print ''.$product_static->LibStatut($objp->tobuy,5,1).''; print "\n"; diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 9b6db71be9e..b9859f9ec9b 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -117,7 +117,6 @@ class MouvementStock extends CommonObject // Test if product require batch data. If yes, and there is not, we throw an error. if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch) { - //if (empty($batch) && empty($eatby) && empty($sellby)) if (empty($batch)) { $this->errors[]=$langs->trans("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->name); @@ -130,8 +129,9 @@ class MouvementStock extends CommonObject // FIXME Code not complete to implement this // Check table llx_product_lot from batchnumber for same product // If found and eatby/sellby defined into table and provided and differs, return error - // If found and eatby/sellby not defined into table and provided, we update table // If found and eatby/sellby defined into table and not provided, we take value from table + // If found and eatby/sellby not defined into table and provided, we update table + // If found and eatby/sellby not defined into table and not provided, we do nothing // If not found, we add record $sql = "SELECT pb.rowid, pb.batch, pb.eatby, pb.sellby FROM ".MAIN_DB_PREFIX."product_lot as pb"; $sql.= " WHERE pb.fk_product = ".$fk_product." AND pb.batch = '".$this->db->escape($batch)."'"; @@ -158,6 +158,27 @@ class MouvementStock extends CommonObject return -3; } } + else + { + $eatby = $obj->eatby; // If found and eatby/sellby defined into table and not provided, we take value from table + } + } + else + { + if ($eatby) // If found and eatby/sellby not defined into table and provided, we update table + { + $productlot = new Productlot($this->db); + $result = $productlot->fetch($obj->rowid); + $productlot->eatby = $eatby; + $result = $productlot->update($user); + if ($result <= 0) + { + $this->error = $productlot->error; + $this->errors = $productlot->errors; + $this->db->rollback(); + return -5; + } + } } if ($obj->sellby) { @@ -171,8 +192,30 @@ class MouvementStock extends CommonObject return -3; } } + else + { + $sellby = $obj->sellby; // If found and eatby/sellby defined into table and not provided, we take value from table + } } - $i++; + else + { + if ($sellby) // If found and eatby/sellby not defined into table and provided, we update table + { + $productlot = new Productlot($this->db); + $result = $productlot->fetch($obj->rowid); + $productlot->sellby = $sellby; + $result = $productlot->update($user); + if ($result <= 0) + { + $this->error = $productlot->error; + $this->errors = $productlot->errors; + $this->db->rollback(); + return -5; + } + } + } + + $i++; } } else @@ -181,7 +224,7 @@ class MouvementStock extends CommonObject $productlot->fk_product = $fk_product; $productlot->batch = $batch; $result = $productlot->create($user); - if (! $result) + if ($result <= 0) { $this->error = $productlot->error; $this->errors = $productlot->errors; @@ -367,7 +410,7 @@ class MouvementStock extends CommonObject $sql = "UPDATE ".MAIN_DB_PREFIX."product as p SET p.pmp = ".$newpmp.", "; $sql.= " stock=(SELECT SUM(ps.reel) FROM ".MAIN_DB_PREFIX."product_stock ps WHERE ps.fk_product = p.rowid)"; $sql.= " WHERE rowid = ".$fk_product; - print $sql; + dol_syslog(get_class($this)."::_create", LOG_DEBUG); $resql=$this->db->query($sql); if (! $resql) diff --git a/htdocs/product/stock/class/productlot.class.php b/htdocs/product/stock/class/productlot.class.php index 26970a68e85..9b15ed31fcc 100644 --- a/htdocs/product/stock/class/productlot.class.php +++ b/htdocs/product/stock/class/productlot.class.php @@ -56,14 +56,15 @@ class Productlot extends CommonObject /** */ - public $tms = ''; + public $entity; public $fk_product; public $batch; public $eatby = ''; public $sellby = ''; - public $note_public; - public $note_private; - public $qty; + public $datec = ''; + public $tms = ''; + public $fk_user_creat; + public $fk_user_modif; public $import_key; /** @@ -96,20 +97,20 @@ class Productlot extends CommonObject // Clean parameters + if (isset($this->entity)) { + $this->entity = trim($this->entity); + } if (isset($this->fk_product)) { $this->fk_product = trim($this->fk_product); } if (isset($this->batch)) { $this->batch = trim($this->batch); } - if (isset($this->note_public)) { - $this->note_public = trim($this->note_public); + if (isset($this->fk_user_creat)) { + $this->fk_user_creat = trim($this->fk_user_creat); } - if (isset($this->note_private)) { - $this->note_private = trim($this->note_private); - } - if (isset($this->qty)) { - $this->qty = trim($this->qty); + if (isset($this->fk_user_modif)) { + $this->fk_user_modif = trim($this->fk_user_modif); } if (isset($this->import_key)) { $this->import_key = trim($this->import_key); @@ -123,26 +124,28 @@ class Productlot extends CommonObject // Insert request $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '('; + $sql.= 'entity,'; $sql.= 'fk_product,'; $sql.= 'batch,'; $sql.= 'eatby,'; $sql.= 'sellby,'; - $sql.= 'note_public,'; - $sql.= 'note_private,'; - $sql.= 'qty'; + $sql.= 'datec,'; + $sql.= 'fk_user_creat,'; + $sql.= 'fk_user_modif,'; $sql.= 'import_key'; $sql .= ') VALUES ('; + $sql .= ' '.(! isset($this->entity)?'NULL':$this->entity).','; $sql .= ' '.(! isset($this->fk_product)?'NULL':$this->fk_product).','; $sql .= ' '.(! isset($this->batch)?'NULL':"'".$this->db->escape($this->batch)."'").','; $sql .= ' '.(! isset($this->eatby) || dol_strlen($this->eatby)==0?'NULL':"'".$this->db->idate($this->eatby)."'").','; $sql .= ' '.(! isset($this->sellby) || dol_strlen($this->sellby)==0?'NULL':"'".$this->db->idate($this->sellby)."'").','; - $sql .= ' '.(! isset($this->note_public)?'NULL':"'".$this->db->escape($this->note_public)."'").','; - $sql .= ' '.(! isset($this->note_private)?'NULL':"'".$this->db->escape($this->note_private)."'").','; - $sql .= ' '.(! isset($this->qty)?'NULL':"'".$this->qty."'").','; - $sql .= ' '.(! isset($this->import_key)?'NULL':"'".$this->db->escape($this->import_key)."'"); + $sql .= ' '."'".$this->db->idate(dol_now())."'".','; + $sql .= ' '.(! isset($this->fk_user_creat)?'NULL':$this->fk_user_creat).','; + $sql .= ' '.(! isset($this->fk_user_modif)?'NULL':$this->fk_user_modif).','; + $sql .= ' '.(! isset($this->import_key)?'NULL':$this->import_key); $sql .= ')'; @@ -197,14 +200,15 @@ class Productlot extends CommonObject $sql = 'SELECT'; $sql .= ' t.rowid,'; - $sql .= " t.tms,"; + $sql .= " t.entity,"; $sql .= " t.fk_product,"; $sql .= " t.batch,"; $sql .= " t.eatby,"; $sql .= " t.sellby,"; - $sql .= " t.note_public,"; - $sql .= " t.note_private,"; - $sql .= " t.qty,"; + $sql .= " t.datec,"; + $sql .= " t.tms,"; + $sql .= " t.fk_user_creat,"; + $sql .= " t.fk_user_modif,"; $sql .= " t.import_key"; @@ -223,14 +227,15 @@ class Productlot extends CommonObject $this->id = $obj->rowid; - $this->tms = $this->db->jdate($obj->tms); + $this->entity = $obj->entity; $this->fk_product = $obj->fk_product; $this->batch = $obj->batch; $this->eatby = $this->db->jdate($obj->eatby); $this->sellby = $this->db->jdate($obj->sellby); - $this->note_public = $obj->note_public; - $this->note_private = $obj->note_private; - $this->qty = $obj->qty; + $this->datec = $this->db->jdate($obj->datec); + $this->tms = $this->db->jdate($obj->tms); + $this->fk_user_creat = $obj->fk_user_creat; + $this->fk_user_modif = $obj->fk_user_modif; $this->import_key = $obj->import_key; @@ -269,14 +274,15 @@ class Productlot extends CommonObject $sql = 'SELECT'; $sql .= ' t.rowid,'; - $sql .= " t.tms,"; + $sql .= " t.entity,"; $sql .= " t.fk_product,"; $sql .= " t.batch,"; $sql .= " t.eatby,"; $sql .= " t.sellby,"; - $sql .= " t.note_public,"; - $sql .= " t.note_private,"; - $sql .= " t.qty,"; + $sql .= " t.datec,"; + $sql .= " t.tms,"; + $sql .= " t.fk_user_creat,"; + $sql .= " t.fk_user_modif,"; $sql .= " t.import_key"; @@ -310,14 +316,15 @@ class Productlot extends CommonObject $line->id = $obj->rowid; - $line->tms = $this->db->jdate($obj->tms); + $line->entity = $obj->entity; $line->fk_product = $obj->fk_product; $line->batch = $obj->batch; $line->eatby = $this->db->jdate($obj->eatby); $line->sellby = $this->db->jdate($obj->sellby); - $line->note_public = $obj->note_public; - $line->note_private = $obj->note_private; - $line->qty = $obj->qty; + $line->datec = $this->db->jdate($obj->datec); + $line->tms = $this->db->jdate($obj->tms); + $line->fk_user_creat = $obj->fk_user_creat; + $line->fk_user_modif = $obj->fk_user_modif; $line->import_key = $obj->import_key; @@ -351,20 +358,20 @@ class Productlot extends CommonObject // Clean parameters + if (isset($this->entity)) { + $this->entity = trim($this->entity); + } if (isset($this->fk_product)) { $this->fk_product = trim($this->fk_product); } if (isset($this->batch)) { $this->batch = trim($this->batch); } - if (isset($this->note_public)) { - $this->note_public = trim($this->note_public); + if (isset($this->fk_user_creat)) { + $this->fk_user_creat = trim($this->fk_user_creat); } - if (isset($this->note_private)) { - $this->note_private = trim($this->note_private); - } - if (isset($this->qty)) { - $this->qty = trim($this->qty); + if (isset($this->fk_user_modif)) { + $this->fk_user_modif = trim($this->fk_user_modif); } if (isset($this->import_key)) { $this->import_key = trim($this->import_key); @@ -378,15 +385,16 @@ class Productlot extends CommonObject // Update request $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET'; - $sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'").','; + $sql .= ' entity = '.(isset($this->entity)?$this->entity:"null").','; $sql .= ' fk_product = '.(isset($this->fk_product)?$this->fk_product:"null").','; $sql .= ' batch = '.(isset($this->batch)?"'".$this->db->escape($this->batch)."'":"null").','; $sql .= ' eatby = '.(! isset($this->eatby) || dol_strlen($this->eatby) != 0 ? "'".$this->db->idate($this->eatby)."'" : 'null').','; $sql .= ' sellby = '.(! isset($this->sellby) || dol_strlen($this->sellby) != 0 ? "'".$this->db->idate($this->sellby)."'" : 'null').','; - $sql .= ' note_public = '.(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").','; - $sql .= ' note_private = '.(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").','; - $sql .= ' qty = '.(isset($this->qty)?$this->qty:"null").','; - $sql .= ' import_key = '.(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null"); + $sql .= ' datec = '.(! isset($this->datec) || dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').','; + $sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'").','; + $sql .= ' fk_user_creat = '.(isset($this->fk_user_creat)?$this->fk_user_creat:"null").','; + $sql .= ' fk_user_modif = '.(isset($this->fk_user_modif)?$this->fk_user_modif:"null").','; + $sql .= ' import_key = '.(isset($this->import_key)?$this->import_key:"null"); $sql .= ' WHERE rowid=' . $this->id; @@ -626,14 +634,15 @@ class Productlot extends CommonObject { $this->id = 0; - $this->tms = ''; + $this->entity = ''; $this->fk_product = ''; $this->batch = ''; $this->eatby = ''; $this->sellby = ''; - $this->note_public = ''; - $this->note_private = ''; - $this->qty = ''; + $this->datec = ''; + $this->tms = ''; + $this->fk_user_creat = ''; + $this->fk_user_modif = ''; $this->import_key = ''; @@ -654,14 +663,15 @@ class ProductlotLine * @var mixed Sample line property 1 */ - public $tms = ''; + public $entity; public $fk_product; public $batch; public $eatby = ''; public $sellby = ''; - public $note_public; - public $note_private; - public $qty; + public $datec = ''; + public $tms = ''; + public $fk_user_creat; + public $fk_user_modif; public $import_key; /** diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index 29ee33c56eb..b18ca146a25 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -367,7 +367,7 @@ if (! empty($search_movement)) $sql.= " AND m.label LIKE '%".$db->escape($s if (! empty($search_inventorycode)) $sql.= " AND m.inventorycode LIKE '%".$db->escape($search_inventorycode)."%'"; if (! empty($search_product_ref)) $sql.= " AND p.ref LIKE '%".$db->escape($search_product_ref)."%'"; if (! empty($search_product)) $sql.= " AND p.label LIKE '%".$db->escape($search_product)."%'"; -if (! empty($search_warehouse)) $sql.= " AND e.label LIKE '%".$db->escape($search_warehouse)."%'"; +if (! empty($search_warehouse)) $sql.= " AND e.rowid = '".$db->escape($search_warehouse)."'"; if (! empty($search_user)) $sql.= " AND u.login LIKE '%".$db->escape($search_user)."%'"; if (! empty($search_batch)) $sql.= " AND m.batch LIKE '%".$db->escape($search_batch)."%'"; @@ -702,7 +702,8 @@ if ($resql) if (! $id > 0) { print ''; - print ''; + //print ''; + print $formproduct->selectWarehouses($search_warehouse, 'search_warehouse', '', 1); print ''; } // Author diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 499efd29847..1b4c159b1d6 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -273,6 +273,10 @@ if ($action == "transfert_stock" && ! $cancel) $eatby,$sellby,$batch, GETPOST('inventorycode') ); + if ($result1 < 0) $error++; + } + if (! $error) + { // Add stock $result2=$object->correct_stock_batch( $user, @@ -284,31 +288,39 @@ if ($action == "transfert_stock" && ! $cancel) $eatby,$sellby,$batch, GETPOST('inventorycode') ); + if ($result2 < 0) $error++; } } else { - // Remove stock - $result1=$object->correct_stock( - $user, - GETPOST("id_entrepot"), - GETPOST("nbpiece"), - 1, - GETPOST("label"), - $pricesrc, - GETPOST('inventorycode') - ); - - // Add stock - $result2=$object->correct_stock( - $user, - GETPOST("id_entrepot_destination"), - GETPOST("nbpiece"), - 0, - GETPOST("label"), - $pricedest, - GETPOST('inventorycode') - ); + if (! $error) + { + // Remove stock + $result1=$object->correct_stock( + $user, + GETPOST("id_entrepot"), + GETPOST("nbpiece"), + 1, + GETPOST("label"), + $pricesrc, + GETPOST('inventorycode') + ); + if ($result1 < 0) $error++; + } + if (! $error) + { + // Add stock + $result2=$object->correct_stock( + $user, + GETPOST("id_entrepot_destination"), + GETPOST("nbpiece"), + 0, + GETPOST("label"), + $pricedest, + GETPOST('inventorycode') + ); + if ($result2 < 0) $error++; + } } if (! $error && $result1 >= 0 && $result2 >= 0) { @@ -589,7 +601,7 @@ if ($id > 0 || $ref) if ($action == "correction") { include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stockcorrection.tpl.php'; - print '
    '; + print '

    '; } /* @@ -598,7 +610,7 @@ if ($id > 0 || $ref) if ($action == "transfert") { include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stocktransfer.tpl.php'; - print '
    '; + print '

    '; } /* @@ -657,10 +669,10 @@ if (empty($action) && $object->id) /* - * Stock detail (by warehouse). Do not go down into batch. + * Stock detail (by warehouse). May go down into batch details. */ -print '
    '; +print '
    '; print ''; print ''; print ''; @@ -726,14 +738,14 @@ if ($resql) if (price2num($object->pmp)) $totalwithpmp += $obj->reel; $totalvalue = $totalvalue + ($object->pmp*$obj->reel); $totalvaluesell = $totalvaluesell + ($object->price*$obj->reel); - //Batch Detail + // Batch Detail if ((! empty($conf->productbatch->enabled)) && $object->hasbatch()) { - $details=Productbatch::findAll($db,$obj->product_stock_id); + $details=Productbatch::findAll($db, $obj->product_stock_id, 0, $object->id); if ($details<0) dol_print_error($db); foreach ($details as $pdluo) { - if ( $action == 'editline' && GETPOST('lineid','int')==$pdluo->id ) + if ($action == 'editline' && GETPOST('lineid','int') == $pdluo->id) { //Current line edit print "\n".''; +if (! empty($arrayfields['t.entity']['checked'])) print ''; if (! empty($arrayfields['t.fk_product']['checked'])) print ''; if (! empty($arrayfields['t.batch']['checked'])) print ''; -if (! empty($arrayfields['t.note_public']['checked'])) print ''; -if (! empty($arrayfields['t.note_private']['checked'])) print ''; -if (! empty($arrayfields['t.qty']['checked'])) print ''; +if (! empty($arrayfields['t.fk_user_creat']['checked'])) print ''; +if (! empty($arrayfields['t.fk_user_modif']['checked'])) print ''; if (! empty($arrayfields['t.import_key']['checked'])) print ''; diff --git a/htdocs/product/stock/tpl/stocktransfer.tpl.php b/htdocs/product/stock/tpl/stocktransfer.tpl.php index a50fafca278..824eda16d30 100644 --- a/htdocs/product/stock/tpl/stocktransfer.tpl.php +++ b/htdocs/product/stock/tpl/stocktransfer.tpl.php @@ -88,7 +88,16 @@ { print ''; print 'element == 'stock'?'': ' class="fieldrequired"').'>'.$langs->trans("batch_number").''; print ''; print ''; if ($conf->productbatch->enabled) { print ''; } // In warehouse @@ -390,7 +390,7 @@ print ''; // Qty -print ''; +print ''; // Button to add line print ''; diff --git a/htdocs/product/stock/replenish.php b/htdocs/product/stock/replenish.php index 7af08ee6575..21f492e0c00 100644 --- a/htdocs/product/stock/replenish.php +++ b/htdocs/product/stock/replenish.php @@ -75,13 +75,15 @@ if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global { $virtualdiffersfromphysical=1; // According to increase/decrease stock options, virtual and physical stock may differs. } +$usevirtualstock=0; +if ($mode == 'virtual') $usevirtualstock=1; /* * Actions */ -if (isset($_POST['button_removefilter']) || isset($_POST['valid'])) +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter") || isset($_POST['valid'])) // Both test are required to be compatible with all browsers { $sref = ''; $snom = ''; From 7bbb3a7da10e024d78ad4911bf6f7a730f720ebe Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:15:44 +0200 Subject: [PATCH 704/834] Supplier proposal was missing --- htdocs/langs/en_US/other.lang | 2 ++ htdocs/product/class/product.class.php | 37 ++++++++++++++++++++++++++ htdocs/product/stats/card.php | 23 ++++++++++------ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 278fa9c77b7..329ab278329 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -157,11 +157,13 @@ StatsByNumberOfEntities=Statistics in number of referring entities NumberOfProposals=Number of proposals on last 12 month NumberOfCustomerOrders=Number of customer orders on last 12 month NumberOfCustomerInvoices=Number of customer invoices on last 12 month +NumberOfSupplierProposals=Number of supplier proposals on last 12 month NumberOfSupplierOrders=Number of supplier orders on last 12 month NumberOfSupplierInvoices=Number of supplier invoices on last 12 month NumberOfUnitsProposals=Number of units on proposals on last 12 month NumberOfUnitsCustomerOrders=Number of units on customer orders on last 12 month NumberOfUnitsCustomerInvoices=Number of units on customer invoices on last 12 month +NumberOfUnitsSupplierProposals=Number of units on supplier proposals on last 12 month NumberOfUnitsSupplierOrders=Number of units on supplier orders on last 12 month NumberOfUnitsSupplierInvoices=Number of units on supplier invoices on last 12 month EMailTextInterventionValidated=The intervention %s has been validated. diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index fce31e31923..ba2eac00cd9 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2280,6 +2280,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE f.rowid = d.fk_facture"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND f.fk_soc = s.rowid"; $sql.= " AND f.entity IN (".getEntity('facture', 1).")"; @@ -2312,6 +2313,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE f.rowid = d.fk_facture_fourn"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND f.fk_soc = s.rowid"; $sql.= " AND f.entity IN (".getEntity('facture_fourn', 1).")"; @@ -2344,6 +2346,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE p.rowid = d.fk_propal"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; $sql.= " AND p.fk_soc = s.rowid"; $sql.= " AND p.entity IN (".getEntity('propal', 1).")"; @@ -2355,6 +2358,38 @@ class Product extends CommonObject return $this->_get_stats($sql,$mode); } + /** + * Return nb of units or proposals in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_propalsupplier($socid, $mode, $filteronproducttype=-1) + { + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(p.date_valid, '%Y%m')"; + if ($mode == 'bynumber') $sql.= ", count(DISTINCT p.rowid)"; + $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as prod"; + if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + $sql.= " WHERE p.rowid = d.fk_supplier_proposal"; + if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; + if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('propal', 1).")"; + if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; + $sql.= " GROUP BY date_format(p.date_valid,'%Y%m')"; + $sql.= " ORDER BY date_format(p.date_valid,'%Y%m') DESC"; + + return $this->_get_stats($sql,$mode); + } + /** * Return nb of units or orders in which product is included * @@ -2374,6 +2409,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE c.rowid = d.fk_commande"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND c.fk_soc = s.rowid"; $sql.= " AND c.entity IN (".getEntity('commande', 1).")"; @@ -2404,6 +2440,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE c.rowid = d.fk_commande"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND c.fk_soc = s.rowid"; $sql.= " AND c.entity IN (".getEntity('commande_fournisseur', 1).")"; diff --git a/htdocs/product/stats/card.php b/htdocs/product/stats/card.php index 49b700b46ca..d98d8ed31c6 100644 --- a/htdocs/product/stats/card.php +++ b/htdocs/product/stats/card.php @@ -210,15 +210,20 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') 'propal' =>array('modulepart'=>'productstats_proposals', 'file' => $object->id.'/propal12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsProposals"):$langs->transnoentitiesnoconv("NumberOfProposals"))), + 'proposalssuppliers'=>array('modulepart'=>'productstats_proposalssuppliers', + 'file' => $object->id.'/proposalssuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', + 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierProposals"):$langs->transnoentitiesnoconv("NumberOfSupplierProposals"))), + 'orders' =>array('modulepart'=>'productstats_orders', 'file' => $object->id.'/orders12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerOrders"):$langs->transnoentitiesnoconv("NumberOfCustomerOrders"))), - 'invoices' =>array('modulepart'=>'productstats_invoices', - 'file' => $object->id.'/invoices12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', - 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerInvoices"):$langs->transnoentitiesnoconv("NumberOfCustomerInvoices"))), 'orderssuppliers'=>array('modulepart'=>'productstats_orderssuppliers', 'file' => $object->id.'/orderssuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierOrders"):$langs->transnoentitiesnoconv("NumberOfSupplierOrders"))), + + 'invoices' =>array('modulepart'=>'productstats_invoices', + 'file' => $object->id.'/invoices12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', + 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerInvoices"):$langs->transnoentitiesnoconv("NumberOfCustomerInvoices"))), 'invoicessuppliers'=>array('modulepart'=>'productstats_invoicessuppliers', 'file' => $object->id.'/invoicessuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierInvoices"):$langs->transnoentitiesnoconv("NumberOfSupplierInvoices"))), @@ -243,11 +248,12 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') } else { - if ($key == 'propal') $graph_data = $object->get_nb_propal($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'orders') $graph_data = $object->get_nb_order($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'invoices') $graph_data = $object->get_nb_vente($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'invoicessuppliers') $graph_data = $object->get_nb_achat($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'orderssuppliers') $graph_data = $object->get_nb_ordersupplier($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'propal') $graph_data = $object->get_nb_propal($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'orders') $graph_data = $object->get_nb_order($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'invoices') $graph_data = $object->get_nb_vente($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'proposalssuppliers') $graph_data = $object->get_nb_propalsupplier($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'invoicessuppliers') $graph_data = $object->get_nb_achat($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'orderssuppliers') $graph_data = $object->get_nb_ordersupplier($socid,$mode,((string) $type != '' ? $type : -1)); // TODO Save cachefile $graphfiles[$key]['file'] } @@ -289,6 +295,7 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') if ($graphfiles == 'propal' && ! $user->rights->propale->lire) continue; if ($graphfiles == 'order' && ! $user->rights->commande->lire) continue; if ($graphfiles == 'invoices' && ! $user->rights->facture->lire) continue; + if ($graphfiles == 'proposals_suppliers' && ! $user->rights->supplier_proposal->lire) continue; if ($graphfiles == 'invoices_suppliers' && ! $user->rights->fournisseur->facture->lire) continue; if ($graphfiles == 'orders_suppliers' && ! $user->rights->fournisseur->commande->lire) continue; From 47f003c3fae5d899e8752e192f3d69d921ca97e3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:40:58 +0200 Subject: [PATCH 705/834] Restore phpunit tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 74d951bf7bf..0f14e0a49ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -291,7 +291,7 @@ script: echo "Unit testing" # Ensure we catch errors. Set this to +e if you want to go to the end to see log file. set -e - #phpunit -d memory_limit=-1 -c test/phpunit/phpunittest.xml test/phpunit/AllTests.php + phpunit -d memory_limit=-1 -c test/phpunit/phpunittest.xml test/phpunit/AllTests.php set +e - | From 86076c2a0198c154e1508b4c313bab047bce31a4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:49:37 +0200 Subject: [PATCH 706/834] Fix doxygen --- htdocs/core/menus/standard/auguria.lib.php | 4 ++-- htdocs/core/menus/standard/eldy.lib.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/menus/standard/auguria.lib.php b/htdocs/core/menus/standard/auguria.lib.php index cbd4b035c4e..5a853baad51 100644 --- a/htdocs/core/menus/standard/auguria.lib.php +++ b/htdocs/core/menus/standard/auguria.lib.php @@ -31,8 +31,8 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php'; * @param DoliDB $db Database handler * @param string $atarget Target * @param int $type_user 0=Menu for backoffice, 1=Menu for front office - * @param array $tabMenu If array with menu entries already loaded, we put this array here (in most cases, it's empty) - * @param array $menu Object Menu to return back list of menu entries + * @param array $tabMenu If array with menu entries already loaded, we put this array here (in most cases, it's empty) + * @param Menu $menu Object Menu to return back list of menu entries * @param int $noout Disable output (Initialise &$menu only). * @return int 0 */ diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 1ce08a25bba..7164f52393f 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -34,7 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php'; * @param string $atarget Target (Example: '' or '_top') * @param int $type_user 0=Menu for backoffice, 1=Menu for front office * @param array $tabMenu If array with menu entries already loaded, we put this array here (in most cases, it's empty) - * @param array $menu Object Menu to return back list of menu entries + * @param Menu $menu Object Menu to return back list of menu entries * @param int $noout 1=Disable output (Initialise &$menu only). * @return int 0 */ From 92e7177bcb1537e135574505ca7f0410ecc96fed Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:58:29 +0200 Subject: [PATCH 707/834] Fix New into new --- htdocs/admin/dict.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 797b35f7c75..7045695c1c2 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -1553,7 +1553,7 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='') $formadmin = new FormAdmin($db); $formcompany = new FormCompany($db); - if (! empty($conf->accounting->enabled)) $formaccountancy = New FormVentilation($db); + if (! empty($conf->accounting->enabled)) $formaccountancy = new FormVentilation($db); foreach ($fieldlist as $field => $value) { From a36b780c53a3a1891f4af6aded32cdbead02c52d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 20:30:04 +0200 Subject: [PATCH 708/834] Fix phpunit --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 4 ++-- htdocs/install/mysql/tables/llx_c_accounting_category.sql | 4 ++-- test/phpunit/SqlTest.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index 136a7114eb0..f996ca3677a 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -364,8 +364,8 @@ CREATE TABLE llx_c_accounting_category ( code varchar(16) NOT NULL, label varchar(255) NOT NULL, range_account varchar(255) NOT NULL, - sens tinyint(1) NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit - category_type tinyint(1) NOT NULL DEFAULT '0', -- Field calculated or not + sens tinyint NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit + category_type tinyint NOT NULL DEFAULT '0', -- Field calculated or not formula varchar(255) NOT NULL, -- Example : 1 + 2 (rowid of the category) position integer DEFAULT 0, fk_country integer DEFAULT NULL, -- This category is dedicated to a country diff --git a/htdocs/install/mysql/tables/llx_c_accounting_category.sql b/htdocs/install/mysql/tables/llx_c_accounting_category.sql index a5ca2a0352b..683478aceef 100644 --- a/htdocs/install/mysql/tables/llx_c_accounting_category.sql +++ b/htdocs/install/mysql/tables/llx_c_accounting_category.sql @@ -23,8 +23,8 @@ CREATE TABLE llx_c_accounting_category ( code varchar(16) NOT NULL, label varchar(255) NOT NULL, range_account varchar(255) NOT NULL, - sens tinyint(1) NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit - category_type tinyint(1) NOT NULL DEFAULT '0', -- Field calculated or not + sens tinyint NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit + category_type tinyint NOT NULL DEFAULT '0', -- Field calculated or not formula varchar(255) NOT NULL, -- Example : 1 + 2 (rowid of the category) position integer DEFAULT 0, fk_country integer DEFAULT NULL, -- This category is dedicated to a country diff --git a/test/phpunit/SqlTest.php b/test/phpunit/SqlTest.php index 887d2aa89bb..0a9acc50220 100644 --- a/test/phpunit/SqlTest.php +++ b/test/phpunit/SqlTest.php @@ -162,7 +162,7 @@ class SqlTest extends PHPUnit_Framework_TestCase $result=strpos($filecontent,'int('); print __METHOD__." Result for checking we don't have 'int(' instead of 'integer' = ".$result."\n"; - $this->assertTrue($result===false, 'Found int(x) instead of integer into '.$file.'. Bad.'); + $this->assertTrue($result===false, 'Found int(x) or tinyint(x) instead of integer or tinyint into '.$file.'. Bad.'); $result=strpos($filecontent,'ON DELETE CASCADE'); print __METHOD__." Result for checking we don't have 'ON DELETE CASCADE' = ".$result."\n"; From 89d37ccc62cbfd56cf9620e14f1fcc791df8102d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 21:55:39 +0200 Subject: [PATCH 709/834] Fix translation --- htdocs/langs/en_US/main.lang | 1 + htdocs/product/card.php | 2 +- htdocs/product/class/product.class.php | 3 ++- htdocs/projet/class/project.class.php | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 75023bf9184..b92c0db4d49 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -137,6 +137,7 @@ Disabled=Disabled Add=Add AddLink=Add link RemoveLink=Remove link +AddToDraft=Add to draft Update=Update AddActionToDo=Add event to do AddActionDone=Add event done diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 5fdfa1f2337..ca6e6b14544 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1876,7 +1876,7 @@ if ($object->id && ($action == '' || $action == 'view') && $object->status) print ''; print ''; - print load_fiche_titre($langs->trans("Add"),'',''); + print load_fiche_titre($langs->trans("AddToDraft"),'',''); dol_fiche_head(''); diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 32a474d924b..98684d5f980 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -829,7 +829,8 @@ class Product extends CommonObject $res = @rename($olddir, $newdir); if (! $res) { - $this->error='ErrorFailToMoveDir'; + $langs->load("errors"); + $this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir); $error++; } } diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 8e699e8ee91..1de8027dd0e 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -297,7 +297,8 @@ class Project extends CommonObject $res=dol_move($olddir, $newdir); if (! $res) { - $this->error='ErrorFailToMoveDir'; + $langs->load("errors"); + $this->error=$langs->trans('ErrorFailToRenameDir',$olddir,$newdir); $error++; } } From 4e46a01d513bf00650e5b1ac0823dafeade30873 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 22:59:46 +0200 Subject: [PATCH 710/834] Fix regression introduced with option SUPPLIERORDER_WITH_NOPRICEDEFINED --- .../class/fournisseur.commande.class.php | 64 ++++--- .../fourn/class/fournisseur.product.class.php | 174 +++++++++--------- htdocs/product/class/product.class.php | 17 +- test/phpunit/CommandeFournisseurTest.php | 2 +- 4 files changed, 127 insertions(+), 130 deletions(-) diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index cb7d0ce6e6f..f8c57d7cc94 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -1126,7 +1126,7 @@ class CommandeFournisseur extends CommonOrder // insert products details into database for ($i=0;$i<$num;$i++) { - $result = $this->addline( + $result = $this->addline( // This include test on qty if option SUPPLIERORDER_WITH_NOPRICEDEFINED is not set $this->lines[$i]->desc, $this->lines[$i]->subprice, $this->lines[$i]->qty, @@ -1306,11 +1306,11 @@ class CommandeFournisseur extends CommonOrder */ function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $fk_prod_fourn_price=0, $fourn_ref='', $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $type=0, $info_bits=0, $notrigger=false, $date_start=null, $date_end=null, $array_options=0, $fk_unit=null) { - global $langs,$mysoc, $conf; + global $langs,$mysoc,$conf; $error = 0; - dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2. $fk_product, $fk_prod_fourn_price, $fourn_ref, $remise_percent, $price_base_type, $pu_ttc, $type, $fk_unit"); + dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $fk_prod_fourn_price, $fourn_ref, $remise_percent, $price_base_type, $pu_ttc, $type, $fk_unit"); include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; // Clean parameters @@ -1350,40 +1350,45 @@ class CommandeFournisseur extends CommonOrder { $this->db->begin(); - if ($fk_prod_fourn_price > 0) + if ($fk_product > 0) { - $prod = new Product($this->db, $fk_product); - if ($prod->fetch($fk_product) > 0) + if (empty($conf->global->SUPPLIERORDER_WITH_NOPRICEDEFINED)) { - $result=$prod->get_buyprice($fk_prod_fourn_price,$qty,$fk_product,$fourn_ref); - if ($result > 0) + // Check quantity is enough + dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_prod_fourn_price=".$fk_prod_fourn_price." qty=".$qty." fourn_ref=".$fourn_ref); + $prod = new Product($this->db, $fk_product); + if ($prod->fetch($fk_product) > 0) { - $label = $prod->libelle; - $pu = $prod->fourn_pu; - $ref = $prod->ref_fourn; - $product_type = $prod->type; + $result=$prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, $fourn_ref); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$fourn_ref + if ($result > 0) + { + $label = $prod->libelle; + $pu = $prod->fourn_pu; + $ref = $prod->ref_fourn; + $product_type = $prod->type; + } + if ($result == 0 || $result == -1) + { + $langs->load("errors"); + $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier"); + $this->db->rollback(); + dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG); + return -1; + } + if ($result < -1) + { + $this->error=$prod->error; + $this->db->rollback(); + dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR); + return -1; + } } - if ($result == 0 || $result == -1) - { - $langs->load("errors"); - $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier"); - $this->db->rollback(); - dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG); - return -1; - } - if ($result < -1) - { + else + { $this->error=$prod->error; - $this->db->rollback(); - dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR); return -1; } } - else - { - $this->error=$prod->error; - return -1; - } } else { @@ -1449,7 +1454,6 @@ class CommandeFournisseur extends CommonOrder $sql.= ", ".$multicurrency_total_ttc; $sql.= ")"; - dol_syslog(get_class($this)."::addline", LOG_DEBUG); $resql=$this->db->query($sql); //print $sql; if ($resql) diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index 48896689dce..05024f9991a 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -216,7 +216,7 @@ class ProductFournisseur extends Product $this->db->begin(); - if ($this->product_fourn_price_id) + if ($this->product_fourn_price_id > 0) { $sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price"; $sql.= " SET fk_user = " . $user->id." ,"; @@ -237,7 +237,7 @@ class ProductFournisseur extends Product $sql.= " WHERE rowid = ".$this->product_fourn_price_id; // TODO Add price_base_type and price_ttc - dol_syslog(get_class($this).'::update_buyprice', LOG_DEBUG); + dol_syslog(get_class($this).'::update_buyprice update knowing id of line = product_fourn_price_id = '.$this->product_fourn_price_id, LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { @@ -249,7 +249,7 @@ class ProductFournisseur extends Product if (empty($error)) { $this->db->commit(); - return 0; + return $this->product_fourn_price_id; } else { @@ -267,94 +267,88 @@ class ProductFournisseur extends Product else { - // Delete price for this quantity - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; - $sql.= " WHERE fk_soc = ".$fourn->id." AND ref_fourn = '".$this->db->escape($ref_fourn)."' AND quantity = ".$qty." AND entity = ".$conf->entity; - dol_syslog(get_class($this).'::update_buyprice', LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - // Add price for this quantity to supplier - $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_fournisseur_price("; - $sql.= "datec, fk_product, fk_soc, ref_fourn, fk_user, price, quantity, remise_percent, remise, unitprice, tva_tx, charges, unitcharges, fk_availability, info_bits, entity, delivery_time_days,supplier_reputation)"; - $sql.= " values('".$this->db->idate($now)."',"; - $sql.= " ".$this->id.","; - $sql.= " ".$fourn->id.","; - $sql.= " '".$this->db->escape($ref_fourn)."',"; - $sql.= " ".$user->id.","; - $sql.= " ".$buyprice.","; - $sql.= " ".$qty.","; - $sql.= " ".$remise_percent.","; - $sql.= " ".$remise.","; - $sql.= " ".$unitBuyPrice.","; - $sql.= " ".$tva_tx.","; - $sql.= " ".$charges.","; - $sql.= " ".$unitCharges.","; - $sql.= " ".$availability.","; - $sql.= " ".$newnpr.","; - $sql.= $conf->entity.","; - $sql.= $delivery_time_days.","; - $sql.= (empty($supplier_reputation) ? 'NULL' : "'".$this->db->escape($supplier_reputation)."'"); - $sql.=")"; - - dol_syslog(get_class($this)."::update_buyprice", LOG_DEBUG); - if (! $this->db->query($sql)) - { - $error++; - } - - if (! $error && !empty($conf->global->PRODUCT_PRICE_SUPPLIER_NO_LOG)) - { - // Add record into log table - $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_fournisseur_price_log("; - $sql.= "datec, fk_product_fournisseur,fk_user,price,quantity)"; - $sql.= "values('".$this->db->idate($now)."',"; - $sql.= " ".$this->product_fourn_id.","; - $sql.= " ".$user->id.","; - $sql.= " ".price2num($buyprice).","; - $sql.= " ".$qty; - $sql.=")"; - - $resql=$this->db->query($sql); - if (! $resql) - { - $error++; - } - } - - - if (! $error) - { - // Call trigger - $result=$this->call_trigger('SUPPLIER_PRODUCT_BUYPRICE_CREATE',$user); - if ($result < 0) $error++; + dol_syslog(get_class($this) . '::update_buyprice without knowing id of line, so we delete from company, quantity and supplier_ref and insert again', LOG_DEBUG); + + // Delete price for this quantity + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "product_fournisseur_price"; + $sql .= " WHERE fk_soc = " . $fourn->id . " AND ref_fourn = '" . $this->db->escape($ref_fourn) . "' AND quantity = " . $qty . " AND entity = " . $conf->entity; + $resql = $this->db->query($sql); + if ($resql) { + // Add price for this quantity to supplier + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "product_fournisseur_price("; + $sql .= "datec, fk_product, fk_soc, ref_fourn, fk_user, price, quantity, remise_percent, remise, unitprice, tva_tx, charges, unitcharges, fk_availability, info_bits, entity, delivery_time_days,supplier_reputation)"; + $sql .= " values('" . $this->db->idate($now) . "',"; + $sql .= " " . $this->id . ","; + $sql .= " " . $fourn->id . ","; + $sql .= " '" . $this->db->escape($ref_fourn) . "',"; + $sql .= " " . $user->id . ","; + $sql .= " " . $buyprice . ","; + $sql .= " " . $qty . ","; + $sql .= " " . $remise_percent . ","; + $sql .= " " . $remise . ","; + $sql .= " " . $unitBuyPrice . ","; + $sql .= " " . $tva_tx . ","; + $sql .= " " . $charges . ","; + $sql .= " " . $unitCharges . ","; + $sql .= " " . $availability . ","; + $sql .= " " . $newnpr . ","; + $sql .= $conf->entity . ","; + $sql .= $delivery_time_days . ","; + $sql .= (empty($supplier_reputation) ? 'NULL' : "'" . $this->db->escape($supplier_reputation) . "'"); + $sql .= ")"; + + $idinserted = 0; + + $resql = $this->db->query($sql); + if ($resql) { + $idinserted = $this->db->last_insert_id(MAIN_DB_PREFIX . "product_fournisseur_price"); + } + else { + $error++; + } + + if (! $error && ! empty($conf->global->PRODUCT_PRICE_SUPPLIER_NO_LOG)) { + // Add record into log table + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "product_fournisseur_price_log("; + $sql .= "datec, fk_product_fournisseur,fk_user,price,quantity)"; + $sql .= "values('" . $this->db->idate($now) . "',"; + $sql .= " " . $this->product_fourn_id . ","; + $sql .= " " . $user->id . ","; + $sql .= " " . price2num($buyprice) . ","; + $sql .= " " . $qty; + $sql .= ")"; + + $resql = $this->db->query($sql); + if (! $resql) { + $error++; + } + } + + if (! $error) { + // Call trigger + $result = $this->call_trigger('SUPPLIER_PRODUCT_BUYPRICE_CREATE', $user); + if ($result < 0) + $error++; // End call triggers - - if (empty($error)) - { - $this->db->commit(); - return 0; - } - else - { - $this->db->rollback(); - return -1; - } - } - else - { - $this->error=$this->db->error()." sql=".$sql; - $this->db->rollback(); - return -2; - } - } - else - { - $this->error=$this->db->error()." sql=".$sql; - $this->db->rollback(); - return -1; - } - } + + if (empty($error)) { + $this->db->commit(); + return $idinserted; + } else { + $this->db->rollback(); + return -1; + } + } else { + $this->error = $this->db->lasterror() . " sql=" . $sql; + $this->db->rollback(); + return -2; + } + } else { + $this->error = $this->db->lasterror() . " sql=" . $sql; + $this->db->rollback(); + return - 1; + } + } } /** diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 98684d5f980..d242a343ce9 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -1315,21 +1315,21 @@ class Product extends CommonObject * @param double $qty Quantity asked * @param int $product_id Filter on a particular product id * @param string $fourn_ref Filter on a supplier ref - * @return int <-1 if KO, -1 if qty not enough, 0 si ok mais rien trouve, id_product si ok et trouve. May also initialize some properties like (->ref_supplier, buyprice, fourn_pu, vatrate_supplier...) + * @return int <-1 if KO, -1 if qty not enough, 0 if OK but nothing found, id_product if OK and found. May also initialize some properties like (->ref_supplier, buyprice, fourn_pu, vatrate_supplier...) */ function get_buyprice($prodfournprice,$qty,$product_id=0,$fourn_ref=0) { global $conf; $result = 0; - // We do select by searching with qty and prodfournprice + // We do a first seach with a select by searching with couple prodfournprice and qty only (later we will search on triplet qty/product_id/fourn_ref) $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity,"; $sql.= " pfp.fk_product, pfp.ref_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_supplier_price_expression"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " WHERE pfp.rowid = ".$prodfournprice; if ($qty) $sql.= " AND pfp.quantity <= ".$qty; - dol_syslog(get_class($this)."::get_buyprice", LOG_DEBUG); + dol_syslog(get_class($this)."::get_buyprice first search by prodfournprice/qty", LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { @@ -1358,9 +1358,9 @@ class Product extends CommonObject $result=$obj->fk_product; return $result; } - else + else // If not found { - // We do same select again but searching with qty, ref and id product + // We do a second search by doing a select again but searching with qty, ref and id product $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.fk_soc,"; $sql.= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.tva_tx, pfp.fk_supplier_price_expression"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; @@ -1370,7 +1370,7 @@ class Product extends CommonObject $sql.= " ORDER BY pfp.quantity DESC"; $sql.= " LIMIT 1"; - dol_syslog(get_class($this)."::get_buyprice", LOG_DEBUG); + dol_syslog(get_class($this)."::get_buyprice second search from qty/ref/product_id", LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { @@ -2656,6 +2656,8 @@ class Product extends CommonObject $now=dol_now(); + dol_syslog(get_class($this)."::add_fournisseur id_fourn = ".$id_fourn." ref_fourn=".$ref_fourn." quantity=".$quantity, LOG_DEBUG); + if ($ref_fourn) { $sql = "SELECT rowid, fk_product"; @@ -2665,7 +2667,6 @@ class Product extends CommonObject $sql.= " AND fk_product != ".$this->id; $sql.= " AND entity = ".$conf->entity; - dol_syslog(get_class($this)."::add_fournisseur", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) { @@ -2689,7 +2690,6 @@ class Product extends CommonObject $sql.= " AND fk_product = ".$this->id; $sql.= " AND entity = ".$conf->entity; - dol_syslog(get_class($this)."::add_fournisseur", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) { @@ -2718,7 +2718,6 @@ class Product extends CommonObject $sql.= ", 0"; $sql.= ")"; - dol_syslog(get_class($this)."::add_fournisseur", LOG_DEBUG); if ($this->db->query($sql)) { $this->product_fourn_price_id = $this->db->last_insert_id(MAIN_DB_PREFIX."product_fournisseur_price"); diff --git a/test/phpunit/CommandeFournisseurTest.php b/test/phpunit/CommandeFournisseurTest.php index 67c120a2bb8..03880a56eca 100644 --- a/test/phpunit/CommandeFournisseurTest.php +++ b/test/phpunit/CommandeFournisseurTest.php @@ -145,7 +145,7 @@ class CommandeFournisseurTest extends PHPUnit_Framework_TestCase // Create supplier price $result=$product->add_fournisseur($user, $societe->id, $ref_fourn, $quantity); // This insert record with no value for price. Values are update later with update_buyprice $this->assertGreaterThanOrEqual(1, $result); - $result=$product->update_buyprice($quantity, 10, $user, 'HT', $societe, '', $ref_fourn, $tva_tx, 0, 0); + $result=$product->update_buyprice($quantity, 20, $user, 'HT', $societe, '', $ref_fourn, $tva_tx, 0, 0); $this->assertGreaterThanOrEqual(0, $result); // Create supplier order with a too low quantity From 753c5c7fc4fbb3b0e798e0265461d8ce217cc86f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 23:14:30 +0200 Subject: [PATCH 711/834] Fix minor error management --- htdocs/core/db/mssql.class.php | 23 ++++++----- htdocs/core/db/mysql.class.php | 8 +++- htdocs/core/db/mysqli.class.php | 8 +++- htdocs/core/db/pgsql.class.php | 67 +++++++++++++++++++-------------- 4 files changed, 65 insertions(+), 41 deletions(-) diff --git a/htdocs/core/db/mssql.class.php b/htdocs/core/db/mssql.class.php index 0be65dfe2e6..2df3e341452 100644 --- a/htdocs/core/db/mssql.class.php +++ b/htdocs/core/db/mssql.class.php @@ -192,8 +192,12 @@ class DoliDBMssql extends DoliDB function getVersion() { $resql=$this->query("SELECT @@VERSION"); - $version=$this->fetch_array($resql); - return $version['computed']; + if ($resql) + { + $version=$this->fetch_array($resql); + return $version['computed']; + } + else return ''; } /** @@ -203,10 +207,7 @@ class DoliDBMssql extends DoliDB */ function getDriverInfo() { - // FIXME: Dummy method - // TODO: Implement - - return ''; + return 'php mssql driver'; } /** @@ -648,7 +649,7 @@ class DoliDBMssql extends DoliDB function last_insert_id($tab,$fieldid='rowid') { $res = $this->query("SELECT @@IDENTITY as id"); - if ($data = $this->fetch_array($res)) + if ($res && $data = $this->fetch_array($res)) { return $data["id"]; } @@ -709,8 +710,12 @@ class DoliDBMssql extends DoliDB function DDLGetConnectId() { $resql=$this->query('SELECT CONNECTION_ID()'); - $row=$this->fetch_row($resql); - return $row[0]; + if ($resql) + { + $row=$this->fetch_row($resql); + return $row[0]; + } + else return '?'; } /** diff --git a/htdocs/core/db/mysql.class.php b/htdocs/core/db/mysql.class.php index 34a7ebdf9f5..a92bb09e0b3 100644 --- a/htdocs/core/db/mysql.class.php +++ b/htdocs/core/db/mysql.class.php @@ -549,8 +549,12 @@ class DoliDBMysql extends DoliDB function DDLGetConnectId() { $resql=$this->query('SELECT CONNECTION_ID()'); - $row=$this->fetch_row($resql); - return $row[0]; + if ($resql) + { + $row=$this->fetch_row($resql); + return $row[0]; + } + else return '?'; } diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index d21caecea36..b6df8420490 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -532,8 +532,12 @@ class DoliDBMysqli extends DoliDB function DDLGetConnectId() { $resql=$this->query('SELECT CONNECTION_ID()'); - $row=$this->fetch_row($resql); - return $row[0]; + if ($resql) + { + $row=$this->fetch_row($resql); + return $row[0]; + } + else return '?'; } /** diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 048ef2d450d..c8b72ebcdd9 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -874,31 +874,34 @@ class DoliDBPgsql extends DoliDB */ function DDLInfoTable($table) { - $infotables=array(); + $infotables=array(); - $sql="SELECT "; - $sql.=" infcol.column_name as \"Column\","; - $sql.=" CASE WHEN infcol.character_maximum_length IS NOT NULL THEN infcol.udt_name || '('||infcol.character_maximum_length||')'"; - $sql.=" ELSE infcol.udt_name"; - $sql.=" END as \"Type\","; - $sql.=" infcol.collation_name as \"Collation\","; - $sql.=" infcol.is_nullable as \"Null\","; - $sql.=" '' as \"Key\","; - $sql.=" infcol.column_default as \"Default\","; - $sql.=" '' as \"Extra\","; - $sql.=" '' as \"Privileges\""; - $sql.=" FROM information_schema.columns infcol"; - $sql.=" WHERE table_schema='public' "; - $sql.=" AND table_name='".$table."'"; - $sql.=" ORDER BY ordinal_position;"; + $sql="SELECT "; + $sql.=" infcol.column_name as \"Column\","; + $sql.=" CASE WHEN infcol.character_maximum_length IS NOT NULL THEN infcol.udt_name || '('||infcol.character_maximum_length||')'"; + $sql.=" ELSE infcol.udt_name"; + $sql.=" END as \"Type\","; + $sql.=" infcol.collation_name as \"Collation\","; + $sql.=" infcol.is_nullable as \"Null\","; + $sql.=" '' as \"Key\","; + $sql.=" infcol.column_default as \"Default\","; + $sql.=" '' as \"Extra\","; + $sql.=" '' as \"Privileges\""; + $sql.=" FROM information_schema.columns infcol"; + $sql.=" WHERE table_schema='public' "; + $sql.=" AND table_name='".$table."'"; + $sql.=" ORDER BY ordinal_position;"; - dol_syslog($sql,LOG_DEBUG); - $result = $this->query($sql); - while($row = $this->fetch_row($result)) - { - $infotables[] = $row; - } - return $infotables; + dol_syslog($sql,LOG_DEBUG); + $result = $this->query($sql); + if ($result) + { + while($row = $this->fetch_row($result)) + { + $infotables[] = $row; + } + } + return $infotables; } @@ -1111,8 +1114,12 @@ class DoliDBPgsql extends DoliDB function getDefaultCharacterSetDatabase() { $resql=$this->query('SHOW SERVER_ENCODING'); - $liste=$this->fetch_array($resql); - return $liste['server_encoding']; + if ($resql) + { + $liste=$this->fetch_array($resql); + return $liste['server_encoding']; + } + else return ''; } /** @@ -1127,7 +1134,7 @@ class DoliDBPgsql extends DoliDB if ($resql) { $i = 0; - while ($obj = $this->fetch_object($resql) ) + while ($obj = $this->fetch_object($resql)) { $liste[$i]['charset'] = $obj->server_encoding; $liste[$i]['description'] = 'Default database charset'; @@ -1148,8 +1155,12 @@ class DoliDBPgsql extends DoliDB function getDefaultCollationDatabase() { $resql=$this->query('SHOW LC_COLLATE'); - $liste=$this->fetch_array($resql); - return $liste['lc_collate']; + if ($resql) + { + $liste=$this->fetch_array($resql); + return $liste['lc_collate']; + } + else return ''; } /** From c8a8474b69664c2481a416b4a25f55b5e6c9af18 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 23:19:57 +0200 Subject: [PATCH 712/834] Better practice in boolean comparison --- htdocs/commande/index.php | 4 ++-- htdocs/contrat/index.php | 4 ++-- htdocs/core/db/mssql.class.php | 2 +- htdocs/fichinter/index.php | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/htdocs/commande/index.php b/htdocs/commande/index.php index fa1b2064ed2..5731535697e 100644 --- a/htdocs/commande/index.php +++ b/htdocs/commande/index.php @@ -123,7 +123,7 @@ if ($resql) foreach ($listofstatus as $status) { $dataseries[]=array('label'=>$commandestatic->LibStatut($status,$bool,1),'data'=>(isset($vals[$status.$bool])?(int) $vals[$status.$bool]:0)); - if ($status==3 && $bool==false) $bool=true; + if ($status==3 && ! $bool) $bool=true; else $bool=false; } if ($conf->use_javascript_ajax) @@ -147,7 +147,7 @@ if ($resql) print ''; print ''; print "\n"; - if ($status==3 && $bool==false) $bool=true; + if ($status==3 && ! $bool) $bool=true; else $bool=false; } } diff --git a/htdocs/contrat/index.php b/htdocs/contrat/index.php index 18a60a3429d..250f1c98199 100644 --- a/htdocs/contrat/index.php +++ b/htdocs/contrat/index.php @@ -190,7 +190,7 @@ foreach($listofstatus as $status) print ''; print "\n"; } - if ($status==4 && $bool==false) $bool=true; + if ($status==4 && ! $bool) $bool=true; else $bool=false; } if (! empty($conf->use_javascript_ajax)) @@ -210,7 +210,7 @@ foreach($listofstatus as $status) print ''; print ''; print ''; - if ($status==4 && $bool==false) $bool=true; + if ($status==4 && ! $bool) $bool=true; else $bool=false; print "\n"; } diff --git a/htdocs/core/db/mssql.class.php b/htdocs/core/db/mssql.class.php index 2df3e341452..97c72160d76 100644 --- a/htdocs/core/db/mssql.class.php +++ b/htdocs/core/db/mssql.class.php @@ -360,7 +360,7 @@ class DoliDBMssql extends DoliDB $query_comp[]=$fld->COLUMN_NAME." IS NOT NULL"; } } - if ($query_comp) + if (! empty($query_comp)) $query.=" WHERE ".implode(" AND ",$query_comp); } } diff --git a/htdocs/fichinter/index.php b/htdocs/fichinter/index.php index 3e629e05789..fc24d8c8326 100644 --- a/htdocs/fichinter/index.php +++ b/htdocs/fichinter/index.php @@ -120,7 +120,7 @@ if ($resql) foreach ($listofstatus as $status) { $dataseries[]=array('label'=>$fichinterstatic->LibStatut($status,$bool,1),'data'=>(isset($vals[$status.$bool])?(int) $vals[$status.$bool]:0)); - if ($status==3 && $bool==false) $bool=true; + if ($status==3 && ! $bool) $bool=true; else $bool=false; } if ($conf->use_javascript_ajax) @@ -144,7 +144,7 @@ if ($resql) print ''; print ''; print "\n"; - if ($status==3 && $bool==false) $bool=true; + if ($status==3 && ! $bool) $bool=true; else $bool=false; } } From acae888b3ab27dc7577896db36af45a5c3d86c03 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 23:38:07 +0200 Subject: [PATCH 713/834] Fix phpunits --- .../facture/class/facture-rec.class.php | 114 +++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/htdocs/compta/facture/class/facture-rec.class.php b/htdocs/compta/facture/class/facture-rec.class.php index cddd511dbe4..13c10a5da0c 100644 --- a/htdocs/compta/facture/class/facture-rec.class.php +++ b/htdocs/compta/facture/class/facture-rec.class.php @@ -760,8 +760,120 @@ class FactureRec extends CommonInvoice $arraynow=dol_getdate($now); $nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']); - parent::initAsSpecimen($option); + $prodids = array(); + $sql = "SELECT rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."product"; + $sql.= " WHERE entity IN (".getEntity('product', 1).")"; + $resql = $this->db->query($sql); + if ($resql) + { + $num_prods = $this->db->num_rows($resql); + $i = 0; + while ($i < $num_prods) + { + $i++; + $row = $this->db->fetch_row($resql); + $prodids[$i] = $row[0]; + } + } + // Initialize parameters + $this->id=0; + $this->ref = 'SPECIMEN'; + $this->specimen=1; + $this->socid = 1; + $this->date = $nownotime; + $this->date_lim_reglement = $nownotime + 3600 * 24 *30; + $this->cond_reglement_id = 1; + $this->cond_reglement_code = 'RECEP'; + $this->date_lim_reglement=$this->calculate_date_lim_reglement(); + $this->mode_reglement_id = 0; // Not forced to show payment mode CHQ + VIR + $this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR + $this->note_public='This is a comment (public)'; + $this->note_private='This is a comment (private)'; + $this->note='This is a comment (private)'; + $this->fk_incoterms=0; + $this->location_incoterms=''; + + if (empty($option) || $option != 'nolines') + { + // Lines + $nbp = 5; + $xnbp = 0; + while ($xnbp < $nbp) + { + $line=new FactureLigne($this->db); + $line->desc=$langs->trans("Description")." ".$xnbp; + $line->qty=1; + $line->subprice=100; + $line->tva_tx=19.6; + $line->localtax1_tx=0; + $line->localtax2_tx=0; + $line->remise_percent=0; + if ($xnbp == 1) // Qty is negative (product line) + { + $prodid = mt_rand(1, $num_prods); + $line->fk_product=$prodids[$prodid]; + $line->qty=-1; + $line->total_ht=-100; + $line->total_ttc=-119.6; + $line->total_tva=-19.6; + } + else if ($xnbp == 2) // UP is negative (free line) + { + $line->subprice=-100; + $line->total_ht=-100; + $line->total_ttc=-119.6; + $line->total_tva=-19.6; + $line->remise_percent=0; + } + else if ($xnbp == 3) // Discount is 50% (product line) + { + $prodid = mt_rand(1, $num_prods); + $line->fk_product=$prodids[$prodid]; + $line->total_ht=50; + $line->total_ttc=59.8; + $line->total_tva=9.8; + $line->remise_percent=50; + } + else // (product line) + { + $prodid = mt_rand(1, $num_prods); + $line->fk_product=$prodids[$prodid]; + $line->total_ht=100; + $line->total_ttc=119.6; + $line->total_tva=19.6; + $line->remise_percent=00; + } + + $this->lines[$xnbp]=$line; + $xnbp++; + + $this->total_ht += $line->total_ht; + $this->total_tva += $line->total_tva; + $this->total_ttc += $line->total_ttc; + } + $this->revenuestamp = 0; + + // Add a line "offered" + $line=new FactureLigne($this->db); + $line->desc=$langs->trans("Description")." (offered line)"; + $line->qty=1; + $line->subprice=100; + $line->tva_tx=19.6; + $line->localtax1_tx=0; + $line->localtax2_tx=0; + $line->remise_percent=100; + $line->total_ht=0; + $line->total_ttc=0; // 90 * 1.196 + $line->total_tva=0; + $prodid = mt_rand(1, $num_prods); + $line->fk_product=$prodids[$prodid]; + + $this->lines[$xnbp]=$line; + $xnbp++; + } + $this->usenewprice = 1; } From cd75b43d581000e29bbf04f382679d3f15b8e343 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 00:44:55 +0200 Subject: [PATCH 714/834] Can enable modules during migration --- htdocs/install/upgrade2.php | 41 ++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index 9ee3d842e51..d3eb23e99b6 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -63,6 +63,7 @@ $setuplang=GETPOST("selectlang",'',3)?GETPOST("selectlang",'',3):'auto'; $langs->setDefaultLang($setuplang); $versionfrom=GETPOST("versionfrom",'',3)?GETPOST("versionfrom",'',3):(empty($argv[1])?'':$argv[1]); $versionto=GETPOST("versionto",'',3)?GETPOST("versionto",'',3):(empty($argv[2])?'':$argv[2]); +$enablemodules=GETPOST("enablemodules",'',3)?GETPOST("enablemodules",'',3):(empty($argv[3])?'':$argv[3]); $langs->load('admin'); $langs->load('install'); @@ -84,9 +85,9 @@ if (! is_object($conf)) dolibarr_install_syslog("upgrade2: conf file not initial * View */ -if (! $versionfrom && ! $versionto) +if ((! $versionfrom || preg_match('/version/', $versionfrom)) && (! $versionto || preg_match('/version/', $versionto))) { - print 'Error: Parameter versionfrom or versionto missing.'."\n"; + print 'Error: Parameter versionfrom or versionto missing or having a bad format.'."\n"; print 'Upgrade must be ran from cmmand line with parameters or called from page install/index.php (like a first install) instead of page install/upgrade.php'."\n"; // Test if batch mode $sapi_type = php_sapi_name(); @@ -416,6 +417,21 @@ if (! GETPOST("action") || preg_match('/upgrade/i',GETPOST('action'))) migrate_reload_menu($db,$langs,$conf,$versionto); } + + // Can force activation of some module during migration with third paramater = MAIN_MODULE_XXX,MAIN_MODULE_YYY,... + if ($enablemodules) + { + // Reload modules (this must be always and only into last targeted version) + $listofmodules=array(); + $tmplistofmodules=explode(',', $enablemodules); + foreach($tmplistofmodules as $value) + { + $listofmodules[$value]='newboxdefonly'; + } + migrate_reload_modules($db,$langs,$conf,$listofmodules,1); + } + + print ''; // On commit dans tous les cas. @@ -3776,11 +3792,12 @@ function migrate_delete_old_dir($db,$langs,$conf) * @param Translate $langs Object langs * @param Conf $conf Object conf * @param array $listofmodule List of modules + * @param int $force 1=Reload module even if not already loaded * @return void */ -function migrate_reload_modules($db,$langs,$conf,$listofmodule=array()) +function migrate_reload_modules($db,$langs,$conf,$listofmodule=array(),$force=0) { - dolibarr_install_syslog("upgrade2::migrate_reload_modules"); + dolibarr_install_syslog("upgrade2::migrate_reload_modules force=".$force); // If no info is provided, we reload all modules with mode newboxdefonly. if (count($listofmodule) == 0) @@ -3805,7 +3822,7 @@ function migrate_reload_modules($db,$langs,$conf,$listofmodule=array()) foreach($listofmodule as $moduletoreload => $reloadmode) { - if (empty($moduletoreload) || empty($conf->global->$moduletoreload)) continue; + if (empty($moduletoreload) || (empty($conf->global->$moduletoreload) && ! $force)) continue; // Discard reload if module not enabled $mod=null; @@ -3819,7 +3836,17 @@ function migrate_reload_modules($db,$langs,$conf,$listofmodule=array()) $mod->init($reloadmode); } } - if ($moduletoreload == 'MAIN_MODULE_BARCODE') + if ($moduletoreload == 'MAIN_MODULE_API') + { + dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Rest API module"); + $res=@include_once DOL_DOCUMENT_ROOT.'/core/modules/modApi.class.php'; + if ($res) { + $mod=new modApi($db); + //$mod->remove('noboxes'); + $mod->init($reloadmode); + } + } + if ($moduletoreload == 'MAIN_MODULE_BARCODE') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Barcode module"); $res=@include_once DOL_DOCUMENT_ROOT.'/core/modules/modBarcode.class.php'; @@ -3974,7 +4001,7 @@ function migrate_reload_modules($db,$langs,$conf,$listofmodule=array()) { print ''; From e6196f69cdabd6173da1938ad85f340ae3dd9a06 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 00:45:13 +0200 Subject: [PATCH 715/834] Fix Restler bug returning warnings --- dev/dolibarr_changes.txt | 13 ++++++++++--- .../framework/Luracast/Restler/AutoLoader.php | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dev/dolibarr_changes.txt b/dev/dolibarr_changes.txt index a7aed128151..82f364b0c1f 100644 --- a/dev/dolibarr_changes.txt +++ b/dev/dolibarr_changes.txt @@ -116,8 +116,15 @@ JQUERYFILETREE: RESTLER: -------- - if ($className == 'Luracast\Restler\string') return; - if ($className == 'Luracast\Restler\mixed') return; - +* Add 2 lines into function + private function alias($className, $currentClass) + { + ... +to get + private function alias($className, $currentClass) + { + if ($className == 'Luracast\Restler\string') return; + if ($className == 'Luracast\Restler\mixed') return; + ... \ No newline at end of file diff --git a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php index 8f2ec3bf339..c2e454346ee 100644 --- a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php +++ b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php @@ -307,6 +307,9 @@ class AutoLoader */ private function alias($className, $currentClass) { + if ($className == 'Luracast\Restler\string') return; + if ($className == 'Luracast\Restler\mixed') return; + if ($className != $currentClass && false !== strpos($className, $currentClass)) if (!class_exists($currentClass, false) From 08549304f1ef9fdd84937661c9e26d0e5b52ed2c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 00:51:15 +0200 Subject: [PATCH 716/834] Enhance travis to have phpunit test on modules not available on old version by enabling them at end of migration. --- .travis.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f14e0a49ca..e5d0025d3ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -269,20 +269,20 @@ script: set +e cd htdocs/install php upgrade.php 3.5.0 3.6.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade350360.log - php upgrade2.php 3.5.0 3.6.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade350360-2.log - php step5.php 3.5.0 3.6.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade350360-3.log + php upgrade2.php 3.5.0 3.6.0 > $TRAVIS_BUILD_DIR/upgrade350360-2.log + php step5.php 3.5.0 3.6.0 > $TRAVIS_BUILD_DIR/upgrade350360-3.log php upgrade.php 3.6.0 3.7.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade360370.log - php upgrade2.php 3.6.0 3.7.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade360370-2.log - php step5.php 3.6.0 3.7.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade360370-3.log + php upgrade2.php 3.6.0 3.7.0 > $TRAVIS_BUILD_DIR/upgrade360370-2.log + php step5.php 3.6.0 3.7.0 > $TRAVIS_BUILD_DIR/upgrade360370-3.log php upgrade.php 3.7.0 3.8.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade370380.log - php upgrade2.php 3.7.0 3.8.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade370380-2.log - php step5.php 3.7.0 3.8.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade370380-3.log + php upgrade2.php 3.7.0 3.8.0 > $TRAVIS_BUILD_DIR/upgrade370380-2.log + php step5.php 3.7.0 3.8.0 > $TRAVIS_BUILD_DIR/upgrade370380-3.log php upgrade.php 3.8.0 3.9.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade380390.log - php upgrade2.php 3.8.0 3.9.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade380390-2.log - php step5.php 3.8.0 3.9.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade380390-3.log + php upgrade2.php 3.8.0 3.9.0 > $TRAVIS_BUILD_DIR/upgrade380390-2.log + php step5.php 3.8.0 3.9.0 > $TRAVIS_BUILD_DIR/upgrade380390-3.log php upgrade.php 3.9.0 4.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade390400.log - php upgrade2.php 3.9.0 4.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade390400-2.log - php step5.php 3.9.0 4.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade390400-3.log + php upgrade2.php 3.9.0 4.0.0 MAIN_MODULE_API > $TRAVIS_BUILD_DIR/upgrade390400-2.log + php step5.php 3.9.0 4.0.0 > $TRAVIS_BUILD_DIR/upgrade390400-3.log cd - set +e echo From f00414e4f7eaf7e0f67a52295b4eb4a1103c8e16 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 10:50:42 +0200 Subject: [PATCH 717/834] Show travis log --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e5d0025d3ea..4810502852f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -286,7 +286,9 @@ script: cd - set +e echo - + echo Log for upgrade2.php 3.9.0 4.0.0 MAIN_MODULE_API + cat $TRAVIS_BUILD_DIR/upgrade390400-2.log + - | echo "Unit testing" # Ensure we catch errors. Set this to +e if you want to go to the end to see log file. From ac361080fa299452f52f7ed769e9062cf8663089 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 10:53:27 +0200 Subject: [PATCH 718/834] Debug travis error --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4810502852f..2c2e34eb4b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -288,6 +288,7 @@ script: echo echo Log for upgrade2.php 3.9.0 4.0.0 MAIN_MODULE_API cat $TRAVIS_BUILD_DIR/upgrade390400-2.log + cat /tmp/dolibarr_install.log - | echo "Unit testing" From 236ecf01b640560f184a44ec78c384d3b1091fe8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 10:57:03 +0200 Subject: [PATCH 719/834] Help debug travis --- test/phpunit/RestAPIUserTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/phpunit/RestAPIUserTest.php b/test/phpunit/RestAPIUserTest.php index 466c3cab782..189c17a0bd3 100644 --- a/test/phpunit/RestAPIUserTest.php +++ b/test/phpunit/RestAPIUserTest.php @@ -114,7 +114,7 @@ class RestAPIUserTest extends PHPUnit_Framework_TestCase $url=$this->api_url.'/login?login='.$login.'&password='.$password; // Call the API login method to save api_key for this test class $result=getURLContent($url, 'GET', '', 1, array()); - //print __METHOD__." result = ".var_export($result, true)."\n"; + print __METHOD__." result = ".var_export($result, true)."\n"; print __METHOD__." curl_error_no: ".$result['curl_error_no']."\n"; $this->assertEquals($result['curl_error_no'],''); $object=json_decode($result['content'], true); From 9114513df25701bb314cc87ca84646e85c56a663 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 11:42:33 +0200 Subject: [PATCH 720/834] Revert "remove composer.lock" This reverts commit 7ec8557db9e83a3a0ea4c55d1aa0b327133bb9e1. --- .gitignore | 1 - composer.lock | 420 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index ca0334a63e9..1efe4f4bb82 100755 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ Thumbs.db # Vagrant generated files .vagrant # Composer installed repositories -composer.lock /htdocs/includes/**/.git # Composer autoloader and unwanted files htdocs/includes/autoload.php diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000000..43a9f1d2d7c --- /dev/null +++ b/composer.lock @@ -0,0 +1,420 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "c2b53c577364dbe3a56137043081b511", + "content-hash": "8f7a86cfbc13f45e13b73c49531818cb", + "packages": [ + { + "name": "ccampbell/chromephp", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/ccampbell/chromephp.git", + "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ccampbell/chromephp/zipball/c3c297615d48ae5b2a86a82311152d1ed095fcef", + "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "ChromePhp": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Craig Campbell", + "email": "iamcraigcampbell@gmail.com", + "homepage": "http://craig.is", + "role": "Developer" + } + ], + "description": "Log variables to the Chrome console (via Chrome Logger Google Chrome extension).", + "homepage": "http://github.com/ccampbell/chromephp", + "keywords": [ + "log", + "logging" + ], + "time": "2013-06-26 03:44:33" + }, + { + "name": "ckeditor/ckeditor", + "version": "dev-full/stable", + "source": { + "type": "git", + "url": "https://github.com/ckeditor/ckeditor-releases.git", + "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", + "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+", + "LGPL-2.1+", + "MPL-1.1+" + ], + "authors": [ + { + "name": "CKSource", + "homepage": "http://cksource.com" + } + ], + "description": "JavaScript WYSIWYG web text editor.", + "homepage": "http://ckeditor.com", + "keywords": [ + "CKEditor", + "editor", + "fckeditor", + "html", + "javascript", + "richtext", + "text", + "wysiwyg" + ], + "time": "2016-03-31 16:19:25" + }, + { + "name": "mike42/escpos-php", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/mike42/escpos-php.git", + "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mike42/escpos-php/zipball/96f05cbf460f5b67c2184ee4e91aedfbcedeb788", + "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mike42\\": "src/Mike42" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roni Saha", + "email": "roni.cse@gmail.com" + }, + { + "name": "Michael Billington", + "email": "michael.billington@gmail.com" + }, + { + "name": "Gergely Radics", + "email": "gerifield@ustream.tv" + }, + { + "name": "Warren Doyle", + "email": "w.doyle@fuelled.co" + }, + { + "name": "vharo", + "email": "vharo@geepok.com" + } + ], + "description": "Thermal receipt printer library, for use with ESC/POS compatible printers", + "homepage": "https://github.com/mike42/escpos-php", + "keywords": [ + "ESC-POS", + "driver", + "escpos", + "print", + "receipt" + ], + "time": "2016-03-27 23:08:27" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.17", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/b87da5f63a76e9615a0c74fcf168657b1ea7e41d", + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "time": "2015-09-17 14:45:21" + }, + { + "name": "phpoffice/phpexcel", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PHPExcel.git", + "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32", + "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "ext-xmlwriter": "*", + "php": ">=5.2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "PHPExcel": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "http://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker" + }, + { + "name": "Franck Lefevre", + "homepage": "http://blog.rootslabs.net" + }, + { + "name": "Erik Tilt" + } + ], + "description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "http://phpexcel.codeplex.com", + "keywords": [ + "OpenXML", + "excel", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "time": "2015-05-01 07:00:55" + }, + { + "name": "restler/framework", + "version": "3.0.0", + "target-dir": "Luracast/Restler", + "source": { + "type": "git", + "url": "https://github.com/Luracast/Restler-Framework.git", + "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/6ee10b3e5dbc6376916fed55ec2340a37cce436b", + "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "replace": { + "luracast/restler": "3.*" + }, + "require-dev": { + "bshaffer/oauth2-server-php": "v1.0", + "luracast/explorer": "*", + "mustache/mustache": "dev-master", + "rodneyrehm/plist": "dev-master", + "symfony/yaml": "*", + "twig/twig": "v1.13.0", + "zendframework/zendamf": "dev-master" + }, + "suggest": { + "bshaffer/oauth2-server-php": "Restler can provide OAuth2 authentication using this library (see require-dev for details)", + "luracast/explorer": "Restler's very own api explorer (see require-dev for details)", + "mustache/mustache": "Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)", + "rodneyrehm/plist": "Restler supports tho Apple plist xml format (see require-dev for details)", + "symfony/yaml": "Restler can produce content in yaml format as well (see require-dev for details)", + "twig/twig": "Restler can render HtmlView using twig templates (see require-dev for details)", + "zendframework/zendamf": "Support for the amf document format (see require-dev for details)" + }, + "type": "library", + "extra": { + "branch-alias": { + "master": "v3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Luracast\\Restler": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Luracast", + "email": "arul@luracast.com" + }, + { + "name": "Nick nickl- Lombard", + "email": "github@jigsoft.co.za" + } + ], + "description": "Just the Restler Framework without the tests and examples", + "homepage": "http://luracast.com/products/restler/", + "keywords": [ + "api", + "framework", + "rest", + "server" + ], + "time": "2015-08-04 07:52:49" + }, + { + "name": "tecnickcom/tcpdf", + "version": "6.2.12", + "source": { + "type": "git", + "url": "https://github.com/tecnickcom/TCPDF.git", + "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/2f732eaa91b5665274689b1d40b285a7bacdc37f", + "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "fonts", + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPLv3" + ], + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "homepage": "http://nicolaasuni.tecnick.com" + } + ], + "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", + "homepage": "http://www.tcpdf.org/", + "keywords": [ + "PDFD32000-2008", + "TCPDF", + "barcodes", + "datamatrix", + "pdf", + "pdf417", + "qrcode" + ], + "time": "2015-09-12 10:08:34" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "ckeditor/ckeditor": 20, + "mike42/escpos-php": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.0", + "ext-curl": "*" + }, + "platform-dev": [] +} From e7385c9e4f943663edbf84749c704eed25732da2 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 11:57:13 +0200 Subject: [PATCH 721/834] Try to solve side effect of this fuck... autoload creating a big mess in a lot of features. --- .travis.yml | 10 ++-------- htdocs/.gitignore | 2 +- .../restler/framework/Luracast/Restler/AutoLoader.php | 1 + 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c2e34eb4b8..3052d5ad84c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,11 +103,6 @@ install: composer require squizlabs/php_codesniffer ^2 echo -- | - echo "Adding Composer binaries to the path" - export PATH="$TRAVIS_BUILD_DIR/htdocs/includes/bin:$PATH" - echo - before_script: - | echo Start travis @@ -286,9 +281,8 @@ script: cd - set +e echo - echo Log for upgrade2.php 3.9.0 4.0.0 MAIN_MODULE_API - cat $TRAVIS_BUILD_DIR/upgrade390400-2.log - cat /tmp/dolibarr_install.log + #cat $TRAVIS_BUILD_DIR/upgrade390400-2.log + #cat /tmp/dolibarr_install.log - | echo "Unit testing" diff --git a/htdocs/.gitignore b/htdocs/.gitignore index 050707daf91..608ef55d106 100644 --- a/htdocs/.gitignore +++ b/htdocs/.gitignore @@ -20,5 +20,5 @@ /allscreens* /ecommerce/ /cabinetmed* -/conf/conf.php /webmail* +/conf/conf.php diff --git a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php index c2e454346ee..9639ab44fb7 100644 --- a/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php +++ b/htdocs/includes/restler/framework/Luracast/Restler/AutoLoader.php @@ -307,6 +307,7 @@ class AutoLoader */ private function alias($className, $currentClass) { + // DOL_LDR if ($className == 'Luracast\Restler\string') return; if ($className == 'Luracast\Restler\mixed') return; From 83f3b7305ac6bf0472e1c24725097cc79dc2431e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 12:26:46 +0200 Subject: [PATCH 722/834] Fix travis file --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3052d5ad84c..ef9a7f51d32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,6 +103,11 @@ install: composer require squizlabs/php_codesniffer ^2 echo +- | + echo "Adding path of binaries tools installed by composer to the PATH" + export PATH="$TRAVIS_BUILD_DIR/htdocs/includes/bin:$PATH" + echo + before_script: - | echo Start travis From ea8bb1f0221b1e197eeeb160f6c5491530666fc5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 12:27:45 +0200 Subject: [PATCH 723/834] Fix composer.lock --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 43a9f1d2d7c..df333f5571c 100644 --- a/composer.lock +++ b/composer.lock @@ -267,7 +267,7 @@ }, { "name": "restler/framework", - "version": "3.0.0", + "version": "3.0.0-RC6", "target-dir": "Luracast/Restler", "source": { "type": "git", From e571741ef8964c8f4a19ebac4287d31afbcc4c75 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 12:58:50 +0200 Subject: [PATCH 724/834] Add doc on composer --- build/composer/README | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 build/composer/README diff --git a/build/composer/README b/build/composer/README new file mode 100644 index 00000000000..2464b0d246e --- /dev/null +++ b/build/composer/README @@ -0,0 +1,5 @@ + +To upgrade a lib with composer: + +composer update --no-dev --no-autoloader --dry-run ccampbell/chromephp + From 9f20779136cb12bb1d5fe08cbd871d682022d44e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 13:18:55 +0200 Subject: [PATCH 725/834] Revert "Update escpos-php" This reverts commit 2f42a226d0db3ce0ca6b543668d525a3581fcc70. --- htdocs/includes/mike42/escpos-php/.gitignore | 3 - htdocs/includes/mike42/escpos-php/.travis.yml | 20 - htdocs/includes/mike42/escpos-php/Escpos.php | 851 ++++++++++++++++++ htdocs/includes/mike42/escpos-php/README.md | 285 ++---- .../includes/mike42/escpos-php/autoload.php | 26 - .../includes/mike42/escpos-php/composer.json | 5 - .../mike42/escpos-php/example/README.md | 26 + .../mike42/escpos-php/example/barcode.php | 181 ++++ .../mike42/escpos-php/example/bit-image.php | 32 + .../character-encodings-with-images.php | 59 ++ .../example/character-encodings.php | 58 ++ .../escpos-php/example/character-tables.php | 71 ++ .../mike42/escpos-php/example/demo.php | 167 ++++ .../mike42/escpos-php/example/graphics.php | 32 + .../escpos-php/example/interface/README.md | 8 + .../escpos-php/example/interface/ethernet.php | 22 + .../example/interface/linux-usb.php | 33 + .../escpos-php/example/interface/smb.php | 51 ++ .../example/interface/windows-lpt.php | 30 + .../example/interface/windows-usb.php | 32 + .../escpos-php/example/print-from-html.php | 53 ++ .../escpos-php/example/print-from-pdf.php | 71 ++ .../mike42/escpos-php/example/qr-code.php | 81 ++ .../escpos-php/example/receipt-with-logo.php | 96 ++ .../character-encoding-test-strings.inc | 35 + .../escpos-php/example/resources/document.odt | Bin 0 -> 39751 bytes .../escpos-php/example/resources/document.pdf | Bin 0 -> 24389 bytes .../escpos-php/example/resources/document.z | Bin 0 -> 20320 bytes .../example/resources/escpos-php-small.png | Bin 0 -> 1941 bytes .../example/resources/escpos-php.png | Bin 0 -> 7871 bytes .../escpos-php/example/resources/tux.png | Bin 0 -> 5198 bytes .../specific/29-latvian-star-tup592.php | 21 + .../32-german-tm-t20-ii-custom-command.php | 36 + .../specific/33-spanish-seypos-prp-300.php | 16 + .../example/specific/39-currency-symbols.php | 69 ++ .../specific/44-pound-symbol-star-tsp650.php | 31 + .../example/specific/50-P-822D-greek.php | 16 + .../example/specific/54-gfx-sidebyside.php | 58 ++ .../specific/6-arabic-epos-tep-220m.php | 47 + .../escpos-php/example/specific/README.md | 7 + .../mike42/escpos-php/example/text-size.php | 62 ++ .../src/AbstractCapabilityProfile.php | 61 ++ .../mike42/escpos-php/src/CodePage.php | 181 ++++ .../src/DefaultCapabilityProfile.php | 103 +++ .../escpos-php/src/DummyPrintConnector.php | 78 ++ .../src/EposTepCapabilityProfile.php | 4 + .../mike42/escpos-php/src/EscposImage.php | 405 +++++++++ .../escpos-php/src/EscposPrintBuffer.php | 304 +++++++ .../escpos-php/src/FilePrintConnector.php | 80 ++ .../escpos-php/src/ImagePrintBuffer.php | 99 ++ .../escpos-php/src/NetworkPrintConnector.php | 39 + .../escpos-php/src/P822DCapabilityProfile.php | 90 ++ .../mike42/escpos-php/src/PrintBuffer.php | 75 ++ .../mike42/escpos-php/src/PrintConnector.php | 56 ++ .../src/SimpleCapabilityProfile.php | 17 + .../escpos-php/src/StarCapabilityProfile.php | 82 ++ .../escpos-php/src/WindowsPrintConnector.php | 356 ++++++++ ...Characters-DefaultCapabilityProfile.ser.gz | Bin 0 -> 12928 bytes .../Characters-SimpleCapabilityProfile.ser.gz | Bin 0 -> 1116 bytes .../Characters-StarCapabilityProfile.ser.gz | Bin 0 -> 9416 bytes .../mike42/escpos-php/test/bootstrap.php | 27 + .../test/integration/ExampleTest.php | 126 +++ .../integration/resources/output/barcode.bin | Bin 0 -> 2342 bytes .../resources/output/bit-image.bin | Bin 0 -> 9789 bytes .../resources/output/character-encodings.bin | Bin 0 -> 1780 bytes .../resources/output/character-tables.bin | Bin 0 -> 8174 bytes .../integration/resources/output/demo.bin | Bin 0 -> 73643 bytes .../integration/resources/output/graphics.bin | Bin 0 -> 9635 bytes .../resources/output/interface.bin | 2 + .../resources/output/print-from-pdf.bin.z | Bin 0 -> 17081 bytes .../integration/resources/output/qr-code.bin | Bin 0 -> 1551 bytes .../resources/output/receipt-with-logo.bin | Bin 0 -> 9579 bytes .../resources/output/text-size.bin | Bin 0 -> 368 bytes .../mike42/escpos-php/test/phpunit.xml | 16 + .../unit/AbstractCapabilityProfileTest.php | 69 ++ .../escpos-php/test/unit/EscposImageTest.php | 235 +++++ .../test/unit/EscposPrintBufferTest.php | 150 +++ .../escpos-php/test/unit/EscposTest.php | 765 ++++++++++++++++ .../test/unit/WindowsPrintConnectorTest.php | 278 ++++++ .../test/unit/resources/black_transparent.gif | Bin 0 -> 65 bytes .../test/unit/resources/black_transparent.png | Bin 0 -> 167 bytes .../test/unit/resources/black_white.bmp | Bin 0 -> 138 bytes .../test/unit/resources/black_white.gif | Bin 0 -> 65 bytes .../test/unit/resources/black_white.jpg | Bin 0 -> 175 bytes .../test/unit/resources/black_white.png | Bin 0 -> 156 bytes .../test/unit/resources/canvas_black.bmp | Bin 0 -> 142 bytes .../test/unit/resources/canvas_black.gif | Bin 0 -> 72 bytes .../test/unit/resources/canvas_black.jpg | Bin 0 -> 160 bytes .../test/unit/resources/canvas_black.png | Bin 0 -> 239 bytes .../test/unit/resources/canvas_white.bmp | Bin 0 -> 142 bytes .../test/unit/resources/canvas_white.gif | Bin 0 -> 72 bytes .../test/unit/resources/canvas_white.jpg | Bin 0 -> 160 bytes .../test/unit/resources/canvas_white.png | Bin 0 -> 239 bytes .../escpos-php/test/unit/resources/demo.php | 19 + .../escpos-php/test/unit/resources/doc.pdf | Bin 0 -> 6681 bytes 95 files changed, 6156 insertions(+), 252 deletions(-) delete mode 100644 htdocs/includes/mike42/escpos-php/.travis.yml create mode 100644 htdocs/includes/mike42/escpos-php/Escpos.php delete mode 100644 htdocs/includes/mike42/escpos-php/autoload.php create mode 100644 htdocs/includes/mike42/escpos-php/example/README.md create mode 100644 htdocs/includes/mike42/escpos-php/example/barcode.php create mode 100644 htdocs/includes/mike42/escpos-php/example/bit-image.php create mode 100644 htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php create mode 100644 htdocs/includes/mike42/escpos-php/example/character-encodings.php create mode 100644 htdocs/includes/mike42/escpos-php/example/character-tables.php create mode 100644 htdocs/includes/mike42/escpos-php/example/demo.php create mode 100644 htdocs/includes/mike42/escpos-php/example/graphics.php create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/README.md create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/ethernet.php create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/smb.php create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php create mode 100644 htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php create mode 100644 htdocs/includes/mike42/escpos-php/example/print-from-html.php create mode 100644 htdocs/includes/mike42/escpos-php/example/print-from-pdf.php create mode 100644 htdocs/includes/mike42/escpos-php/example/qr-code.php create mode 100644 htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/document.odt create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/document.pdf create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/document.z create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png create mode 100644 htdocs/includes/mike42/escpos-php/example/resources/tux.png create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php create mode 100644 htdocs/includes/mike42/escpos-php/example/specific/README.md create mode 100644 htdocs/includes/mike42/escpos-php/example/text-size.php create mode 100644 htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/CodePage.php create mode 100644 htdocs/includes/mike42/escpos-php/src/DefaultCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php create mode 100644 htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/EscposImage.php create mode 100644 htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php create mode 100644 htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php create mode 100644 htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php create mode 100644 htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php create mode 100644 htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/PrintBuffer.php create mode 100644 htdocs/includes/mike42/escpos-php/src/PrintConnector.php create mode 100644 htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php create mode 100644 htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php create mode 100644 htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz create mode 100644 htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz create mode 100644 htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz create mode 100644 htdocs/includes/mike42/escpos-php/test/bootstrap.php create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin create mode 100644 htdocs/includes/mike42/escpos-php/test/phpunit.xml create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php create mode 100644 htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf diff --git a/htdocs/includes/mike42/escpos-php/.gitignore b/htdocs/includes/mike42/escpos-php/.gitignore index 3fe7ed0ea16..0e8fc4a31ee 100644 --- a/htdocs/includes/mike42/escpos-php/.gitignore +++ b/htdocs/includes/mike42/escpos-php/.gitignore @@ -10,6 +10,3 @@ doc/doxygen_sqlite3.db # composer files vendor/ - -# cache -src/Mike42/Escpos/PrintBuffers/cache/ \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/.travis.yml b/htdocs/includes/mike42/escpos-php/.travis.yml deleted file mode 100644 index 969cd6aad15..00000000000 --- a/htdocs/includes/mike42/escpos-php/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - - nightly - -matrix: - allow_failures: - - php: nightly - -script: phpunit --configuration test/phpunit.xml --coverage-text - -install: - - composer install - diff --git a/htdocs/includes/mike42/escpos-php/Escpos.php b/htdocs/includes/mike42/escpos-php/Escpos.php new file mode 100644 index 00000000000..57e7eb2c8ac --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/Escpos.php @@ -0,0 +1,851 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * This class generates ESC/POS printer control commands for compatible printers. + * See README.md for a summary of compatible printers and supported commands, and + * basic usage. + * + * See example/demo.php for a detailed print-out demonstrating the range of commands + * implemented in this project. + * + * Note that some functions have not been implemented: + * - Set paper sensors + * - Select print colour + * + * Please direct feature requests, bug reports and contributions to escpos-php + * on Github: + * - https://github.com/mike42/escpos-php + */ +require_once(dirname(__FILE__) . "/src/EscposImage.php"); +require_once(dirname(__FILE__) . "/src/PrintBuffer.php"); +require_once(dirname(__FILE__) . "/src/EscposPrintBuffer.php"); +require_once(dirname(__FILE__) . "/src/PrintConnector.php"); +require_once(dirname(__FILE__) . "/src/WindowsPrintConnector.php"); +require_once(dirname(__FILE__) . "/src/FilePrintConnector.php"); +require_once(dirname(__FILE__) . "/src/NetworkPrintConnector.php"); +require_once(dirname(__FILE__) . "/src/AbstractCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/DefaultCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/SimpleCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/EposTepCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/StarCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/P822DCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/CodePage.php"); +require_once(dirname(__FILE__) . "/src/ImagePrintBuffer.php"); + +class Escpos { + /* ASCII codes */ + const NUL = "\x00"; + const LF = "\x0a"; + const ESC = "\x1b"; + const FS = "\x1c"; + const FF = "\x0c"; + const GS = "\x1d"; + const DLE = "\x10"; + const EOT = "\x04"; + + /* Barcode types */ + const BARCODE_UPCA = 65; + const BARCODE_UPCE = 66; + const BARCODE_JAN13 = 67; + const BARCODE_JAN8 = 68; + const BARCODE_CODE39 = 69; + const BARCODE_ITF = 70; + const BARCODE_CODABAR = 71; + const BARCODE_CODE93 = 72; + const BARCODE_CODE128 = 73; + + /* Barcode HRI (human-readable interpretation) text position */ + const BARCODE_TEXT_NONE = 0; + const BARCODE_TEXT_ABOVE = 1; + const BARCODE_TEXT_BELOW = 2; + + /* Cut types */ + const CUT_FULL = 65; + const CUT_PARTIAL = 66; + + /* Fonts */ + const FONT_A = 0; + const FONT_B = 1; + const FONT_C = 2; + + /* Image sizing options */ + const IMG_DEFAULT = 0; + const IMG_DOUBLE_WIDTH = 1; + const IMG_DOUBLE_HEIGHT = 2; + + /* Justifications */ + const JUSTIFY_LEFT = 0; + const JUSTIFY_CENTER = 1; + const JUSTIFY_RIGHT = 2; + + /* Print mode constants */ + const MODE_FONT_A = 0; + const MODE_FONT_B = 1; + const MODE_EMPHASIZED = 8; + const MODE_DOUBLE_HEIGHT = 16; + const MODE_DOUBLE_WIDTH = 32; + const MODE_UNDERLINE = 128; + + /* QR code error correction levels */ + const QR_ECLEVEL_L = 0; + const QR_ECLEVEL_M = 1; + const QR_ECLEVEL_Q = 2; + const QR_ECLEVEL_H = 3; + + /* QR code models */ + const QR_MODEL_1 = 1; + const QR_MODEL_2 = 2; + const QR_MICRO = 3; + + /* Printer statuses */ + const STATUS_PRINTER = 1; + const STATUS_OFFLINE_CAUSE = 2; + const STATUS_ERROR_CAUSE = 3; + const STATUS_PAPER_ROLL = 4; + const STATUS_INK_A = 7; + const STATUS_INK_B = 6; + const STATUS_PEELER = 8; + + /* Underline */ + const UNDERLINE_NONE = 0; + const UNDERLINE_SINGLE = 1; + const UNDERLINE_DOUBLE = 2; + + /** + * @var PrintBuffer The printer's output buffer. + */ + private $buffer; + + /** + * @var PrintConnector + */ + private $connector; + + /** + * @var AbstractCapabilityProfile + */ + private $profile; + + /** + * @var int Current character code table + */ + private $characterTable; + + /** + * Construct a new print object + * + * @param PrintConnector $connector The PrintConnector to send data to. If not set, output is sent to standard output. + * @param AbstractCapabilityProfile $profile Supported features of this printer. If not set, the DefaultCapabilityProfile will be used, which is suitable for Epson printers. + * @throws InvalidArgumentException + */ + function __construct(PrintConnector $connector = null, AbstractCapabilityProfile $profile = null) { + if(is_null($connector)) { + if(php_sapi_name() == 'cli') { + $connector = new FilePrintConnector("php://stdout"); + } else { + throw new InvalidArgumentException("Argument passed to Escpos::__construct() must implement interface PrintConnector, null given."); + } + } + /* Set connector */ + $this -> connector = $connector; + + /* Set capability profile */ + if($profile === null) { + $profile = DefaultCapabilityProfile::getInstance(); + } + $this -> profile = $profile; + /* Set buffer */ + $buffer = new EscposPrintBuffer(); + $this -> buffer = null; + $this -> setPrintBuffer($buffer); + $this -> initialize(); + } + + /** + * Print a barcode. + * + * @param string $content The information to encode. + * @param int $type The barcode standard to output. If not specified, `Escpos::BARCODE_CODE39` will be used. Note that some barcode formats only support specific lengths or sets of characters. + * @throws InvalidArgumentException Where the length or characters used in $content is invalid for the requested barcode format. + */ + function barcode($content, $type = self::BARCODE_CODE39) { + /* Validate input */ + self::validateInteger($type, 65, 73, __FUNCTION__, "Barcode type"); + $len = strlen($content); + switch($type) { + case self::BARCODE_UPCA: + self::validateInteger($len, 11, 12, __FUNCTION__, "UPCA barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{11,12}$/", "UPCA barcode content"); + break; + case self::BARCODE_UPCE: + self::validateIntegerMulti($len, array(array(6, 8), array(11, 12)), __FUNCTION__, "UPCE barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{6,8}|[0-9]{11,12})$/", "UPCE barcode content"); + break; + case self::BARCODE_JAN13: + self::validateInteger($len, 12, 13, __FUNCTION__, "JAN13 barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{12,13}$/", "JAN13 barcode content"); + break; + case self::BARCODE_JAN8: + self::validateInteger($len, 7, 8, __FUNCTION__, "JAN8 barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{7,8}$/", "JAN8 barcode content"); + break; + case self::BARCODE_CODE39: + self::validateInteger($len, 1, 255, __FUNCTION__, "CODE39 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$/", "CODE39 barcode content"); + break; + case self::BARCODE_ITF: + self::validateInteger($len, 2, 255, __FUNCTION__, "ITF barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{2})+$/", "ITF barcode content"); + break; + case self::BARCODE_CODABAR: + self::validateInteger($len, 1, 255, __FUNCTION__, "Codabar barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$/", "Codabar barcode content"); + break; + case self::BARCODE_CODE93: + self::validateInteger($len, 1, 255, __FUNCTION__, "Code93 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^[\\x00-\\x7F]+$/", "Code93 barcode content"); + break; + case self::BARCODE_CODE128: + self::validateInteger($len, 1, 255, __FUNCTION__, "Code128 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + // The CODE128 encoder is quite complex, so only a very basic header-check is applied here. + self::validateStringRegex($content, __FUNCTION__, "/^\{[A-C][\\x00-\\x7F]+$/", "Code128 barcode content"); + break; + } + if(!$this -> profile -> getSupportsBarcodeB()) { + // A simpler barcode command which supports fewer codes + self::validateInteger($type, 65, 71, __FUNCTION__); + $this -> connector -> write(self::GS . "k" . chr($type - 65) . $content . self::NUL); + return; + } + // More advanced function B, used in preference + $this -> connector -> write(self::GS . "k" . chr($type) . chr(strlen($content)) . $content); + } + + /** + * Print an image, using the older "bit image" command. This creates padding on the right of the image, + * if its width is not divisible by 8. + * + * Should only be used if your printer does not support the graphics() command. + * + * @param EscposImage $img The image to print + * @param EscposImage $size Size modifier for the image. + */ + function bitImage(EscposImage $img, $size = self::IMG_DEFAULT) { + self::validateInteger($size, 0, 3, __FUNCTION__); + $header = self::dataHeader(array($img -> getWidthBytes(), $img -> getHeight()), true); + $this -> connector -> write(self::GS . "v0" . chr($size) . $header); + $this -> connector -> write($img -> toRasterFormat()); + } + + /** + * Close the underlying buffer. With some connectors, the + * job will not actually be sent to the printer until this is called. + */ + function close() { + $this -> connector -> finalize(); + } + + /** + * Cut the paper. + * + * @param int $mode Cut mode, either Escpos::CUT_FULL or Escpos::CUT_PARTIAL. If not specified, `Escpos::CUT_FULL` will be used. + * @param int $lines Number of lines to feed + */ + function cut($mode = self::CUT_FULL, $lines = 3) { + // TODO validation on cut() inputs + $this -> connector -> write(self::GS . "V" . chr($mode) . chr($lines)); + } + + /** + * Print and feed line / Print and feed n lines. + * + * @param int $lines Number of lines to feed + */ + function feed($lines = 1) { + self::validateInteger($lines, 1, 255, __FUNCTION__); + if($lines <= 1) { + $this -> connector -> write(self::LF); + } else { + $this -> connector -> write(self::ESC . "d" . chr($lines)); + } + } + + /** + * Some printers require a form feed to release the paper. On most printers, this + * command is only useful in page mode, which is not implemented in this driver. + */ + function feedForm() { + $this -> connector -> write(self::FF); + } + + /** + * Print and reverse feed n lines. + * + * @param int $lines number of lines to feed. If not specified, 1 line will be fed. + */ + function feedReverse($lines = 1) { + self::validateInteger($lines, 1, 255, __FUNCTION__); + $this -> connector -> write(self::ESC . "e" . chr($lines)); + } + + /** + * @return number + */ + function getCharacterTable() { + return $this -> characterTable; + } + + /** + * @return PrintBuffer + */ + function getPrintBuffer() { + return $this -> buffer; + } + + /** + * @return PrintConnector + */ + function getPrintConnector() { + return $this -> connector; + } + + /** + * @return AbstractCapabilityProfile + */ + function getPrinterCapabilityProfile() { + return $this -> profile; + } + + /** + * @param int $type The type of status to request + * @return stdClass Class containing requested status, or null if either no status was received, or your print connector is unable to read from the printer. + */ + function getPrinterStatus($type = self::STATUS_PRINTER) { + self::validateIntegerMulti($type, array(array(1, 4), array(6, 8)), __FUNCTION__); + // Determine which flags we are looking for + $statusFlags = array( + self::STATUS_PRINTER => array( + 4 => "pulseHigh", // connector pin 3, see pulse(). + 8 => "offline", + 32 => "waitingForOnlineRecovery", + 64 => "feedButtonPressed" + ), + self::STATUS_OFFLINE_CAUSE => array( + 4 => "coverOpen", + 8 => "paperManualFeed", + 32 => "paperEnd", + 64 => "errorOccurred" + ), + self::STATUS_ERROR_CAUSE => array( + 4 => "recoverableError", + 8 => "autocutterError", + 32 => "unrecoverableError", + 64 => "autorecoverableError" + ), + self::STATUS_PAPER_ROLL => array( + 4 => "paperNearEnd", + 32 => "paperNotPresent" + ), + self::STATUS_INK_A => array( + 4 => "inkNearEnd", + 8 => "inkEnd", + 32 => "inkNotPresent", + 64 => "cleaning" + ), + self::STATUS_INK_B => array( + 4 => "inkNearEnd", + 8 => "inkEnd", + 32 => "inkNotPresent" + ), + self::STATUS_PEELER => array( + 4 => "labelWaitingForRemoval", + 32 => "labelPaperNotDetected" + ) + ); + $flags = $statusFlags[$type]; + // Clear any previous statuses which haven't been read yet + $f = $this -> connector -> read(1); + // Make request + $reqC = chr($type); + switch($type) { + // Special cases: These are two-character requests + case self::STATUS_INK_A: + $reqC = chr(7) . chr(1); + break; + case self::STATUS_INK_B: + $reqC = chr(7) . chr(2); + break; + case self::STATUS_PEELER: + $reqC = chr(8) . chr(3); + break; + } + $this -> connector -> write(self::DLE . self::EOT . $reqC); + // Wait for single-character response + $f = $this -> connector -> read(1); + $i = 0; + while($f === false && $i < 50000) { + usleep(100); + $f = $this -> connector -> read(1); + $i++; + } + if($f === false) { + // Timeout + return null; + } + $ret = new stdClass(); + foreach($flags as $num => $name) { + $ret -> $name = (ord($f) & $num) != 0; + } + return $ret; + } + + /** + * Print an image to the printer. + * + * Size modifiers are: + * - IMG_DEFAULT (leave image at original size) + * - IMG_DOUBLE_WIDTH + * - IMG_DOUBLE_HEIGHT + * + * See the example/ folder for detailed examples. + * + * The function bitImage() takes the same parameters, and can be used if + * your printer doesn't support the newer graphics commands. + * + * @param EscposImage $img The image to print. + * @param int $size Output size modifier for the image. + */ + function graphics(EscposImage $img, $size = self::IMG_DEFAULT) { + self::validateInteger($size, 0, 3, __FUNCTION__); + $imgHeader = self::dataHeader(array($img -> getWidth(), $img -> getHeight()), true); + $tone = '0'; + $colors = '1'; + $xm = (($size & self::IMG_DOUBLE_WIDTH) == self::IMG_DOUBLE_WIDTH) ? chr(2) : chr(1); + $ym = (($size & self::IMG_DOUBLE_HEIGHT) == self::IMG_DOUBLE_HEIGHT) ? chr(2) : chr(1); + $header = $tone . $xm . $ym . $colors . $imgHeader; + $this -> wrapperSendGraphicsData('0', 'p', $header . $img -> toRasterFormat()); + $this -> wrapperSendGraphicsData('0', '2'); + } + + /** + * Initialize printer. This resets formatting back to the defaults. + */ + function initialize() { + $this -> connector -> write(self::ESC . "@"); + $this -> characterTable = 0; + } + + /** + * Generate a pulse, for opening a cash drawer if one is connected. + * The default settings should open an Epson drawer. + * + * @param int $pin 0 or 1, for pin 2 or pin 5 kick-out connector respectively. + * @param int $on_ms pulse ON time, in milliseconds. + * @param int $off_ms pulse OFF time, in milliseconds. + */ + function pulse($pin = 0, $on_ms = 120, $off_ms = 240) { + self::validateInteger($pin, 0, 1, __FUNCTION__); + self::validateInteger($on_ms, 1, 511, __FUNCTION__); + self::validateInteger($off_ms, 1, 511, __FUNCTION__); + $this -> connector -> write(self::ESC . "p" . chr($pin + 48) . chr($on_ms / 2) . chr($off_ms / 2)); + } + + /** + * Print the given data as a QR code on the printer. + * + * @param string $content The content of the code. Numeric data will be more efficiently compacted. + * @param int $ec Error-correction level to use. One of Escpos::QR_ECLEVEL_L (default), Escpos::QR_ECLEVEL_M, Escpos::QR_ECLEVEL_Q or Escpos::QR_ECLEVEL_H. Higher error correction results in a less compact code. + * @param int $size Pixel size to use. Must be 1-16 (default 3) + * @param int $model QR code model to use. Must be one of Escpos::QR_MODEL_1, Escpos::QR_MODEL_2 (default) or Escpos::QR_MICRO (not supported by all printers). + */ + function qrCode($content, $ec = self::QR_ECLEVEL_L, $size = 3, $model = self::QR_MODEL_2) { + self::validateString($content, __FUNCTION__); + self::validateInteger($ec, 0, 3, __FUNCTION__); + self::validateInteger($size, 1, 16, __FUNCTION__); + self::validateInteger($model, 1, 3, __FUNCTION__); + if($content == "") { + return; + } + if(!$this -> profile -> getSupportsQrCode()) { + // TODO use software rendering via phpqrcode instead + throw new Exception("QR codes are not supported on your printer."); + } + $cn = '1'; // Code type for QR code + // Select model: 1, 2 or micro. + $this -> wrapperSend2dCodeData(chr(65), $cn, chr(48 + $model) . chr(0)); + // Set dot size. + $this -> wrapperSend2dCodeData(chr(67), $cn, chr($size)); + // Set error correction level: L, M, Q, or H + $this -> wrapperSend2dCodeData(chr(69), $cn, chr(48 + $ec)); + // Send content & print + $this -> wrapperSend2dCodeData(chr(80), $cn, $content, '0'); + $this -> wrapperSend2dCodeData(chr(81), $cn, '', '0'); + } + + /** + * Switch character table (code page) manually. Used in conjunction with textRaw() to + * print special characters which can't be encoded automatically. + * + * @param int $table The table to select. Available code tables are model-specific. + */ + function selectCharacterTable($table = 0) { + self::validateInteger($table, 0, 255, __FUNCTION__); + $supported = $this -> profile -> getSupportedCodePages(); + if(!isset($supported[$table])) { + throw new InvalidArgumentException("There is no code table $table allowed by this printer's capability profile."); + } + $this -> characterTable = $table; + if($this -> profile -> getSupportsStarCommands()) { + /* Not an ESC/POS command: STAR printers stash all the extra code pages under a different command. */ + $this -> connector -> write(self::ESC . self::GS . "t" . chr($table)); + return; + } + $this -> connector -> write(self::ESC . "t" . chr($table)); + } + + /** + * Select print mode(s). + * + * Several MODE_* constants can be OR'd together passed to this function's `$mode` argument. The valid modes are: + * - MODE_FONT_A + * - MODE_FONT_B + * - MODE_EMPHASIZED + * - MODE_DOUBLE_HEIGHT + * - MODE_DOUBLE_WIDTH + * - MODE_UNDERLINE + * + * @param int $mode The mode to use. Default is Escpos::MODE_FONT_A, with no special formatting. This has a similar effect to running initialize(). + */ + function selectPrintMode($mode = self::MODE_FONT_A) { + $allModes = self::MODE_FONT_B | self::MODE_EMPHASIZED | self::MODE_DOUBLE_HEIGHT | self::MODE_DOUBLE_WIDTH | self::MODE_UNDERLINE; + if(!is_integer($mode) || $mode < 0 || ($mode & $allModes) != $mode) { + throw new InvalidArgumentException("Invalid mode"); + } + + $this -> connector -> write(self::ESC . "!" . chr($mode)); + } + + /** + * Set barcode height. + * + * @param int $height Height in dots. If not specified, 8 will be used. + */ + function setBarcodeHeight($height = 8) { + self::validateInteger($height, 1, 255, __FUNCTION__); + $this -> connector -> write(self::GS . "h" . chr($height)); + } + + + /** + * Set the position for the Human Readable Interpretation (HRI) of barcode characters. + * + * @param position $position. Use Escpos::BARCODE_TEXT_NONE to hide the text (default), or any combination of Escpos::BARCODE_TEXT_TOP and Escpos::BARCODE_TEXT_BOTTOM flags to display the text. + */ + function setBarcodeTextPosition($position = self::BARCODE_TEXT_NONE) { + self::validateInteger($position, 0, 3, __FUNCTION__, "Barcode text position"); + $this -> connector -> write(self::GS . "H" . chr($position)); + } + + /** + * Turn double-strike mode on/off. + * + * @param boolean $on true for double strike, false for no double strike + */ + function setDoubleStrike($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::ESC . "G". ($on ? chr(1) : chr(0))); + } + + /** + * Turn emphasized mode on/off. + * + * @param boolean $on true for emphasis, false for no emphasis + */ + function setEmphasis($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::ESC . "E". ($on ? chr(1) : chr(0))); + } + + /** + * Select font. Most printers have two fonts (Fonts A and B), and some have a third (Font C). + * + * @param int $font The font to use. Must be either Escpos::FONT_A, Escpos::FONT_B, or Escpos::FONT_C. + */ + function setFont($font = self::FONT_A) { + self::validateInteger($font, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "M" . chr($font)); + } + + /** + * Select justification. + * + * @param int $justification One of Escpos::JUSTIFY_LEFT, Escpos::JUSTIFY_CENTER, or Escpos::JUSTIFY_RIGHT. + */ + function setJustification($justification = self::JUSTIFY_LEFT) { + self::validateInteger($justification, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "a" . chr($justification)); + } + + /** + * Attach a different print buffer to the printer. Buffers are responsible for handling text output to the printer. + * + * @param PrintBuffer $buffer The buffer to use. + * @throws InvalidArgumentException Where the buffer is already attached to a different printer. + */ + function setPrintBuffer(PrintBuffer $buffer) { + if($buffer === $this -> buffer) { + return; + } + if($buffer -> getPrinter() != null) { + throw new InvalidArgumentException("This buffer is already attached to a printer."); + } + if($this -> buffer !== null) { + $this -> buffer -> setPrinter(null); + } + $this -> buffer = $buffer; + $this -> buffer -> setPrinter($this); + } + + /** + * Set black/white reverse mode on or off. In this mode, text is printed white on a black background. + * + * @param boolean $on True to enable, false to disable. + */ + function setReverseColors($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::GS . "B" . ($on ? chr(1) : chr(0))); + } + + /** + * Set the size of text, as a multiple of the normal size. + * + * @param int $widthMultiplier Multiple of the regular height to use (range 1 - 8) + * @param int $heightMultiplier Multiple of the regular height to use (range 1 - 8) + */ + function setTextSize($widthMultiplier, $heightMultiplier) { + self::validateInteger($widthMultiplier, 1, 8, __FUNCTION__); + self::validateInteger($heightMultiplier, 1, 8, __FUNCTION__); + $c = pow(2,4) * ($widthMultiplier - 1) + ($heightMultiplier - 1); + $this -> connector -> write(self::GS . "!" . chr($c)); + } + + /** + * Set underline for printed text. + * + * Argument can be true/false, or one of UNDERLINE_NONE, + * UNDERLINE_SINGLE or UNDERLINE_DOUBLE. + * + * @param int $underline Either true/false, or one of Escpos::UNDERLINE_NONE, Escpos::UNDERLINE_SINGLE or Escpos::UNDERLINE_DOUBLE. Defaults to Escpos::UNDERLINE_SINGLE. + */ + function setUnderline($underline = self::UNDERLINE_SINGLE) { + /* Map true/false to underline constants */ + if($underline === true) { + $underline = self::UNDERLINE_SINGLE; + } else if($underline === false) { + $underline = self::UNDERLINE_NONE; + } + /* Set the underline */ + self::validateInteger($underline, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "-". chr($underline)); + } + + /** + * Add text to the buffer. + * + * Text should either be followed by a line-break, or feed() should be called + * after this to clear the print buffer. + * + * @param string $str Text to print + */ + function text($str = "") { + self::validateString($str, __FUNCTION__); + $this -> buffer -> writeText((string)$str); + } + + /** + * Add text to the buffer without attempting to interpret chararacter codes. + * + * Text should either be followed by a line-break, or feed() should be called + * after this to clear the print buffer. + * + * @param string $str Text to print + */ + function textRaw($str = "") { + self::validateString($str, __FUNCTION__); + $this -> buffer -> writeTextRaw((string)$str); + } + + /** + * Wrapper for GS ( k, to calculate and send correct data length. + * + * @param string $fn Function to use + * @param string $cn Output code type. Affects available data + * @param string $data Data to send. + * @param string $m Modifier/variant for function. Often '0' where used. + * @throws InvalidArgumentException Where the input lengths are bad. + */ + private function wrapperSend2dCodeData($fn, $cn, $data = '', $m = '') { + if(strlen($m) > 1 || strlen($cn) != 1 || strlen($fn) != 1) { + throw new InvalidArgumentException("wrapperSend2dCodeData: cn and fn must be one character each."); + } + $header = $this -> intLowHigh(strlen($data) + strlen($m) + 2, 2); + $this -> connector -> write(self::GS . "(k" . $header . $cn . $fn . $m . $data); + } + + /** + * Wrapper for GS ( L, to calculate and send correct data length. + * + * @param string $m Modifier/variant for function. Usually '0'. + * @param string $fn Function number to use, as character. + * @param string $data Data to send. + * @throws InvalidArgumentException Where the input lengths are bad. + */ + private function wrapperSendGraphicsData($m, $fn, $data = '') { + if(strlen($m) != 1 || strlen($fn) != 1) { + throw new InvalidArgumentException("wrapperSendGraphicsData: m and fn must be one character each."); + } + $header = $this -> intLowHigh(strlen($data) + 2, 2); + $this -> connector -> write(self::GS . "(L" . $header . $m . $fn . $data); + } + + /** + * Convert widths and heights to characters. Used before sending graphics to set the size. + * + * @param array $inputs + * @param boolean $long True to use 4 bytes, false to use 2 + * @return string + */ + private static function dataHeader(array $inputs, $long = true) { + $outp = array(); + foreach($inputs as $input) { + if($long) { + $outp[] = Escpos::intLowHigh($input, 2); + } else { + self::validateInteger($input, 0 , 255, __FUNCTION__); + $outp[] = chr($input); + } + } + return implode("", $outp); + } + + /** + * Generate two characters for a number: In lower and higher parts, or more parts as needed. + * @param int $int Input number + * @param int $length The number of bytes to output (1 - 4). + */ + private static function intLowHigh($input, $length) { + $maxInput = (256 << ($length * 8) - 1); + self::validateInteger($length, 1, 4, __FUNCTION__); + self::validateInteger($input, 0, $maxInput, __FUNCTION__); + $outp = ""; + for($i = 0; $i < $length; $i++) { + $outp .= chr($input % 256); + $input = (int)($input / 256); + } + return $outp; + } + + /** + * Throw an exception if the argument given is not a boolean + * + * @param boolean $test the input to test + * @param string $source the name of the function calling this + */ + protected static function validateBoolean($test, $source) { + if(!($test === true || $test === false)) { + throw new InvalidArgumentException("Argument to $source must be a boolean"); + } + } + + /** + * Throw an exception if the argument given is not an integer within the specified range + * + * @param int $test the input to test + * @param int $min the minimum allowable value (inclusive) + * @param int $max the maximum allowable value (inclusive) + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateInteger($test, $min, $max, $source, $argument = "Argument") { + self::validateIntegerMulti($test, array(array($min, $max)), $source, $argument); + } + + /** + * Throw an exception if the argument given is not an integer within one of the specified ranges + * + * @param int $test the input to test + * @param arrray $ranges array of two-item min/max ranges. + * @param string $source the name of the function calling this + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateIntegerMulti($test, array $ranges, $source, $argument = "Argument") { + if(!is_integer($test)) { + throw new InvalidArgumentException("$argument given to $source must be a number, but '$test' was given."); + } + $match = false; + foreach($ranges as $range) { + $match |= $test >= $range[0] && $test <= $range[1]; + } + if(!$match) { + // Put together a good error "range 1-2 or 4-6" + $rangeStr = "range "; + for($i = 0; $i < count($ranges); $i++) { + $rangeStr .= $ranges[$i][0] . "-" . $ranges[$i][1]; + if($i == count($ranges) - 1) { + continue; + } else if($i == count($ranges) - 2) { + $rangeStr .= " or "; + } else { + $rangeStr .= ", "; + } + } + throw new InvalidArgumentException("$argument given to $source must be in $rangeStr, but $test was given."); + } + } + + /** + * Throw an exception if the argument given can't be cast to a string + * + * @param string $test the input to test + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateString($test, $source, $argument = "Argument") { + if (is_object($test) && !method_exists($test, '__toString')) { + throw new InvalidArgumentException("$argument to $source must be a string"); + } + } + + protected static function validateStringRegex($test, $source, $regex, $argument = "Argument") { + if(preg_match($regex, $test) === 0) { + throw new InvalidArgumentException("$argument given to $source is invalid. It should match regex '$regex', but '$test' was given."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/README.md b/htdocs/includes/mike42/escpos-php/README.md index 065fdea3768..43798bb0852 100644 --- a/htdocs/includes/mike42/escpos-php/README.md +++ b/htdocs/includes/mike42/escpos-php/README.md @@ -1,10 +1,73 @@ -# ESC/POS Print Driver for PHP - +ESC/POS Print Driver for PHP +============================ This project implements a subset of Epson's ESC/POS protocol for thermal receipt printers. It allows you to generate and print receipts with basic formatting, cutting, and barcodes on a compatible printer. The library was developed to add drop-in support for receipt printing to any PHP app, including web-based point-of-sale (POS) applications. -## Compatibility +Basic usage +----------- +A "hello world" receipt can be generated easily (Call this `hello-world.php`): +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` +This would be printed as: +``` +# Networked printer +php hello-world.php | nc 10.x.x.x. 9100 +# Local printer +php hello-world.php > /dev/... +# Windows local printer +php hello-world.php > foo.txt +net use LPT1 \\server\printer +copy foo.txt LPT1 +del foo.txt +``` + +From your web app, you could pass the output directly to a socket if your printer is networked: +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` + +Or to a local printer: +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` + +### Basic workflow +The library should be initialised with a PrintConnector, which will pass on the data to your printer. +Use the table under "Compatibility", or the examples below to choose the appropriate connector for your +platform & interface. If no connector is specified, then standard output is used. + +When you have finished using the print object, call `close()` to finalize any data transfers. + +### Tips & examples +On Linux, your printer device file will be somewhere like `/dev/lp0` (parallel), `/dev/usb/lp1` (USB), `/dev/ttyUSB0` (USB-Serial), `/dev/ttyS0` (serial). + +On Windows, the device files will be along the lines of `LPT1` (parallel) or `COM1` (serial). Use the `WindowsPrintConnector` to tap into system printing on Windows (eg. [Windows USB](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-usb.php), [SMB](https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php) or [Windows LPT](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-lpt.php)) - this submits print jobs via a queue rather than communicating directly with the printer. + +A complete real-world receipt can be found in the code of [Auth](https://github.com/mike42/Auth) in [ReceiptPrinter.php](https://github.com/mike42/Auth/blob/master/lib/misc/ReceiptPrinter.php). It includes justification, boldness, and a barcode. + +Other examples are located in the [example/](https://github.com/mike42/escpos-php/blob/master/example/) directory. + +Compatibility +------------- ### Interfaces and operating systems This driver is known to work with the following OS/interface combinations: @@ -52,19 +115,11 @@ This driver is known to work with the following OS/interface combinations: - - - - - -
    '.$langs->trans("Warehouse").''.$langs->trans("NumberOfUnit").''.$langs->trans("AverageUnitPricePMPShort").'
    '; print '
    '; diff --git a/htdocs/product/stock/productlot_card.php b/htdocs/product/stock/productlot_card.php index 2e8620797c7..4af5b8a4d59 100644 --- a/htdocs/product/stock/productlot_card.php +++ b/htdocs/product/stock/productlot_card.php @@ -20,7 +20,7 @@ * \file stock/productlot_card.php * \ingroup stock * \brief This file is an example of a php page - * Initialy built by build_class_from_table on 2016-05-17 10:33 + * Initialy built by build_class_from_table on 2016-05-17 12:22 */ //if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); @@ -57,12 +57,12 @@ $backtopage = GETPOST('backtopage'); $myparam = GETPOST('myparam','alpha'); +$search_entity=GETPOST('search_entity','int'); $search_fk_product=GETPOST('search_fk_product','int'); $search_batch=GETPOST('search_batch','alpha'); -$search_note_public=GETPOST('search_note_public','alpha'); -$search_note_private=GETPOST('search_note_private','alpha'); -$search_qty=GETPOST('search_qty','alpha'); -$search_import_key=GETPOST('search_import_key','alpha'); +$search_fk_user_creat=GETPOST('search_fk_user_creat','int'); +$search_fk_user_modif=GETPOST('search_fk_user_modif','int'); +$search_import_key=GETPOST('search_import_key','int'); @@ -114,12 +114,12 @@ if (empty($reshook)) /* object_prop_getpost_prop */ + $object->entity=GETPOST('entity','int'); $object->fk_product=GETPOST('fk_product','int'); $object->batch=GETPOST('batch','alpha'); - $object->note_public=GETPOST('note_public','alpha'); - $object->note_private=GETPOST('note_private','alpha'); - $object->qty=GETPOST('qty','alpha'); - $object->import_key=GETPOST('import_key','alpha'); + $object->fk_user_creat=GETPOST('fk_user_creat','int'); + $object->fk_user_modif=GETPOST('fk_user_modif','int'); + $object->import_key=GETPOST('import_key','int'); @@ -161,12 +161,12 @@ if (empty($reshook)) $error=0; + $object->entity=GETPOST('entity','int'); $object->fk_product=GETPOST('fk_product','int'); $object->batch=GETPOST('batch','alpha'); - $object->note_public=GETPOST('note_public','alpha'); - $object->note_private=GETPOST('note_private','alpha'); - $object->qty=GETPOST('qty','alpha'); - $object->import_key=GETPOST('import_key','alpha'); + $object->fk_user_creat=GETPOST('fk_user_creat','int'); + $object->fk_user_modif=GETPOST('fk_user_modif','int'); + $object->import_key=GETPOST('import_key','int'); @@ -262,11 +262,11 @@ if ($action == 'create') print '
    '."\n"; // print ''; // +print ''; print ''; print ''; -print ''; -print ''; -print ''; +print ''; +print ''; print ''; print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldentity").'
    '.$langs->trans("Fieldfk_product").'
    '.$langs->trans("Fieldbatch").'
    '.$langs->trans("Fieldnote_public").'
    '.$langs->trans("Fieldnote_private").'
    '.$langs->trans("Fieldqty").'
    '.$langs->trans("Fieldfk_user_creat").'
    '.$langs->trans("Fieldfk_user_modif").'
    '.$langs->trans("Fieldimport_key").'
    '."\n"; @@ -295,11 +295,11 @@ if (($id || $ref) && $action == 'edit') print ''."\n"; // print ''; // +print ''; print ''; print ''; -print ''; -print ''; -print ''; +print ''; +print ''; print ''; print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldentity").'
    '.$langs->trans("Fieldfk_product").'
    '.$langs->trans("Fieldbatch").'
    '.$langs->trans("Fieldnote_public").'
    '.$langs->trans("Fieldnote_private").'
    '.$langs->trans("Fieldqty").'
    '.$langs->trans("Fieldfk_user_creat").'
    '.$langs->trans("Fieldfk_user_modif").'
    '.$langs->trans("Fieldimport_key").'
    '; @@ -330,11 +330,11 @@ if ($id && (empty($action) || $action == 'view' || $action == 'delete')) print ''."\n"; // print ''; // +print ''; print ''; print ''; -print ''; -print ''; -print ''; +print ''; +print ''; print ''; print '
    '.$langs->trans("Label").'
    '.$langs->trans("Fieldentity").'$object->entity
    '.$langs->trans("Fieldfk_product").'$object->fk_product
    '.$langs->trans("Fieldbatch").'$object->batch
    '.$langs->trans("Fieldnote_public").'$object->note_public
    '.$langs->trans("Fieldnote_private").'$object->note_private
    '.$langs->trans("Fieldqty").'$object->qty
    '.$langs->trans("Fieldfk_user_creat").'$object->fk_user_creat
    '.$langs->trans("Fieldfk_user_modif").'$object->fk_user_modif
    '.$langs->trans("Fieldimport_key").'$object->import_key
    '; diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php index ded98827e31..a8fd68f6a34 100644 --- a/htdocs/product/stock/productlot_list.php +++ b/htdocs/product/stock/productlot_list.php @@ -20,7 +20,7 @@ * \file stock/productlot_list.php * \ingroup stock * \brief This file is an example of a php page - * Initialy built by build_class_from_table on 2016-05-17 10:33 + * Initialy built by build_class_from_table on 2016-05-17 12:22 */ //if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); @@ -59,12 +59,12 @@ $backtopage = GETPOST('backtopage'); $myparam = GETPOST('myparam','alpha'); +$search_entity=GETPOST('search_entity','int'); $search_fk_product=GETPOST('search_fk_product','int'); $search_batch=GETPOST('search_batch','alpha'); -$search_note_public=GETPOST('search_note_public','alpha'); -$search_note_private=GETPOST('search_note_private','alpha'); -$search_qty=GETPOST('search_qty','alpha'); -$search_import_key=GETPOST('search_import_key','alpha'); +$search_fk_user_creat=GETPOST('search_fk_user_creat','int'); +$search_fk_user_modif=GETPOST('search_fk_user_modif','int'); +$search_import_key=GETPOST('search_import_key','int'); $search_myfield=GETPOST('search_myfield'); @@ -109,11 +109,11 @@ if (($id > 0 || ! empty($ref)) && $action != 'add') // Definition of fields for list $arrayfields=array( +'t.entity'=>array('label'=>$langs->trans("Fieldentity"), 'checked'=>1), 't.fk_product'=>array('label'=>$langs->trans("Fieldfk_product"), 'checked'=>1), 't.batch'=>array('label'=>$langs->trans("Fieldbatch"), 'checked'=>1), -'t.note_public'=>array('label'=>$langs->trans("Fieldnote_public"), 'checked'=>1), -'t.note_private'=>array('label'=>$langs->trans("Fieldnote_private"), 'checked'=>1), -'t.qty'=>array('label'=>$langs->trans("Fieldqty"), 'checked'=>1), +'t.fk_user_creat'=>array('label'=>$langs->trans("Fieldfk_user_creat"), 'checked'=>1), +'t.fk_user_modif'=>array('label'=>$langs->trans("Fieldfk_user_modif"), 'checked'=>1), 't.import_key'=>array('label'=>$langs->trans("Fieldimport_key"), 'checked'=>1), @@ -152,11 +152,11 @@ include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers { +$search_entity=''; $search_fk_product=''; $search_batch=''; -$search_note_public=''; -$search_note_private=''; -$search_qty=''; +$search_fk_user_creat=''; +$search_fk_user_modif=''; $search_import_key=''; @@ -235,14 +235,15 @@ jQuery(document).ready(function() { $sql = "SELECT"; $sql.= " t.rowid,"; - $sql .= " t.tms,"; + $sql .= " t.entity,"; $sql .= " t.fk_product,"; $sql .= " t.batch,"; $sql .= " t.eatby,"; $sql .= " t.sellby,"; - $sql .= " t.note_public,"; - $sql .= " t.note_private,"; - $sql .= " t.qty,"; + $sql .= " t.datec,"; + $sql .= " t.tms,"; + $sql .= " t.fk_user_creat,"; + $sql .= " t.fk_user_modif,"; $sql .= " t.import_key"; @@ -257,11 +258,11 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab $sql.= " WHERE 1 = 1"; //$sql.= " WHERE u.entity IN (".getEntity('mytable',1).")"; +if ($search_entity) $sql.= natural_search("entity",$search_entity); if ($search_fk_product) $sql.= natural_search("fk_product",$search_fk_product); if ($search_batch) $sql.= natural_search("batch",$search_batch); -if ($search_note_public) $sql.= natural_search("note_public",$search_note_public); -if ($search_note_private) $sql.= natural_search("note_private",$search_note_private); -if ($search_qty) $sql.= natural_search("qty",$search_qty); +if ($search_fk_user_creat) $sql.= natural_search("fk_user_creat",$search_fk_user_creat); +if ($search_fk_user_modif) $sql.= natural_search("fk_user_modif",$search_fk_user_modif); if ($search_import_key) $sql.= natural_search("import_key",$search_import_key); @@ -306,11 +307,11 @@ if ($resql) $params=''; if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; +if ($search_entity != '') $params.= '&search_entity='.urlencode($search_entity); if ($search_fk_product != '') $params.= '&search_fk_product='.urlencode($search_fk_product); if ($search_batch != '') $params.= '&search_batch='.urlencode($search_batch); -if ($search_note_public != '') $params.= '&search_note_public='.urlencode($search_note_public); -if ($search_note_private != '') $params.= '&search_note_private='.urlencode($search_note_private); -if ($search_qty != '') $params.= '&search_qty='.urlencode($search_qty); +if ($search_fk_user_creat != '') $params.= '&search_fk_user_creat='.urlencode($search_fk_user_creat); +if ($search_fk_user_modif != '') $params.= '&search_fk_user_modif='.urlencode($search_fk_user_modif); if ($search_import_key != '') $params.= '&search_import_key='.urlencode($search_import_key); @@ -389,11 +390,11 @@ if ($search_import_key != '') $params.= '&search_import_key='.urlencode($sea // Fields title search print '
    '; - print ' 0 ? ' disabled':'').' value="'.(GETPOST('batch_number')?GETPOST('batch_number'):$pdluo->batch).'">'; // If form was opened for a specific pdluoid, field is disabled + if ($pdluoid > 0) + { + // If form was opened for a specific pdluoid, field is disabled + print ''; + print ''; + } + else + { + print ''; + } print '
    '.$langs->trans("l_eatby").''; From be251cd2b0f39299cd6a6b5dbba0171c56c05037 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 18:22:48 +0200 Subject: [PATCH 701/834] Add high risk sql at end --- htdocs/install/mysql/migration/3.9.0-4.0.0.sql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql index f34e52dafe6..136a7114eb0 100644 --- a/htdocs/install/mysql/migration/3.9.0-4.0.0.sql +++ b/htdocs/install/mysql/migration/3.9.0-4.0.0.sql @@ -430,8 +430,6 @@ ALTER TABLE llx_categorie_account ADD CONSTRAINT fk_categorie_account_fk_account -- Delete old deprecated field ALTER TABLE llx_product_stock DROP COLUMN pmp; --- VMYSQL4.1 ALTER TABLE llx_c_type_resource CHANGE COLUMN rowid rowid integer NOT NULL AUTO_INCREMENT; - ALTER TABLE llx_resource ADD COLUMN asset_number varchar(255) after ref; ALTER TABLE llx_resource ADD COLUMN datec datetime DEFAULT NULL; ALTER TABLE llx_resource ADD COLUMN date_valid datetime DEFAULT NULL; @@ -468,6 +466,9 @@ update llx_product_batch set batch = '000000' where batch = 'Undefined'; update llx_product_lot set batch = '000000' where batch = 'Undefined'; update llx_stock_mouvement set batch = '000000' where batch = 'Undefined'; --- At end +-- At end (higher risk of error) + +-- VMYSQL4.1 ALTER TABLE llx_c_type_resource CHANGE COLUMN rowid rowid integer NOT NULL AUTO_INCREMENT; ALTER TABLE llx_product_batch ADD UNIQUE INDEX uk_product_batch (fk_product_stock, batch); + From e2d3f4e27d02d84c9824a97620f421b6a3becd1b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:15:44 +0200 Subject: [PATCH 702/834] Supplier proposal was missing --- htdocs/langs/en_US/other.lang | 2 ++ htdocs/product/class/product.class.php | 37 ++++++++++++++++++++++++++ htdocs/product/stats/card.php | 23 ++++++++++------ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 6187293aefd..af0aea4c274 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -159,11 +159,13 @@ StatsByNumberOfEntities=Statistics in number of referring entities NumberOfProposals=Number of proposals on last 12 month NumberOfCustomerOrders=Number of customer orders on last 12 month NumberOfCustomerInvoices=Number of customer invoices on last 12 month +NumberOfSupplierProposals=Number of supplier proposals on last 12 month NumberOfSupplierOrders=Number of supplier orders on last 12 month NumberOfSupplierInvoices=Number of supplier invoices on last 12 month NumberOfUnitsProposals=Number of units on proposals on last 12 month NumberOfUnitsCustomerOrders=Number of units on customer orders on last 12 month NumberOfUnitsCustomerInvoices=Number of units on customer invoices on last 12 month +NumberOfUnitsSupplierProposals=Number of units on supplier proposals on last 12 month NumberOfUnitsSupplierOrders=Number of units on supplier orders on last 12 month NumberOfUnitsSupplierInvoices=Number of units on supplier invoices on last 12 month EMailTextInterventionAddedContact=A newintervention %s has been assigned to you. diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index e125d538fac..32a474d924b 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2311,6 +2311,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE f.rowid = d.fk_facture"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND f.fk_soc = s.rowid"; $sql.= " AND f.entity IN (".getEntity('facture', 1).")"; @@ -2343,6 +2344,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE f.rowid = d.fk_facture_fourn"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND f.fk_soc = s.rowid"; $sql.= " AND f.entity IN (".getEntity('facture_fourn', 1).")"; @@ -2375,6 +2377,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE p.rowid = d.fk_propal"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; $sql.= " AND p.fk_soc = s.rowid"; $sql.= " AND p.entity IN (".getEntity('propal', 1).")"; @@ -2386,6 +2389,38 @@ class Product extends CommonObject return $this->_get_stats($sql,$mode); } + /** + * Return nb of units or proposals in which product is included + * + * @param int $socid Limit count on a particular third party id + * @param string $mode 'byunit'=number of unit, 'bynumber'=nb of entities + * @param int $filteronproducttype 0=To filter on product only, 1=To filter on services only + * @return array <0 if KO, result[month]=array(valuex,valuey) where month is 0 to 11 + */ + function get_nb_propalsupplier($socid, $mode, $filteronproducttype=-1) + { + global $conf; + global $user; + + $sql = "SELECT sum(d.qty), date_format(p.date_valid, '%Y%m')"; + if ($mode == 'bynumber') $sql.= ", count(DISTINCT p.rowid)"; + $sql.= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."societe as s"; + if ($filteronproducttype >= 0) $sql.=", ".MAIN_DB_PREFIX."product as prod"; + if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + $sql.= " WHERE p.rowid = d.fk_supplier_proposal"; + if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; + if ($filteronproducttype >= 0) $sql.= " AND prod.rowid = d.fk_product AND prod.fk_product_type =".$filteronproducttype; + $sql.= " AND p.fk_soc = s.rowid"; + $sql.= " AND p.entity IN (".getEntity('propal', 1).")"; + if (!$user->rights->societe->client->voir && !$socid) $sql.= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " .$user->id; + if ($socid > 0) $sql.= " AND p.fk_soc = ".$socid; + $sql.= " GROUP BY date_format(p.date_valid,'%Y%m')"; + $sql.= " ORDER BY date_format(p.date_valid,'%Y%m') DESC"; + + return $this->_get_stats($sql,$mode); + } + /** * Return nb of units or orders in which product is included * @@ -2405,6 +2440,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE c.rowid = d.fk_commande"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND c.fk_soc = s.rowid"; $sql.= " AND c.entity IN (".getEntity('commande', 1).")"; @@ -2435,6 +2471,7 @@ class Product extends CommonObject if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE c.rowid = d.fk_commande"; if ($this->id > 0) $sql.= " AND d.fk_product =".$this->id; + else $sql.=" AND d.fk_product > 0"; if ($filteronproducttype >= 0) $sql.= " AND p.rowid = d.fk_product AND p.fk_product_type =".$filteronproducttype; $sql.= " AND c.fk_soc = s.rowid"; $sql.= " AND c.entity IN (".getEntity('commande_fournisseur', 1).")"; diff --git a/htdocs/product/stats/card.php b/htdocs/product/stats/card.php index 7003690e3c8..3a01d952acd 100644 --- a/htdocs/product/stats/card.php +++ b/htdocs/product/stats/card.php @@ -212,15 +212,20 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') 'propal' =>array('modulepart'=>'productstats_proposals', 'file' => $object->id.'/propal12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsProposals"):$langs->transnoentitiesnoconv("NumberOfProposals"))), + 'proposalssuppliers'=>array('modulepart'=>'productstats_proposalssuppliers', + 'file' => $object->id.'/proposalssuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', + 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierProposals"):$langs->transnoentitiesnoconv("NumberOfSupplierProposals"))), + 'orders' =>array('modulepart'=>'productstats_orders', 'file' => $object->id.'/orders12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerOrders"):$langs->transnoentitiesnoconv("NumberOfCustomerOrders"))), - 'invoices' =>array('modulepart'=>'productstats_invoices', - 'file' => $object->id.'/invoices12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', - 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerInvoices"):$langs->transnoentitiesnoconv("NumberOfCustomerInvoices"))), 'orderssuppliers'=>array('modulepart'=>'productstats_orderssuppliers', 'file' => $object->id.'/orderssuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierOrders"):$langs->transnoentitiesnoconv("NumberOfSupplierOrders"))), + + 'invoices' =>array('modulepart'=>'productstats_invoices', + 'file' => $object->id.'/invoices12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', + 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsCustomerInvoices"):$langs->transnoentitiesnoconv("NumberOfCustomerInvoices"))), 'invoicessuppliers'=>array('modulepart'=>'productstats_invoicessuppliers', 'file' => $object->id.'/invoicessuppliers12m'.((string) $type != '' ? '_type'.$type : '').'_'.$mode.'.png', 'label' => ($mode=='byunit'?$langs->transnoentitiesnoconv("NumberOfUnitsSupplierInvoices"):$langs->transnoentitiesnoconv("NumberOfSupplierInvoices"))), @@ -245,11 +250,12 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') } else { - if ($key == 'propal') $graph_data = $object->get_nb_propal($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'orders') $graph_data = $object->get_nb_order($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'invoices') $graph_data = $object->get_nb_vente($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'invoicessuppliers') $graph_data = $object->get_nb_achat($socid,$mode,((string) $type != '' ? $type : -1)); - if ($key == 'orderssuppliers') $graph_data = $object->get_nb_ordersupplier($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'propal') $graph_data = $object->get_nb_propal($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'orders') $graph_data = $object->get_nb_order($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'invoices') $graph_data = $object->get_nb_vente($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'proposalssuppliers') $graph_data = $object->get_nb_propalsupplier($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'invoicessuppliers') $graph_data = $object->get_nb_achat($socid,$mode,((string) $type != '' ? $type : -1)); + if ($key == 'orderssuppliers') $graph_data = $object->get_nb_ordersupplier($socid,$mode,((string) $type != '' ? $type : -1)); // TODO Save cachefile $graphfiles[$key]['file'] } @@ -291,6 +297,7 @@ if (! empty($id) || ! empty($ref) || GETPOST('id') == 'all') if ($graphfiles == 'propal' && ! $user->rights->propale->lire) continue; if ($graphfiles == 'order' && ! $user->rights->commande->lire) continue; if ($graphfiles == 'invoices' && ! $user->rights->facture->lire) continue; + if ($graphfiles == 'proposals_suppliers' && ! $user->rights->supplier_proposal->lire) continue; if ($graphfiles == 'invoices_suppliers' && ! $user->rights->fournisseur->facture->lire) continue; if ($graphfiles == 'orders_suppliers' && ! $user->rights->fournisseur->commande->lire) continue; From 7063b26018c5f87b9a8362eb8ecb9e4fb66017fd Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 17 May 2016 19:32:19 +0200 Subject: [PATCH 703/834] Debug 4.0 --- htdocs/product/stock/massstockmove.php | 4 ++-- htdocs/product/stock/replenish.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index c7b7ed41034..b8ec52efcab 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -378,7 +378,7 @@ print ''; - print ''; + print ''; print ''; print $formproduct->selectWarehouses($id_tw,'id_tw','',1); print '
    '.($nb[$status.$bool]?$nb[$status.$bool]:0).' '.$staticcontratligne->LibStatut($status,3,($bool?1:0)).'
    '.$staticcontratligne->LibStatut($status,0,($bool?1:0)).''.($nb[$status.$bool]?$nb[$status.$bool]:0).' '.$staticcontratligne->LibStatut($status,3,($bool?1:0)).'

    '.$langs->trans("MigrationFinished").'
    '; print ''.$langs->trans('Upgrade').': '; - print $langs->trans('MigrationReloadModule')." ".$mod->getName(); + print $langs->trans('MigrationReloadModule').' '.$mod->getName(); // We keep getName outside of trans because getName is already encoded/translated print ""; print "
    \n"; print '
    No Yes
    CUPS hostedYesYesNo
    ### Printers Many thermal receipt printers support ESC/POS to some degree. This driver has been known to work with: -- Bixolon SRP-350III -- Citizen CBM1000-II - EPOS TEP 220M - Epson TM-T88III - Epson TM-T88IV @@ -74,12 +129,9 @@ Many thermal receipt printers support ESC/POS to some degree. This driver has be - Epson TM-T70II - Epson TM-U220 - Epson FX-890 (requires `feedForm()` to release paper). -- Excelvan HOP-E58 (connect through powered hub) - Okipos 80 Plus III - P-822D -- P85A-401 (make unknown) - SEYPOS PRP-300 (Also marketed as TYSSO PRP-300) -- Silicon SP-201 / RP80USE - Star TSP-650 - Star TUP-592 - Xprinter XP-Q800 @@ -87,152 +139,16 @@ Many thermal receipt printers support ESC/POS to some degree. This driver has be - Zijang ZJ-5870 - Zijang ZJ-5890T (Marketed as POS 5890T) -If you use any other printer with this code, please [let us know](https://github.com/mike42/escpos-php/issues/new) so that it can be added to the list. +If you use any other printer with this code, please let me know so I can add it to the list. -## Basic usage - -### Include the library - -#### Composer -If you are using composer, then add `mike42/escpos-php` as a dependency: - -```` -composer require mike42/escpos-php -```` - -In this case, you would include composer's auto-loader at the top of your source files: - -```` - text("Hello World!\n"); -$printer -> cut(); -$printer -> close(); -``` - -Some examples are below for common interfaces. - -Communicate with a printer with an Ethernet interface using `netcat`: -```` -php hello-world.php | nc 10.x.x.x. 9100 -```` - -A USB local printer connected with `usblp` on Linux has a device file (Includes USB-parallel interfaces): -```` -php hello-world.php > /dev/usb/lp0 -```` - -A computer installed into the local `cups` server is accessed through `lp` or `lpr`: -```` -php hello-world.php > foo.txt -lpr -o raw -H localhost -P printer foo.txt -```` - -A local or networked printer on a Windows computer is mapped in to a file, and generally requires you to share the printer first: - -```` -php hello-world.php > foo.txt -net use LPT1 \\server\printer -copy foo.txt LPT1 -del foo.txt -``` - -If you have troubles at this point, then you should consult your OS and printer system documentation to try to find a working print command. - -### Using a PrintConnector - -To print receipts from PHP, use the most applicable [PrintConnector](https://github.com/mike42/escpos-php/tree/master/src/Mike42/Escpos/PrintConnectors) for your setup. The connector simply provides the plumbing to get data to the printer. - -For example, a `NetworkPrintConnector` accepts an IP address and port: - -````php -use Mike42\Escpos\PrintConnectors\NetworkPrintConnector; -use Mike42\Escpos\Printer; -$connector = new NetworkPrintConnector("10.x.x.x", 9100); -$printer = new Printer($connector); -try { - // ... Print stuff -} finally { - $printer -> close(); -} -```` - -While a serial printer might use: -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\Printer; -$connector = new FilePrintConnector("/dev/ttyS0"); -$printer = new Printer($connector); -``` - -For each OS/interface combination that's supported, there are examples in the compatibility section of how a `PrintConnector` would be constructed. If you can't get a `PrintConnector` to work, then be sure to include the working print command in bug. - -### Using a CapabilityProfile - -Support for commands and code pages varies between printer vendors and models. By default, the driver will accept UTF-8, and output commands that are suitable for Epson TM-series printers. - -When trying out a new brand of printer, it's a good idea to use the `SimpleCapabilityProfile`, which instructs the driver to avoid the use of advanced features (generally simpler image handling, ASCII-only text). - -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\CapabilityProfiles\SimpleCapabilityProfile; -$connector = new WindowsPrintConnector("smb://computer/printer"); -$printer = new Printer($connector, $profile); -``` - -As another example, Star-branded printers use different commands: - -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\CapabilityProfiles\StarCapabilityProfile; -$connector = new WindowsPrintConnector("smb://computer/printer"); -$printer = new Printer($connector, $profile); -``` - -Further developing this mechanism is a priority for future releases. - -### Tips & examples -On Linux, your printer device file will be somewhere like `/dev/lp0` (parallel), `/dev/usb/lp1` (USB), `/dev/ttyUSB0` (USB-Serial), `/dev/ttyS0` (serial). - -On Windows, the device files will be along the lines of `LPT1` (parallel) or `COM1` (serial). Use the `WindowsPrintConnector` to tap into system printing on Windows (eg. [Windows USB](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-usb.php), [SMB](https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php) or [Windows LPT](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-lpt.php)) - this submits print jobs via a queue rather than communicating directly with the printer. - -A complete real-world receipt can be found in the code of [Auth](https://github.com/mike42/Auth) in [ReceiptPrinter.php](https://github.com/mike42/Auth/blob/master/lib/misc/ReceiptPrinter.php). It includes justification, boldness, and a barcode. - -Other examples are located in the [example/](https://github.com/mike42/escpos-php/blob/master/example/) directory. - -## Available methods +Available methods +----------------- ### __construct(PrintConnector $connector, AbstractCapabilityProfile $profile) Construct new print object. Parameters: -- `PrintConnector $connector`: The PrintConnector to send data to. +- `PrintConnector $connector`: The PrintConnector to send data to. If not set, output is sent to standard output. - `AbstractCapabilityProfile $profile` Supported features of this printer. If not set, the DefaultCapabilityProfile will be used, which is suitable for Epson printers. See [example/interface/]("https://github.com/mike42/escpos-php/tree/master/example/interface/) for ways to open connections for different platforms and interfaces. @@ -243,7 +159,7 @@ Print a barcode. Parameters: - `string $content`: The information to encode. -- `int $type`: The barcode standard to output. If not specified, `Printer::BARCODE_CODE39` will be used. +- `int $type`: The barcode standard to output. If not specified, `Escpos::BARCODE_CODE39` will be used. Currently supported barcode standards are (depending on your printer): @@ -265,7 +181,7 @@ Cut the paper. Parameters: -- `int $mode`: Cut mode, either `Printer::CUT_FULL` or `Printer::CUT_PARTIAL`. If not specified, `Printer::CUT_FULL` will be used. +- `int $mode`: Cut mode, either `Escpos::CUT_FULL` or `Escpos::CUT_PARTIAL`. If not specified, `Escpos::CUT_FULL` will be used. - `int $lines`: Number of lines to feed before cutting. If not specified, 3 will be used. ### feed($lines) @@ -309,7 +225,7 @@ $printer -> graphics($img); See the [example/](https://github.com/mike42/escpos-php/blob/master/example/) folder for detailed examples. -The function [bitImage()](#bitimageescposimage-image-size) takes the same parameters, and can be used if your printer doesn't support the newer graphics commands. As an additional fallback, the `bitImageColumnFormat()` function is also provided. +The function [bitImage()](#bitimageescposimage-image-size) takes the same parameters, and can be used if your printer doesn't support the newer graphics commands. ### initialize() Initialize printer. This resets formatting back to the defaults. @@ -327,16 +243,16 @@ Parameters: Print the given data as a QR code on the printer. - `string $content`: The content of the code. Numeric data will be more efficiently compacted. -- `int $ec` Error-correction level to use. One of `Printer::QR_ECLEVEL_L` (default), `Printer::QR_ECLEVEL_M`, `Printer::QR_ECLEVEL_Q` or `Printer::QR_ECLEVEL_H`. Higher error correction results in a less compact code. +- `int $ec` Error-correction level to use. One of `Escpos::QR_ECLEVEL_L` (default), `Escpos::QR_ECLEVEL_M`, `Escpos::QR_ECLEVEL_Q` or `Escpos::QR_ECLEVEL_H`. Higher error correction results in a less compact code. - `int $size`: Pixel size to use. Must be 1-16 (default 3) -- `int $model`: QR code model to use. Must be one of `Printer::QR_MODEL_1`, `Printer::QR_MODEL_2` (default) or `Printer::QR_MICRO` (not supported by all printers). +- `int $model`: QR code model to use. Must be one of `Escpos::QR_MODEL_1`, `Escpos::QR_MODEL_2` (default) or `Escpos::QR_MICRO` (not supported by all printers). ### selectPrintMode($mode) Select print mode(s). Parameters: -- `int $mode`: The mode to use. Default is `Printer::MODE_FONT_A`, with no special formatting. This has a similar effect to running `initialize()`. +- `int $mode`: The mode to use. Default is `Escpos::MODE_FONT_A`, with no special formatting. This has a similar effect to running `initialize()`. Several MODE_* constants can be OR'd together passed to this function's `$mode` argument. The valid modes are: @@ -354,13 +270,6 @@ Parameters: - `int $height`: Height in dots. If not specified, 8 will be used. -### setColor($color) -Select print color - on printers that support multiple colors. - -Parameters: - -- `int $color`: Color to use. Must be either `Printer::COLOR_1` (default), or `Printer::COLOR_2` - ### setDoubleStrike($on) Turn double-strike mode on/off. @@ -380,14 +289,14 @@ Select font. Most printers have two fonts (Fonts A and B), and some have a third Parameters: -- `int $font`: The font to use. Must be either `Printer::FONT_A`, `Printer::FONT_B`, or `Printer::FONT_C`. +- `int $font`: The font to use. Must be either `Escpos::FONT_A`, `Escpos::FONT_B`, or `Escpos::FONT_C`. ### setJustification($justification) Select justification. Parameters: -- `int $justification`: One of `Printer::JUSTIFY_LEFT`, `Printer::JUSTIFY_CENTER`, or `Printer::JUSTIFY_RIGHT`. +- `int $justification`: One of `Escpos::JUSTIFY_LEFT`, `Escpos::JUSTIFY_CENTER`, or `Escpos::JUSTIFY_RIGHT`. ### setReverseColors($on) Set black/white reverse mode on or off. In this mode, text is printed white on a black background. @@ -409,7 +318,7 @@ Set underline for printed text. Parameters: -- `int $underline`: Either `true`/`false`, or one of `Printer::UNDERLINE_NONE`, `Printer::UNDERLINE_SINGLE` or `Printer::UNDERLINE_DOUBLE`. Defaults to `Printer::UNDERLINE_SINGLE`. +- `int $underline`: Either `true`/`false`, or one of `Escpos::UNDERLINE_NONE`, `Escpos::UNDERLINE_SINGLE` or `Escpos::UNDERLINE_DOUBLE`. Defaults to `Escpos::UNDERLINE_SINGLE`. ### text($str) Add text to the buffer. Text should either be followed by a line-break, or `feed()` should be called after this. @@ -418,46 +327,26 @@ Parameters: - `string $str`: The string to print. -# Further notes +Further notes +------------- Posts I've written up for people who are learning how to use receipt printers: * [What is ESC/POS, and how do I use it?](http://mike.bitrevision.com/blog/what-is-escpos-and-how-do-i-use-it), which documents the output of test.php. * [Setting up an Epson receipt printer](http://mike.bitrevision.com/blog/2014-20-26-setting-up-an-epson-receipt-printer) * [Getting a USB receipt printer working on Linux](http://mike.bitrevision.com/blog/2015-03-getting-a-usb-receipt-printer-working-on-linux) -# Development - -This code is MIT licensed, and you are encouraged to contribute any modifications back to the project. - -For development, it's suggested that you load `imagick` and `gd` `Xdebug` PHP modules, and install `composer` and `phpunit`. - -The tests are executed on [Travis CI](https://travis-ci.org/mike42/escpos-php) over versions of PHP from 5.3 up to 5.6, 7, and HHVM. Earlier versions of PHP are not supported. - -Fetch a copy of this code and load idependencies with composer: - - git clone https://github.com/mike42/escpos-php - cd escpos-php/ - composer install - -Execute unit tests via `phpunit`: - - phpunit --configuration test/phpunit.xml --coverage-text - -Pull requests and bug reports welcome. - - +- [ronisaha/php-esc-pos](https://github.com/ronisaha/php-esc-pos) - + diff --git a/htdocs/includes/mike42/escpos-php/autoload.php b/htdocs/includes/mike42/escpos-php/autoload.php deleted file mode 100644 index 7779486b824..00000000000 --- a/htdocs/includes/mike42/escpos-php/autoload.php +++ /dev/null @@ -1,26 +0,0 @@ -setBarcodeHeight ( 40 ); + +/* Text position */ +$printer->selectPrintMode ( Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH ); +$printer->text ( "Text position\n" ); +$printer->selectPrintMode (); +$hri = array ( + Escpos::BARCODE_TEXT_NONE => "No text", + Escpos::BARCODE_TEXT_ABOVE => "Above", + Escpos::BARCODE_TEXT_BELOW => "Below", + Escpos::BARCODE_TEXT_ABOVE | Escpos::BARCODE_TEXT_BELOW => "Both" +); +foreach ( $hri as $position => $caption ) { + $printer->text ( $caption . "\n" ); + $printer->setBarcodeTextPosition ( $position ); + $printer->barcode ( "012345678901", Escpos::BARCODE_JAN13 ); + $printer->feed (); +} + +/* Barcode types */ +$standards = array ( + Escpos::BARCODE_UPCA => array ( + "title" => "UPC-A", + "caption" => "Fixed-length numeric product barcodes.", + "example" => array ( + array ( + "caption" => "12 char numeric including (wrong) check digit.", + "content" => "012345678901" + ), + array ( + "caption" => "Send 11 chars to add check digit automatically.", + "content" => "01234567890" + ) + ) + ), + Escpos::BARCODE_UPCE => array ( + "title" => "UPC-E", + "caption" => "Fixed-length numeric compact product barcodes.", + "example" => array ( + array ( + "caption" => "6 char numeric - auto check digit & NSC", + "content" => "123456" + ), + array ( + "caption" => "7 char numeric - auto check digit", + "content" => "0123456" + ), + array ( + "caption" => "8 char numeric", + "content" => "01234567" + ), + array ( + "caption" => "11 char numeric - auto check digit", + "content" => "01234567890" + ), + array ( + "caption" => "12 char numeric including (wrong) check digit", + "content" => "012345678901" + ) + ) + ), + Escpos::BARCODE_JAN13 => array ( + "title" => "JAN13/EAN13", + "caption" => "Fixed-length numeric barcodes.", + "example" => array ( + array ( + "caption" => "12 char numeric - auto check digit", + "content" => "012345678901" + ), + array ( + "caption" => "13 char numeric including (wrong) check digit", + "content" => "0123456789012" + ) + ) + ), + Escpos::BARCODE_JAN8 => array ( + "title" => "JAN8/EAN8", + "caption" => "Fixed-length numeric barcodes.", + "example" => array ( + array ( + "caption" => "7 char numeric - auto check digit", + "content" => "0123456" + ), + array ( + "caption" => "8 char numeric including (wrong) check digit", + "content" => "01234567" + ) + ) + ), + Escpos::BARCODE_CODE39 => array ( + "title" => "Code39", + "caption" => "Variable length alphanumeric w/ some special chars.", + "example" => array ( + array ( + "caption" => "Text, numbers, spaces", + "content" => "ABC 012" + ), + array ( + "caption" => "Special characters", + "content" => "$%+-./" + ), + array ( + "caption" => "Extra char (*) Used as start/stop", + "content" => "*TEXT*" + ) + ) + ), + Escpos::BARCODE_ITF => array ( + "title" => "ITF", + "caption" => "Variable length numeric w/even number of digits,\nas they are encoded in pairs.", + "example" => array ( + array ( + "caption" => "Numeric- even number of digits", + "content" => "0123456789" + ) + ) + ), + Escpos::BARCODE_CODABAR => array ( + "title" => "Codabar", + "caption" => "Varaible length numeric with some allowable\nextra characters. ABCD/abcd must be used as\nstart/stop characters (one at the start, one\nat the end) to distinguish between barcode\napplications.", + "example" => array ( + array ( + "caption" => "Numeric w/ A A start/stop. ", + "content" => "A012345A" + ), + array ( + "caption" => "Extra allowable characters", + "content" => "A012$+-./:A" + ) + ) + ), + Escpos::BARCODE_CODE93 => array ( + "title" => "Code93", + "caption" => "Variable length- any ASCII is available", + "example" => array ( + array ( + "caption" => "Text", + "content" => "012abcd" + ) + ) + ), + Escpos::BARCODE_CODE128 => array ( + "title" => "Code128", + "caption" => "Variable length- any ASCII is available", + "example" => array ( + array ( + "caption" => "Code set A uppercase & symbols", + "content" => "{A" . "012ABCD" + ), + array ( + "caption" => "Code set B general text", + "content" => "{B" . "012ABCDabcd" + ), + array ( + "caption" => "Code set C compact numbers\n Sending chr(21) chr(32) chr(43)", + "content" => "{C" . chr ( 21 ) . chr ( 32 ) . chr ( 43 ) + ) + ) + ) +); +$printer->setBarcodeTextPosition ( Escpos::BARCODE_TEXT_BELOW ); +foreach ( $standards as $type => $standard ) { + $printer->selectPrintMode ( Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH ); + $printer->text ( $standard ["title"] . "\n" ); + $printer->selectPrintMode (); + $printer->text ( $standard ["caption"] . "\n\n" ); + foreach ( $standard ["example"] as $id => $barcode ) { + $printer->setEmphasis ( true ); + $printer->text ( $barcode ["caption"] . "\n" ); + $printer->setEmphasis ( false ); + $printer->text ( "Content: " . $barcode ["content"] . "\n" ); + $printer->barcode ( $barcode ["content"], $type ); + $printer->feed (); + } +} +$printer->cut (); +$printer->close (); + diff --git a/htdocs/includes/mike42/escpos-php/example/bit-image.php b/htdocs/includes/mike42/escpos-php/example/bit-image.php new file mode 100644 index 00000000000..f3a4054d98f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/bit-image.php @@ -0,0 +1,32 @@ + text("These example images are printed with the older\nbit image print command. You should only use\n\$p -> bitImage() if \$p -> graphics() does not\nwork on your printer.\n\n"); + + $printer -> bitImage($tux); + $printer -> text("Regular Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_WIDTH); + $printer -> text("Wide Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Tall Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Large Tux in correct proportion (bit image).\n"); +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} + +$printer -> cut(); +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php b/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php new file mode 100644 index 00000000000..a98bdbc96ae --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php @@ -0,0 +1,59 @@ + selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Implemented languages\n"); + $printer -> selectPrintMode(); + foreach($inputsOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + foreach($buffers as $buffer) { + $printer -> setPrintBuffer($buffer); + $printer -> text($str); + } + $printer -> setPrintBuffer($buffers[0]); + } + $printer -> feed(); + + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Works in progress\n"); + $printer -> selectPrintMode(); + foreach($inputsNotOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + foreach($buffers as $buffer) { + $printer -> setPrintBuffer($buffer); + $printer -> text($str); + } + $printer -> setPrintBuffer($buffers[0]); + } + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/character-encodings.php b/htdocs/includes/mike42/escpos-php/example/character-encodings.php new file mode 100644 index 00000000000..8a42ecd0847 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-encodings.php @@ -0,0 +1,58 @@ + selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Implemented languages\n"); + $printer -> selectPrintMode(); + foreach($inputsOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + $printer -> text($str); + } + $printer -> feed(); + + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Works in progress\n"); + $printer -> selectPrintMode(); + foreach($inputsNotOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + $printer -> text($str); + } + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/character-tables.php b/htdocs/includes/mike42/escpos-php/example/character-tables.php new file mode 100644 index 00000000000..1ce2c5c9f1e --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-tables.php @@ -0,0 +1,71 @@ + getSupportedCodePages(); +$first = true; // Print larger table for first code-page. +foreach($codePages as $table => $name) { + /* Change printer code page */ + $printer -> selectCharacterTable(255); + $printer -> selectCharacterTable($table); + /* Select & print a label for it */ + $label = $name; + if($name === false) { + $label= " (not matched to iconv table)"; + } + $printer -> setEmphasis(true); + $printer -> textRaw("Table $table: $label\n"); + $printer -> setEmphasis(false); + if($name === false && !$verbose) { + continue; // Skip non-recognised + } + /* Print a table of available characters (first table is larger than subsequent ones */ + if($first) { + $first = false; + compactCharTable($printer, 1, true); + } else { + compactCharTable($printer); + } +} +$printer -> cut(); +$printer -> close(); + +function compactCharTable($printer, $start = 4, $header = false) { + /* Output a compact character table for the current encoding */ + $chars = str_repeat(' ', 256); + for($i = 0; $i < 255; $i++) { + $chars[$i] = ($i > 32 && $i != 127) ? chr($i) : ' '; + } + if($header) { + $printer -> setEmphasis(true); + $printer -> textRaw(" 0123456789ABCDEF0123456789ABCDEF\n"); + $printer -> setEmphasis(false); + } + for($y = $start; $y < 8; $y++) { + $printer -> setEmphasis(true); + $printer -> textRaw(strtoupper(dechex($y * 2)) . " "); + $printer -> setEmphasis(false); + $printer -> textRaw(substr($chars, $y * 32, 32) . "\n"); + } +} diff --git a/htdocs/includes/mike42/escpos-php/example/demo.php b/htdocs/includes/mike42/escpos-php/example/demo.php new file mode 100644 index 00000000000..1c99e5cc880 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/demo.php @@ -0,0 +1,167 @@ + + */ +require_once(dirname(__FILE__) . "/../Escpos.php"); +$printer = new Escpos(); + +/* Initialize */ +$printer -> initialize(); + +/* Text */ +$printer -> text("Hello world\n"); +$printer -> cut(); + +/* Line feeds */ +$printer -> text("ABC"); +$printer -> feed(7); +$printer -> text("DEF"); +$printer -> feedReverse(3); +$printer -> text("GHI"); +$printer -> feed(); +$printer -> cut(); + +/* Font modes */ +$modes = array( + Escpos::MODE_FONT_B, + Escpos::MODE_EMPHASIZED, + Escpos::MODE_DOUBLE_HEIGHT, + Escpos::MODE_DOUBLE_WIDTH, + Escpos::MODE_UNDERLINE); +for($i = 0; $i < pow(2, count($modes)); $i++) { + $bits = str_pad(decbin($i), count($modes), "0", STR_PAD_LEFT); + $mode = 0; + for($j = 0; $j < strlen($bits); $j++) { + if(substr($bits, $j, 1) == "1") { + $mode |= $modes[$j]; + } + } + $printer -> selectPrintMode($mode); + $printer -> text("ABCDEFGHIJabcdefghijk\n"); +} +$printer -> selectPrintMode(); // Reset +$printer -> cut(); + +/* Underline */ +for($i = 0; $i < 3; $i++) { + $printer -> setUnderline($i); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setUnderline(0); // Reset +$printer -> cut(); + +/* Cuts */ +$printer -> text("Partial cut\n(not available on all printers)\n"); +$printer -> cut(Escpos::CUT_PARTIAL); +$printer -> text("Full cut\n"); +$printer -> cut(Escpos::CUT_FULL); + +/* Emphasis */ +for($i = 0; $i < 2; $i++) { + $printer -> setEmphasis($i == 1); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setEmphasis(false); // Reset +$printer -> cut(); + +/* Double-strike (looks basically the same as emphasis) */ +for($i = 0; $i < 2; $i++) { + $printer -> setDoubleStrike($i == 1); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setDoubleStrike(false); +$printer -> cut(); + +/* Fonts (many printers do not have a 'Font C') */ +$fonts = array( + Escpos::FONT_A, + Escpos::FONT_B, + Escpos::FONT_C); +for($i = 0; $i < count($fonts); $i++) { + $printer -> setFont($fonts[$i]); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setFont(); // Reset +$printer -> cut(); + +/* Justification */ +$justification = array( + Escpos::JUSTIFY_LEFT, + Escpos::JUSTIFY_CENTER, + Escpos::JUSTIFY_RIGHT); +for($i = 0; $i < count($justification); $i++) { + $printer -> setJustification($justification[$i]); + $printer -> text("A man a plan a canal panama\n"); +} +$printer -> setJustification(); // Reset +$printer -> cut(); + +/* Barcodes - see barcode.php for more detail */ +$printer -> setBarcodeHeight(80); +$printer->setBarcodeTextPosition ( Escpos::BARCODE_TEXT_BELOW ); +$printer -> barcode("9876"); +$printer -> feed(); +$printer -> cut(); + +/* Graphics - this demo will not work on some non-Epson printers */ +try { + $logo = new EscposImage("resources/escpos-php.png"); + $imgModes = array( + Escpos::IMG_DEFAULT, + Escpos::IMG_DOUBLE_WIDTH, + Escpos::IMG_DOUBLE_HEIGHT, + Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + ); + foreach($imgModes as $mode) { + $printer -> graphics($logo, $mode); + } +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} +$printer -> cut(); + +/* Bit image */ +try { + $logo = new EscposImage("resources/escpos-php.png"); + $imgModes = array( + Escpos::IMG_DEFAULT, + Escpos::IMG_DOUBLE_WIDTH, + Escpos::IMG_DOUBLE_HEIGHT, + Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + ); + foreach($imgModes as $mode) { + $printer -> bitImage($logo, $mode); + } +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} +$printer -> cut(); + +/* QR Code - see also the more in-depth demo at qr-code.php */ +$testStr = "Testing 123"; +$models = array( + Escpos::QR_MODEL_1 => "QR Model 1", + Escpos::QR_MODEL_2 => "QR Model 2 (default)", + Escpos::QR_MICRO => "Micro QR code\n(not supported on all printers)"); +foreach($models as $model => $name) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, 3, $model); + $printer -> text("$name\n"); + $printer -> feed(); +} +$printer -> cut(); + +/* Pulse */ +$printer -> pulse(); + +/* Always close the printer! On some PrintConnectors, no actual + * data is sent until the printer is closed. */ +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/graphics.php b/htdocs/includes/mike42/escpos-php/example/graphics.php new file mode 100644 index 00000000000..7650b6b9a08 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/graphics.php @@ -0,0 +1,32 @@ + graphics($tux); + $printer -> text("Regular Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_WIDTH); + $printer -> text("Wide Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Tall Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Large Tux in correct proportion.\n"); + + $printer -> cut(); +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} + +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/interface/README.md b/htdocs/includes/mike42/escpos-php/example/interface/README.md new file mode 100644 index 00000000000..96fe2eed2fb --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/README.md @@ -0,0 +1,8 @@ +Interfaces +---------- +This directory contains boilerpalte code to show you how to open a print connector +to printers which are connected in different ways. + +To get a list of supported interfaces and operating systems, see the main README.md file for the project. + +If you have a printer interface with no example, and you want to help put one together, then please lodge a request on the bug tracker: https://github.com/mike42/escpos-php/issues diff --git a/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php b/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php new file mode 100644 index 00000000000..bfea443903c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php @@ -0,0 +1,22 @@ + text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php b/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php new file mode 100644 index 00000000000..9e1d96b91c0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php @@ -0,0 +1,33 @@ + text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/interface/smb.php b/htdocs/includes/mike42/escpos-php/example/interface/smb.php new file mode 100644 index 00000000000..40988f553ee --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/smb.php @@ -0,0 +1,51 @@ + testfile + * ## If you need authentication, use "net use" to hook up the printer: + * # net use "\\computername\Receipt Printer" /user:Guest + * # net use "\\computername\Receipt Printer" /user:Bob secret + * # net use "\\computername\Receipt Printer" /user:workgroup\Bob secret + * copy testfile "\\computername\Receipt Printer" + * del testfile + * + * GNU/Linux: + * # No authentication + * echo "Hello World" | smbclient "//computername/Receipt Printer" -c "print -" -N + * # Guest login + * echo "Hello World" | smbclient "//computername/Receipt Printer" -U Guest -c "print -" -N + * # Basic username/password + * echo "Hello World" | smbclient "//computername/Receipt Printer" secret -U "Bob" -c "print -" + * # Including domain name + * echo "Hello World" | smbclient "//computername/Receipt Printer" secret -U "workgroup\\Bob" -c "print -" + */ +try { + // Enter the share name for your printer here, as a smb:// url format + $connector = null; + //$connector = new WindowsPrintConnector("smb://computername/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://Guest@computername/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://FooUser:secret@computername/workgroup/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://User:secret@computername/Receipt Printer"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php b/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php new file mode 100644 index 00000000000..76294425cdc --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php @@ -0,0 +1,30 @@ + LPT1 + */ +try { + $connector = null; + //$connector = new WindowsPrintConnector("LPT1"); + + // A FilePrintConnector will also work, but on non-Windows systems, writes + // to an actual file called 'LPT1' rather than giving a useful error. + // $connector = new FilePrintConnector("LPT1"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php b/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php new file mode 100644 index 00000000000..d4b550e2f93 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php @@ -0,0 +1,32 @@ + testfile + * copy testfile "\\%COMPUTERNAME%\Receipt Printer" + * del testfile + */ +try { + // Enter the share name for your USB printer here + $connector = null; + //$connector = new WindowsPrintConnector("Receipt Printer"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/print-from-html.php b/htdocs/includes/mike42/escpos-php/example/print-from-html.php new file mode 100644 index 00000000000..f0751d7a4ae --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/print-from-html.php @@ -0,0 +1,53 @@ + 225 below) and printing w/ Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + */ +try { + /* Set up command */ + $source = "http://en.m.wikipedia.org/wiki/ESC/P"; + $width = 550; + $dest = tempnam(sys_get_temp_dir(), 'escpos') . ".png"; + $cmd = sprintf("wkhtmltoimage -n -q --width %s %s %s", + escapeshellarg($width), + escapeshellarg($source), + escapeshellarg($dest)); + + /* Run wkhtmltoimage */ + ob_start(); + system($cmd); // Can also use popen() for better control of process + $outp = ob_get_contents(); + ob_end_clean(); + if(!file_exists($dest)) { + throw new Exception("Command $cmd failed: $outp"); + } + + /* Load up the image */ + try { + $img = new EscposImage($dest); + } catch(Exception $e) { + unlink($dest); + throw $e; + } + unlink($dest); + + /* Print it */ + $printer = new Escpos(); // Add connector for your printer here. + $printer -> bitImage($img); // bitImage() seems to allow larger images than graphics() on the TM-T20. + $printer -> cut(); + $printer -> close(); +} catch(Exception $e) { + echo $e -> getMessage(); +} + diff --git a/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php b/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php new file mode 100644 index 00000000000..ba6a4c67a96 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php @@ -0,0 +1,71 @@ + graphics($page); + } + $printer -> cut(); + $printer -> close(); +} catch(Exception $e) { + /* + * loadPdf() throws exceptions if files or not found, or you don't have the + * imagick extension to read PDF's + */ + echo $e -> getMessage() . "\n"; + exit(0); +} + + +/* + * 2: Speed up printing by roughly halving the resolution, and printing double-size. + * This gives a 75% speed increase at the expense of some quality. + * + * Reduce the page width further if necessary: if it extends past the printing area, your prints will be very slow. + */ +$printer = new Escpos(); +$pdf = 'resources/document.pdf'; +$pages = EscposImage::loadPdf($pdf, 260); +foreach($pages as $page) { + $printer -> graphics($page, Escpos::IMG_DOUBLE_HEIGHT | Escpos::IMG_DOUBLE_WIDTH); +} +$printer -> cut(); +$printer -> close(); + +/* + * 3: PDF printing still too slow? If you regularly print the same files, serialize & compress your + * EscposImage objects (after printing[1]), instead of throwing them away. + * + * (You can also do this to print logos on computers which don't have an + * image processing library, by preparing a serialized version of your logo on your PC) + * + * [1]After printing, the pixels are loaded and formatted for the print command you used, so even a raspberry pi can print complex PDF's quickly. + */ +$printer = new Escpos(); +$pdf = 'resources/document.pdf'; +$ser = 'resources/document.z'; +if(!file_exists($ser)) { + $pages = EscposImage::loadPdf($pdf); +} else { + $pages = unserialize(gzuncompress(file_get_contents($ser))); +} + +foreach($pages as $page) { + $printer -> graphics($page); +} +$printer -> cut(); +$printer -> close(); + +if(!file_exists($ser)) { + file_put_contents($ser, gzcompress(serialize($pages))); +} diff --git a/htdocs/includes/mike42/escpos-php/example/qr-code.php b/htdocs/includes/mike42/escpos-php/example/qr-code.php new file mode 100644 index 00000000000..dbf21aaedf4 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/qr-code.php @@ -0,0 +1,81 @@ + qrCode($testStr); +$printer -> text("Most simple example\n"); +$printer -> feed(); + +// Demo that alignment is the same as text +$printer -> setJustification(Escpos::JUSTIFY_CENTER); +$printer -> qrCode($testStr); +$printer -> text("Same example, centred\n"); +$printer -> setJustification(); +$printer -> feed(); + +// Demo of numeric data being packed more densly +title($printer, "Data encoding\n"); +$test = array( + "Numeric" => "0123456789012345678901234567890123456789", + "Alphanumeric" => "abcdefghijklmnopqrstuvwxyzabcdefghijklmn", + "Binary" => str_repeat("\0", 40)); +foreach($test as $type => $data) { + $printer -> qrCode($data); + $printer -> text("$type\n"); + $printer -> feed(); +} + +// Demo of error correction +title($printer, "Error correction\n"); +$ec = array( + Escpos::QR_ECLEVEL_L => "L", + Escpos::QR_ECLEVEL_M => "M", + Escpos::QR_ECLEVEL_Q => "Q", + Escpos::QR_ECLEVEL_H => "H"); +foreach($ec as $level => $name) { + $printer -> qrCode($testStr, $level); + $printer -> text("Error correction $name\n"); + $printer -> feed(); +} + +// Change size +title($printer, "Pixel size\n"); +$sizes = array( + 1 => "(minimum)", + 2 => "", + 3 => "(default)", + 4 => "", + 5 => "", + 10 => "", + 16 => "(maximum)"); +foreach($sizes as $size => $label) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, $size); + $printer -> text("Pixel size $size $label\n"); + $printer -> feed(); +} + +// Change model +title($printer, "QR model\n"); +$models = array( + Escpos::QR_MODEL_1 => "QR Model 1", + Escpos::QR_MODEL_2 => "QR Model 2 (default)", + Escpos::QR_MICRO => "Micro QR code\n(not supported on all printers)"); +foreach($models as $model => $name) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, 3, $model); + $printer -> text("$name\n"); + $printer -> feed(); +} + +// Cut & close +$printer -> cut(); +$printer -> close(); + +function title(Escpos $printer, $str) { + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text($str); + $printer -> selectPrintMode(); +} diff --git a/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php b/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php new file mode 100644 index 00000000000..8322c09c750 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php @@ -0,0 +1,96 @@ + setJustification(Escpos::JUSTIFY_CENTER); +$printer -> graphics($logo); + +/* Name of shop */ +$printer -> selectPrintMode(Escpos::MODE_DOUBLE_WIDTH); +$printer -> text("ExampleMart Ltd.\n"); +$printer -> selectPrintMode(); +$printer -> text("Shop No. 42.\n"); +$printer -> feed(); + +/* Title of receipt */ +$printer -> setEmphasis(true); +$printer -> text("SALES INVOICE\n"); +$printer -> setEmphasis(false); + +/* Items */ +$printer -> setJustification(Escpos::JUSTIFY_LEFT); +$printer -> setEmphasis(true); +$printer -> text(new item('', '$')); +$printer -> setEmphasis(false); +foreach($items as $item) { + $printer -> text($item); +} +$printer -> setEmphasis(true); +$printer -> text($subtotal); +$printer -> setEmphasis(false); +$printer -> feed(); + +/* Tax and total */ +$printer -> text($tax); +$printer -> selectPrintMode(Escpos::MODE_DOUBLE_WIDTH); +$printer -> text($total); +$printer -> selectPrintMode(); + +/* Footer */ +$printer -> feed(2); +$printer -> setJustification(Escpos::JUSTIFY_CENTER); +$printer -> text("Thank you for shopping at ExampleMart\n"); +$printer -> text("For trading hours, please visit example.com\n"); +$printer -> feed(2); +$printer -> text($date . "\n"); + +/* Cut the receipt and open the cash drawer */ +$printer -> cut(); +$printer -> pulse(); + +$printer -> close(); + +/* A wrapper to do organise item names & prices into columns */ +class item { + private $name; + private $price; + private $dollarSign; + + public function __construct($name = '', $price = '', $dollarSign = false) { + $this -> name = $name; + $this -> price = $price; + $this -> dollarSign = $dollarSign; + } + + public function __toString() { + $rightCols = 10; + $leftCols = 38; + if($this -> dollarSign) { + $leftCols = $leftCols / 2 - $rightCols / 2; + } + $left = str_pad($this -> name, $leftCols) ; + + $sign = ($this -> dollarSign ? '$ ' : ''); + $right = str_pad($sign . $this -> price, $rightCols, ' ', STR_PAD_LEFT); + return "$left$right\n"; + } +} +?> diff --git a/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc b/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc new file mode 100644 index 00000000000..caca76c4479 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc @@ -0,0 +1,35 @@ + "Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.\n", + "German" => "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.\n", + "Greek" => "Ξεσκεπάζω την ψυχοφθόρα βδελυγμία\n", + "English" => "The quick brown fox jumps over the lazy dog.\n", + "Spanish" => "El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n", + "French" => "Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë au delà des îles, près du mälström où brûlent les novæ.\n", + "Irish Gaelic" => "D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.\n", + "Hungarian" => "Árvíztűrő tükörfúrógép.\n", + "Icelandic" => "Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa.\n", + "Latvian" => "Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus.\n", + "Polish" => "Pchnąć w tę łódź jeża lub ośm skrzyń fig.\n", + "Russian" => "В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!\n", + "Turkish" => "Pijamalı hasta, yağız şoföre çabucak güvendi.\n", + "Japanese (Katakana half-width)" => implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウイノオクヤマ ケフコエテ アサキユメミシ エヒモセスン")) . "\n" + ); + +/* + * These strings are not expected to print correctly, if at all, even on an Epson printer. This is due to a mix of + * escpos driver, printer, and PHP language support issues. + * + * They are included here as a collection of things not yet implemented. + */ +$inputsNotOk = array( + "Thai (No character encoder available)" => "นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ\n", + "Japanese (Hiragana)" => implode("\n", array("いろはにほへとちりぬるを", " わかよたれそつねならむ", "うゐのおくやまけふこえて", "あさきゆめみしゑひもせす")) . "\n", + "Japanese (Katakana full-width)" => implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン")) . "\n", + "Arabic (RTL not supported, encoding issues)" => "صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ" . "\n", + "Hebrew (RTL not supported, line break issues)" => "דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה" . "\n" + ); + diff --git a/htdocs/includes/mike42/escpos-php/example/resources/document.odt b/htdocs/includes/mike42/escpos-php/example/resources/document.odt new file mode 100644 index 0000000000000000000000000000000000000000..4bbd7f98b654dd1f8086589f22bfb9ed7ecb4c9a GIT binary patch literal 39751 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%0DXC4u*ZB5o$LxKAV8c6QUjWTfwW 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 literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/example/resources/document.pdf b/htdocs/includes/mike42/escpos-php/example/resources/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..93a702337f1b09cfcb96ddce75a0c6421b69251e GIT binary patch literal 24389 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 RuDa7m%-5OL40^FJe*+_L<7)r_ literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png new file mode 100644 index 0000000000000000000000000000000000000000..3c74d2d57a29e0398f706efd0e2f4531c036584a GIT binary patch literal 1941 zcmV;G2Wt3004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv0063uBQgL0010qNS#tmY3ljhU3ljkVnw%H_00wqRL_t(o!|j=SP?Xmh#(&>l zSaz3-7a$0t#I+JdEt*SdVzto;>J%@jY1LGl#3|#nbtcX8MGaPGe0QlGv(= zv6I%pF&Y)r*2Y2{gP101yfEOUTx>vVKtW`e{ht282Q2JjVw)++4A0D-J?Gu$yyw~X zob#Tu-@*XC05Gg~FH?!>Hx;$KgV`-6+t`~hD7s&iG;#GPxm*Q@WpPDCueVP{{UD}* z4bj-3TMP!N`1&O>33SH5)@|&MsPr7y_CI*}%rPKlK+NY@7_45h{Pl*Tx1p6kP(mEb8BZyH>(g5GH_9Bw zu%^NU+EK6J!^BLd7?TQuOkph<WCW9Kb?&t8UeAG6p+8i(*Pj$abX zbl#(xS$qTd7&wTJ*U2EA06}Zv3meCIkz)*E!p+6xa)T0LSjaFIvxjrcV=0MjpcoI& zk&ekks!8SvMzWd_lrn-R$zTyXC?uOD*ezcM6RBhj%NWK(qyawi7>dbQt`Nm<853zU zGcCHgSe-D{jbJZpi|*AEQbb12`oA?n>lBWF=4ffiC~L(#42l?;j&BrcyBSs}XZO1I z*!rlun8Wx8-fi5Er`ITbOw%VJid7NZM1q$!5t%kt_R-^(#oWpX(ihXXMSj0vKulkV zux6U5U@}*!AcJyTjOATiWKu;2XBkZ;6R~lWcJ8DKA17($F3vHE(_G`L#BdxRHKcK# z%iK*Z!%5=rB=axoxRXm%lSLEpr1c>xB+s1Cd|lJq%2A0@HAzKEQl^SDU4PSDl_*y= zs@Hg>sZ{fIM0Y7!NAyj-r70Ssay97z73pCeP_Dcx(sy-0(xWQW5=Ci;@)V_QTB%pU z@=Szj>bR?PByK+eO#JvU30Ss{dq|+G9l(!h$Fyo#ER%Npn6z6bQD-}7m1fw;pRi^O ztNe7CC4AF%_xv;-K35BPK+J%c&qR#6N`;g?84CG<(#=qhPs3TE%06hFBbUaNs2tI|g2>?I8{aZXAFcCxc1GO+2^ZCJs;7$er7N zTJ*j*yfXo4riL+Gra59ogoTO972|5@ZZ@q;j4%?%*QBsOCDExH&@# zmq_9wP29m{5*b7#L%2Z`!)d0GR9cDdNV}}#r)A8fv0GG+Yv=zLH$4ve4+CPpXku(! zU^guQnEaJ8%YKDj)LZrUIBXsLZS3c)Wv%8-S^*FxUHzKa-PP|T2dxJx0g5?eSue7O zmI(2f=4sxj8FDLMEi$!DiMm}yk}m5pxwJral1epBiF!?@+VreqG)-qDozqNt^^~ql zDpZO_>(7!J^+UO}NFPZ$u1OlKO$sWYjf&S-^ns*W&6ivG`bpPr>JsBx>n zo0J%&kb}4DbuE*XPg|3`~WAjZRg`f z8CiH(@A10>Y#@eA-j0`1!#tw-5iOkPDP|O#nF>G@kMkUf0HpIWOR)h4^V!Te0ODB7 zYAc&hW-B=WI9Sea7zRKp1>{*r&1_yF3ji-aV-2wYjAtuz0C2K`U(oA6l%&87Zp#T5?8FWQhbVF}#ZDnqB07G(R zVRU6=Aa`kWXdp*PO;A^X4i^9b03~!qSaf7zbY(hYa%Ew3WdJfTF*PkPGc7SRR5CI; zH8DChGb=GMIxsLILPgX7001R)MObuXVRU6WZEs|0W_bWIFflbPFf%PNG*mJ$Ix;mn bG%zbMF*-0X?$PJj00000NkvXXu0mjfgfCmX literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png new file mode 100644 index 0000000000000000000000000000000000000000..feee2d71cc280655b1765f2689bbed8299ba394e GIT binary patch literal 7871 zcmch6XH-*9*Ka@p1w@f19qGMpsogHQ(@|4Yq*4x#>Zj_#M1(@&aS>MbMg zOx$T-5&pc<^Swhz)bblkf9w?-+3iGHT+i=O|KI-%m8*yPZEPoi7>55*KReO<+t6-S z(HQ*;J>{Ypt6|Bk`(L;7UIDni3FDa$ar%aBglwB^*{_w^&H?eCKqAHMb% zI1m4-xfH4-q=^OoLR&;=_#INko0gUknu9a*8sWE%DV_+9j|s6dD~6xIxmgV>;ZBme zv(y~>zCm}cl)dXVV%nn!@-ntTP-lcwUd&9-*DA;b0TWH5`W4r#9OkE;Y8602QL%p; zVkK)6RzlgYvKa3qXVyN}9I$#{?JoAvqM2FdwL>PGjpP8GIAV$FQl0o8Q*(zbJu01v z@r?Hl@s3su59P8!#rr}+B;k-~HG75s%_kc*j)j?8D+@n8_%C8BGEd2J9794}v(Gm* zn@|E%cn^V~mj3tiDZCQ}laf1%V_wp~0nZ_#-`CmMrCyYF9!yzLp|#NBo_DU8_`;B^ ze^S}CTIFdh^`4=27Ml|y9eozI#?-^=b(0R}RJy|McKc9I?@BHH;Q?@i3Ky7HEXT!J zcZ4OLwsGnG#`P?IRNMP*KeD}kB)Ov4m;hvW#Vd5f@2Ks$JAZ!EcmB>JJ3`U@+8mcU z2bB@g5^=0tGj=!S6nt#ZSrZVG7xtWS_W?$s?Et@9oSn}z`Ejtlu!5uzrPoLqlI2<5 zD&I7u>sj!P+s76Pa^@+(U*CAFsb|dV>-B2NN;=lF^}VQKv+)>WXVQG6wolqpbc+<+ zX~;@y9s;U)IlWICW6RTpC4{v*l_K82+QzO)$=lLysWh?ViI)f_)jC_%p-r%`f+=ziBi<<+F8Q0!>_rKXSXAR!^d8)1?{Lz zl$@<+{9$O>{Eju+h6sKSSm7s-*uB4O4)yd=FSu56zdiF+2`&2`u-93sNT%l+yv86* zB}t;H^kfH&b|Cb97W-nORFOVhzN9YjD}IJeCmo@^d< zB4apl6zyMojGEs=Mym>~M2hZjbRK6lbUKeIiwnG3^F~&|@v%&d+VF zVChiAn$$ldumY#vg3+3i~fFrZ5UN7cYN82$%7P~#Ip-c8OWU0dYwIvD#2uxGCG*rNy1=nFgbL_G1zuHz9 zX}Zo$kiKt3Oq<(poGS7Qy6M{e@`m zyKufEVV9?N3p}1h&@bXifEs@4_p6aHKpx`EB3JtQ-UAt$r<&dln9Rgm@2Y$6$u43D zh_Kw4y}!BZkrbPLCobV(B#O#qP^GtcHJ-S@L}33o86TyD8vY-ACr&CVX>!GY=af^KO5ortjo-Zt$`D&hpEHrF09! z2Xk}lIdyFBr4+1IV{--UD2)B>+jEa;R2l<{cCYey$OCNE2IRx)*=T_2>ie~#Hcy0p z&yyzeTqvG+2h1v)RG0h=5oq!E9yE6Xqo7=@;*bkii9AdG=F}~v{b98|s4eC9k#Ortp@;c4N#%HGLH}DnT{gr$N$B{z z;fHjfd;8agY1X=6>YMZ0Jdwq1XA@gr6|4qt>n9M)S=Wyt22=A<_8;KogY?3C!ki_% zlRoThQ{&Fd&@18o4?b@Sg2rvy#{$enq)E)`KZG873+!o^c(4>rR>dY9rlsx0R)596 zUizCn3h&%|mDlg5tNMh(f3#`6@Spte>jZO$VU;@hf}2Ohq=N~`AC3_sx)d%)eSyCc zsoT(qx#!uo2MiQ74YE!X3lCo_wM-Yj!%D&y!s+nn>cnM-;pn?3I2>gU^)* zn`4GHyDZOkX!fEkA7|`|y-I#@U}r4V6cXXO4i=WQPa91*ib#FcB9+UAkzN_5t`P63fm8>;L zBrNn-C}e(}XR{McBi=ucqBneptNb`I^37+SmBSFF;Ig@Yd!feMHBbo4tVV zYpn9IG^=*KExl0uZC@JNE}(nRLK2a@k*->lmF*C4FPTQ5ilYdqC`e>8BSHguSd2sX z%vDuSvnRY!O^1VKk!~8U5{TyV?(!`OPe9k(+_JF~Tk`DNn990F50yulC)J*%1p_KW z-x$9u^|*8B(-=Wi7Dd{iKbX9vbEc!rLt4XIQ$?=urhN%!O_NVm?MPZRty+qW`}C8G zz*x@F*~&5y6A7GVjARJ97n&?~z>iJk?T^s}+55A`#od3#&8&Bz2`@&^k_Zu%RaP;x#^)BxB+%#%9y=|a$JAgy`_ck=Kj5Y#uQSX5sQ`ZC9f73-Y&wi`RQ;Ak>RBA|U4^WeMkPE)>Qq8Z$I zP3`uiX{F*{n^A!SGr>t+NU2KVKeTi!T#mkE|GWoO+i(~E-_St#RK@PyT|~Ean^s_W zWvHL@c*(99Ve1*4R(lX}#5ZEW+@tbjB+i&dtcYea&QVfC#pw~`3vcX%4DQ72L^`v( zHmMM&i1{$)C{uLr*(oy)Er&i`;4NG4i{EWn?+aS8N0HKM96u(hc@`YrGE-RH4rWkh zs|26-3&-`}1iW_RU@G=ma)PxU0bjEBE=ipP98t!75T&O44g3tLQm7w1xS#p@Cl3Tz zu70f`ig>jBn$vrZ&W4dqesn?ean>>@Hz^_9#5MJMD&=7C>k{?iS0%1>zpL{-Ovh0O z_R}PNd^wq`;IHMzJtK#K6q6sKU`%dr*-(Mp$@T;_5n&?d)R`-mT}=m98yG!E2>hB< zr8pP;QsIYYuMzudd8jU?U>{*d==?mVK5Z4baN1Y$MJw7kXkAK`iYNahB{ow-#}kHF zHO*_y4hb~tV&AClr@1H+GhXJwol5S}mO4f$G6 zK4gHWc~o_t8@)kPXw&qha0!^t#%bB0Jaj$lvKd+%by7zQuA9G-dHw)*{$h2(deSA1 ze|+Eou3jKF`6cSLP>W6{J^V!bY$#^nowc9WOD@*OAA7sY#V+VCx7(S5UJrY-7?HIV zoH9fQ#wc4o>Sz}!FV7!ym;iUh-R_T+W*&l7vS~#o@lf*vNYK0fxaoz0E=QasLVotZ zjr#3PmQBQ;&U%?;5d2xxr10PI+fj|}pI06#$HFVKT`iXIo3wGmjxo3| zZMH02M)475hXQTq-w6B=On%X(J%3j)pkg+(>_PW+vAB`o1rpUB>Vs=4CUPZs>%&!G z70-#j-#Y%&LD`vl#sXlsE#{s-Ur2lfYGpFH%PxXl){(a=wI|d{J*lu z2)>BZRj<9(#r>fGYv!+}qZe-Qy1>x04}YS1x=sccqt8tT{D!U^>^x(*;2($c_P0bt z_eD$>WG$!#_9|_y2S{DixwFW(Hk3`csss%s_1$>E@~Jls>%;q>%2ywrzj=FZK41-6 z?a!IH^@zts7~Vo$t7St+clnANjG~l|Px>75tyB5h#90onO^wA{33sGm+Cp$=z!vf z_8YnzOn)V)DgInv*7n%X_PPUj6r$*quzji?@)(_Z3F9U{a6!hVXZHLh1Y$94sy39b zF=j@-=zO7_!cDiYDqo+TrPXXZeFJ1O3Vna2Z~cWzvho$}{f}009@Mv@=1((Dnjr-7 zN>3=?K^pl2YvL|2k+fKvAD;@I&sa<*s?F;ZhQh^;CJS5Te>*b=;ey$p}=qcK-& zvJ?MK?>2(C2?|2SkNKHHGmqjy=E;R88AtI#<`z`@9$YyoMe25B?|UEN_3m-m{QePh zgTgbVCDuOmlP}Uu(Cfu`x(gixc_-tJfCk9`33}}P_myi7$|dP*z~K}A_xDrep466P zjE!Btd0aSxx&L?6v#owdI^}Xh|36<3`R^55K4dxEKJM*NQ_;VTFnyz_9pGu!VVBV$ z6%~8bZaVf3$|L~28#23+KbPzUwEhf!6`EW+aSB$og-?u(mH(1_();2u`)&MKln99DO zzSiadpPPLu^5q3xWgDsY=Unv`4X0+(TPkY-t+JnZuV-cVx%e=?)^^~^uPuB75s`$v zn#G6H26?%O>W9guz00SXrc4QwFGabU@?qG&Yprf_*a0jwltZ;pN}sH;z*kpiFi%JN zfKoX=SV>Qb=P!{n>Mi%_@ETSw=!3Wtf*a%kMf{J+ntL+rNNt}LsN5R zh13_udz7q*F zSB@}g-Fx$&t+lzSp5KmUnsP9=rzSfNv(~kw5;m*6&zwp=h^xqojvtCIc!NmvggyTT zvNmF(^lsjwqd{iX06!Sme-6=!@H-S){G6EN5f^ed7p(3kT}yxcj6D{1x*B6Dr>*rs z;Cb>Np+}au=M#%LB17XpLrdlE?J9rE(ag*+Actp7%Re%E4m31=(y2o<4rCo3udS`A z&H5$^-A#Wa>}>=!G^Xjavs+>8H6p;<@)593d%sJKghy>fO)d7c$!3+YS2IqCTTsdi z)?!0q>~(yDFv~}fVLASIfjFeIh>M_<5UkdpAkbrUF8WF~xbyevY~ z4(p8mrL?t)a)df+NC*nmd*Q(nUad2pm8(PBSCvlTclB7{$$^IuPpLkJ_|uRRIoszb(h|D9x>tU!Cd7W&p@e& z0A*VDn+7()K(GT+1)JYO1cJn>4fW79D6Dvm?Hd%Qj1phytp z7*gkr;TYIyg`*7pYv%Ir7TDHkFL9K(20YKV5SLaLgQBxPi#rj)Oe1!G9XAjTSYjH+HkSBK&h8RQln&B_m}w0)7Im6IhDV{m zaF_?5Q>UZEcB7?K`&A6Q47>D(HgaVgF$V-4c+U1!?%b zu7YD2j9l68KU9J}n@(N&GU$msMzPpJwG-{a9XIOI!DS>`F_Uze+~G?Qr4E?j3o@j~B06RDM@im`~{ z*Qgyakm7T!ogS99za6pR*}&ARN4O5fBEZVfGoflblQ!D^w3bT(HqJp3JD!4_Je?ho z;Wi@P=wg3tW#bCURx19wH}bE|yOWFZz6KY8$h4Lj;*awO)|Fucfs)NyIVh4_>dr$e zBKXdXAAj1PXbW`9W}Q?H1nJE_Vr=L);PV%#9i`GiVDiks+cBaSR&Mcx8h@i(qGspp z@iAMq;0c?7wGbhlYUOBk_j{34GmXCogK{2TeXwe9zSL+dS~QPhDL|8h6_npKGUFV| zuD1Ts&A27J1J}%9FkzX$V=+b(7N=HL<}sccp75&;osd2~r5@#s?yWP2McKxc3+lj5kEkUldLQ-!`H2dtjNQ;N^1zm5t{XGsL%9&-nD3u&*z zAAS^tue_8;Ik%0Rss-yRKx298XQYLDtQDYWqeGDCA}c*^roXV1YaBKbLb0FNu+eCM3iXViSkfo3 zX$zR5Fij?%CgsFV9&M-7Vwb_ie4hBznjvgyG?A^JA0(>o_hYPc%=j7x`LT#&`TB_3}TNKf~4nWn)w$YohoXgT`#{1 z8*i>+)pITvDN1(Ys_ z43})sHYa5qV}m~Qhu>|qi)kCN7_8EE+(3T4D~RfuYT;-V_} zexa?zHE;A#iyzg5>T_)6a3f1{3vmSn)^tFBVY(cDS7P)tipxaEFBaFwwS(@MvXlTh zdHH;(szCi^p;=9rAw}z>I~}oI;>dXu$EFQldpcnjN4! zYQX0t%nS{P{-d=3rU-M(2km!W&LG?({zbep^fFe$MG0zGb4hoiRxz^lAf{Z=$xF1w zFE;NnW0)9;daAN1)Q>8ht^Zx%sao!^Ko8rq7Iw|zs(>ec7)Zc}RQjmyAKr&QU)s)d>T@ zY8w|f54^aUiZz#WwChDPU;h7_QThc9zddixVH>5_004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv0RM-N%)bBt010qNS#tmY4c7nw4c7reD4Tcy025S6L_t(|+U1>Td=yo>#{Zpk zht3|75D7~H$Rax^ASl}f1OypHKop0?jZqkN60!sg{Qn%2EZginF-DrlL5Ud0PP8?KH#8G}CjRRyO)N8v;MhHEa zO)5pyAhOBarjAGK;Rc{4>Se;5XK^^br2U2_<>e|rr{5s^n0kwkN?IV zXd=F%E9nLh7bxJ~KvVGzHWnK~TtHy^jWyBrSLe5}e6B&gl=}ls%o8ju5*z!CV#U>(6a^Ir z&#C^tJEv8tSMrFF%``*8Tf`w;#>eEyuK&*aRLahevsU(-YdFGZs^waZoj1&x+ys%@ zZkOv@Ntd0i=QIGJjA0?qNh>{pZ^>6}t1t70N0=eu1)KmJBnPlDn3ilN6FARK$-rDn zsz~Sw<`RjM+n7!v04Knu8Cd|dVLFjO z5Mvl6epf)6YRR_iojT$ZT@D2E2NJMwH=_Z_q)fcWA-Wbv(nh>ur=K_qD><)PuA$B- zGpPpw4e0@ZjX~IfVyV|_B|!-P<_UL)p{5m-XBj|+|vI?)-hu}qcv$3|-L<|wjBgi*x~I`NLzj)Nd>XEFa`AmHGv>OF0Z z^y!#Gy*YL)gm9Y1{L?GfK{BuM0al)284#*abgrSr_3vk|waqQTMgq&(Ngua&M>C0C zY+yJc;+6j(S9QRbT<`G}cG0ky%NU>`ZJ3L5+BKEcfyxX}nA#TU7maB}`z0G0ik%NFo=2VV+J>K58WAr4JV~!@t^D^SEOyDH3Jw;Bun{>5*!>;r5?d(wkYhn*AXJv zAPvtPdeNBq{K&)NpKK?V1mcO1%`Fx>u#i+X@hr`xarlzP(|(14fu^X1p5m%aKp=}` zz{Vuf_=|=qIhv`Y@^1zZt$6(UYe;#4PmMK78wCsVWsTfVoOtyfQdvZji);yFFkiEu zi7N6xK{F7k$c#`j{YWNF*2v}3Ol_eT>)FF!=uR?CNTL%HSxqYQX{jM>-_Tsa?IS7B zMC)Rv!oKpl;=4E|v!4CzVHbPY&-cutvm32^0C^hJoh*?4)awMT6n$RC_&Rq)(UxBH z<#yT=>tSk*XRl_z=QAPDWaY53K;cF{tvj51rm9cA_BcwnKoh=({w`6mU+9*^w`nT0 z0s`x&hK>CLe=1`?*Se_x_A5*AwL%xv51D_N&nF5z) zZ8s#avjxZ9P#4H#NMMN%R0#q3n;|6!^AESw1yW3i%=8+q*}ky zQDBJ2@Gq6-Y%nbDC9012tSj!`4n$&*mA|S8qH3laSjS^-MP02{9{_Q8kE+Ey!_X+^ zrkXyuYsV?$Qw7|kwZ`h|zYH~jSePS~T*4ujN%ZRRy@9zl9udX|s)}YI!wjyelQe^r z@;o1@x+81qb4fEr%3X9)A^Q_HdGxFLiV&8_hV~!%MAiSECx$ASjxI7~<06}-o~U55 z3AOc=*?vwkfp1g?{b6o4pLjSQOBFg4TEmfSBnBy-_h0G2C#sX&EaR)5OodcIDU%4!+KYNjzl^?Coz=v>G+w#$m&#X!zTXWNLa2kJ1MGFcnGq&d4(s5^Ml zQ$fB>MI&UPAp#p{C#{w$SwUXCP~R#hj?|(%7S>XCvo0&+jqD1gX*h%1SRUBlj?Y`}e%ZuiCu@2R&|SiCQ=jds#xc9i4wMjR^G zCCi|RCzxMl=hx8EeSEI>)Q8bhc6OY6+4S+9YD%7Y*ex6K;aEAXx-nyfdxxaA)WaF5 zxHlcdoEFHblf`H@&ahAV983^-RH)w9%biKwTk0)kA`KuD3ysCkS)8h8>J=Q8WJ?uJ zN>v9ubaS_HgtycOOGE)mIf+$rA*U#kcIyX+#S~B|#DPPKyi+2^-5Sn2>LGMf+*!t1 z5UZ;uRU;wKOdOXc(+gA(q%nASZxEuiL4vvvWB^TKNZc+}SwTtl1OVZONk;6(fXE+AHMT!GJ z9agZ1)`XG)Bxx>}W3g$7Krg8$yNr%-T9U?5N{AqXl7LXpC6^TOa}nuuP_+<6>7yP( zg3Fa~IvN#T?m;(0E1c)%)tLh0|In6kiYhm2WVFa8Upg)aSC$&;%u1rIAt5C1z zb(6B+wPB$t3pvlND&KVxJLn|+xQ(;kTIk#(l1(bq1uoZ@KU+iy4|9YXF40N#`!S-2 zU^_{o-L}A8RGl~d=AZ}=D5j8V!iW)dr$)@Dh;8*m+{$!jW8)MJ>rA$_I6*PtKv$B) z?8wWCEr9&MNX}5jDu$9o6FM@9@1%H^6O1y^!6?UA^2lK-50bADpEXW&eCIQq4-gRe zSf70F#jjMcKoRyC&+AfPXf`jn)07QDA_u7A8K5=UtW?q2lcXuQjE$T^poSOpRCyEM zA@I*Sa=ZagOXSP>lHMlw{hz7fE24l%Hj^iA{c#PgWm~#AsHPex0w;LE&opux@HS4q zr2NYB;|Vu+v{&T12%KP|DZ%;AAn=atZ+(nn1S+{qu53HTEMBS;h~ArY3K{8rRTi2v zgR@lemWL#c$qMJ6Kml`%-ryK3A5o(SNekr_SwMq;z!jErhpN1J8G&WqN&1Ei<_*%Q zA)k33Wp|X!?{+noWvMhYjX?}MD5SsQnw)R839kru?b-I2`B=Qw!CT?G~fv72g2*vnh= z^*TbLfh>sgT2#Xq2KM~ToFiRf4EGJQ@UVr`NV2G46QhasagOe#^6CR8OAJ$VZY7tk zI!YTwIbUnbtoHmu1#|sU0BoY#==#jhGH7eO5tQ+X4wbY8XUNdbf_)W%m40u1a=U6O z-bI5Vs;1y%fsWsYv4*O;Nv5?!JAu*U@xI??Q0Q> zip#+wzTkFV_tzk(KoYIUQVj(45BXGG4L`X`Jq{=^DA+TfS4m+kPwHO21TmCG>@@I| zCvAu#M;>Jt#kZ9CV!`r=ySdC|ow==vyulU98EaUpTJSASUY3vV-sF>NXnt5QvvIQ9 z@7$vZ#<80k1TqbJOIQ$h@Ero1l}3;02z+4d#`7ew4S@qZ=<`xIoMC(}rF9sw|AB?p zyvBK)Y*y}UVc}f_L-!e|#VGO-DB(-)@zTa(;U*q%**f_}-L%{C62}m@#5~u)(MZ0d zfZm2vw@cYbMQma!9o;Awv6QwZl-`WEMupd zS`^QZTr?%~JGM?dhLbXKxWH+C<2O!oo+}i)d2jPxKRtN~Jk4)7$!8TqwMUzse^X^r zB-DIOQr;c@>=GT!AhzM4h&9~hN&BCUlW$!j&Wyodby9!R=NhA!K{hq)V~jVxgl6nQ zU^7FAGuTi5s!r;@MnK)fhg6Wu-@Wh19_*r;0`{?hN!&qul4wMP&bi*oM{a0UQA#o8 z8n3*lJzn*xzQ%lpljDr`<}tY{?o*k-1jf^W-Z&|tj8clZKqlFo`qdN7Q~`5Vhf;XEZcDdI45>Exx}>F&IpHKBbaF8t-cI7ERfUgP9&Id>#3c zYBIRj>BUq~m0d0Vd>OT%drb+~l@A%2Avg z@|3)59R1Eb$1aKxsN_5cS<5FZWd-XwLOxC|^N#LaWC4PClpG3KNDG%f4I++lY@(D> zmin`%_y3==(2&mD%B}Pwo=|KA;iQUE(%HfmeiGl{r59GlF^5(hh($ literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php b/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php new file mode 100644 index 00000000000..72dc7c8f2ed --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php @@ -0,0 +1,21 @@ + text("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus\n"); +$printer -> cut(); +$printer -> close(); + +/* Option 2: Image-based output (formatting not available using this output) */ +$buffer = new ImagePrintBuffer(); +$connector = new FilePrintConnector("php://stdout"); +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus\n"); +$printer -> cut(); +$printer -> close(); +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php b/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php new file mode 100644 index 00000000000..49eca235091 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php @@ -0,0 +1,36 @@ + getPrintConnector() -> write($cmd); +$printer -> text("Beispieltext in Deutsch\n"); +$printer -> cut(); +$printer -> close(); + +/* + * Hex-dump of output confirms that ESC V 1 being sent: + * + * 0000000 033 @ 033 V 001 B e i s p i e l t e x + * 0000010 t i n D e u t s c h \n 035 V A + * 0000020 003 + */ \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php b/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php new file mode 100644 index 00000000000..7314ad21bf0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php @@ -0,0 +1,16 @@ + text("El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n"); +$printer -> cut(); +$printer -> close(); + diff --git a/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php b/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php new file mode 100644 index 00000000000..b3d4b29edd1 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php @@ -0,0 +1,69 @@ + text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); +$printer -> cut(); +$printer -> close(); + +/* Option 2: Image-based output (formatting not available using this output). */ +$buffer = new ImagePrintBuffer(); +$connector = new FilePrintConnector("php://stdout"); +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); +$printer -> cut(); +$printer -> close(); + +/* + Option 3: If the printer is configured to print in a specific code + page, you can set up a CapabilityProfile which either references its + iconv encoding name, or includes all of the available characters. + + Here, we make use of CP858 for its inclusion of currency symbols which + are not available in CP437. CP858 has good printer support, but is not + included in all iconv builds. +*/ +class CustomCapabilityProfile extends SimpleCapabilityProfile { + function getCustomCodePages() { + /* + * Example to print in a specific, user-defined character set + * on a printer which has been configured to use i + */ + return array( + 'CP858' => "ÇüéâäàåçêëèïîìÄÅ" . + "ÉæÆôöòûùÿÖÜø£Ø×ƒ" . + "áíóúñѪº¿®¬½¼¡«»" . + "░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐" . + "└┴┬├─┼ãÃ╚╔╩╦╠═╬¤" . + "ðÐÊËÈ€ÍÎÏ┘┌█▄¦Ì▀" . + "ÓßÔÒõÕµþÞÚÛÙýݯ´" . + " ±‗¾¶§÷¸°¨·¹³²■ "); + } + + function getSupportedCodePages() { + return array( + 0 => 'custom:CP858'); + } +} + +$connector = new FilePrintConnector("php://stdout"); +$profile = CustomCapabilityProfile::getInstance(); +$printer = new Escpos($connector, $profile); +$printer -> text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); + +$printer -> cut(); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php b/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php new file mode 100644 index 00000000000..4ef1a9575c2 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php @@ -0,0 +1,31 @@ + textRaw("\x9C"); // based on position in CP437 +$printer -> text(" 1.95\n"); + +// B) Manually encoded UTF8 pound symbol. Tests that the driver correctly +// encodes this as CP437. +$printer -> text(base64_decode("wqM=") . " 2.95\n"); + +// C) Pasted in file. Tests that your files are being saved as UTF-8, which +// escpos-php is able to convert automatically to a mix of code pages. +$printer -> text("£ 3.95\n"); + +$printer -> cut(); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php b/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php new file mode 100644 index 00000000000..2d88cf1a2a8 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php @@ -0,0 +1,16 @@ + text($text . "\n"); +$printer -> cut(); + +// Close the connection +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php b/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php new file mode 100644 index 00000000000..316099cf304 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php @@ -0,0 +1,58 @@ + setJustification(Escpos::JUSTIFY_CENTER); + $printer -> graphics($img); + $printer -> cut(); + } finally { + // Always close the connection + $printer -> close(); + } +} catch (Exception $e) { + // Print out any errors: Eg. printer connection, image loading & external image manipulation. + echo $e -> getMessage() . "\n"; + echo $e -> getTraceAsString(); +} finally { + unlink ( $imgCombined_path ); + unlink ( $tmpf_path ); +} diff --git a/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php b/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php new file mode 100644 index 00000000000..c2b0fb5aa57 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php @@ -0,0 +1,47 @@ + utf8Glyphs($text); + +/* + * Set up and use the printer + */ +$buffer = new ImagePrintBuffer(); +$profile = EposTepCapabilityProfile::getInstance(); +$connector = new FilePrintConnector("php://output"); + // = WindowsPrintConnector("LPT2"); + // Windows LPT2 was used in the bug tracker + +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text($text . "\n"); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/README.md b/htdocs/includes/mike42/escpos-php/example/specific/README.md new file mode 100644 index 00000000000..7433e7aa247 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/README.md @@ -0,0 +1,7 @@ +Specific examples +----------------- + +These examples are designed for specific combinations of language, +printer and interface. + +They are documented here because the general examples all set up the printer in a similar way. diff --git a/htdocs/includes/mike42/escpos-php/example/text-size.php b/htdocs/includes/mike42/escpos-php/example/text-size.php new file mode 100644 index 00000000000..2f9272e434b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/text-size.php @@ -0,0 +1,62 @@ + + */ +require_once(dirname(__FILE__) . "/../Escpos.php"); +$printer = new Escpos(); + +/* Initialize */ +$printer -> initialize(); + +/* Text of various (in-proportion) sizes */ +title($printer, "Change height & width\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize($i, $i); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Width changing only */ +title($printer, "Change width only (height=4):\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize($i, 4); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Height changing only */ +title($printer, "Change height only (width=4):\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize(4, $i); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Very narrow text */ +title($printer, "Very narrow text:\n"); +$printer -> setTextSize(1, 8); +$printer -> text("The quick brown fox jumps over the lazy dog.\n"); + +/* Very flat text */ +title($printer, "Very wide text:\n"); +$printer -> setTextSize(4, 1); +$printer -> text("Hello world!\n"); + +/* Very large text */ +title($printer, "Largest possible text:\n"); +$printer -> setTextSize(8,8); +$printer -> text("Hello\nworld!\n"); + +$printer -> cut(); +$printer -> close(); + +function title(Escpos $printer, $text) { + $printer -> selectPrintMode(Escpos::MODE_EMPHASIZED); + $printer -> text("\n" . $text); + $printer -> selectPrintMode(); // Reset +} + +?> diff --git a/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php new file mode 100644 index 00000000000..db2e8e01f58 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php @@ -0,0 +1,61 @@ + CodePage::CP437, + 1 => CodePage::CP932, + 2 => CodePage::CP850, + 3 => CodePage::CP860, + 4 => CodePage::CP863, + 5 => CodePage::CP865, + 6 => false, // Hiragana + 7 => false, // One-pass printing Kanji characters + 8 => false, // Page 8 [One-pass printing Kanji characters] + 11 => CodePage::CP851, + 12 => CodePage::CP853, + 13 => CodePage::CP857, + 14 => CodePage::CP737, + 15 => CodePage::ISO8859_7, + 16 => CodePage::CP1252, + 17 => CodePage::CP866, + 18 => CodePage::CP852, + 19 => CodePage::CP858, + 20 => false, // Thai Character Code 42 + 21 => CodePage::CP874, // Thai Character Code 11 + 22 => false, // Thai Character Code 13 + 23 => false, // Thai Character Code 14 + 24 => false, // Thai Character Code 16 + 25 => false, // Thai Character Code 17 + 26 => false, // Thai Character Code 18 + 30 => false, // TCVN-3: Vietnamese + 31 => false, // TCVN-3: Vietnamese + 32 => CodePage::CP720, + 33 => CodePage::CP775, + 34 => CodePage::CP855, + 35 => CodePage::CP861, + 36 => CodePage::CP862, + 37 => CodePage::CP864, + 38 => CodePage::CP869, + 39 => CodePage::ISO8859_2, + 40 => CodePage::ISO8859_15, + 41 => CodePage::CP1098, // PC1098: Farsi + 42 => CodePage::CP774, + 43 => CodePage::CP772, + 44 => CodePage::CP1125, + 45 => CodePage::CP1250, + 46 => CodePage::CP1251, + 47 => CodePage::CP1253, + 48 => CodePage::CP1254, + 49 => CodePage::CP1255, + 50 => CodePage::CP1256, + 51 => CodePage::CP1257, + 52 => CodePage::CP1258, + 53 => CodePage::RK1048, + 66 => false, // Devanagari + 67 => false, // Bengali + 68 => false, // Tamil + 69 => false, // Telugu + 70 => false, // Assamese + 71 => false, // Oriya + 72 => false, // Kannada + 73 => false, // Malayalam + 74 => false, // Gujarati + 75 => false, // Punjabi + 82 => false, // Marathi + 254 => false, + 255 => false); + } + + function getSupportsBarcodeB() { + return true; + } + + function getSupportsBitImage() { + return true; + } + + function getSupportsGraphics() { + return true; + } + + function getSupportsStarCommands() { + return false; + } + + function getSupportsQrCode() { + return true; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php new file mode 100644 index 00000000000..e1a197b7c7b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php @@ -0,0 +1,78 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * Print connector that writes to nowhere, but allows the user to retrieve the + * buffered data. Used for testing. + */ +final class DummyPrintConnector implements PrintConnector { + /** + * @var array Buffer of accumilated data. + */ + private $buffer; + + /** + * @var string data which the printer will provide on next read + */ + private $readData; + + /** + * Create new print connector + */ + public function __construct() { + $this -> buffer = array(); + } + + public function __destruct() { + if($this -> buffer !== null) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + public function finalize() { + $this -> buffer = null; + } + + /** + * @return string Get the accumulated data that has been sent to this buffer. + */ + public function getData() { + return implode($this -> buffer); + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + return $len >= strlen($this -> readData) ? $this -> readData : substr($this -> readData, 0, $len); + } + + public function write($data) { + $this -> buffer[] = $data; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php new file mode 100644 index 00000000000..2803fe3f69c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php @@ -0,0 +1,4 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * This class deals with images in raster formats, and converts them into formats + * which are suitable for use on thermal receipt printers. Currently, only PNG + * images (in) and ESC/POS raster format (out) are implemeted. + * + * Input formats: + * - Currently, only PNG is supported. + * - Other easily read raster formats (jpg, gif) will be added at a later date, as this is not complex. + * - The BMP format can be directly read by some commands, but this has not yet been implemented. + * + * Output formats: + * - Currently, only ESC/POS raster format is supported + * - ESC/POS 'column format' support is partially implemented, but is not yet used by Escpos.php library. + * - Output as multiple rows of column format image is not yet in the works. + * + * Libraries: + * - Currently, php-gd is used to read the input. Support for imagemagick where gd is not installed is + * also not complex to add, and is a likely future feature. + * - Support for native use of the BMP format is a goal, for maximum compatibility with target environments. + */ +class EscposImage { + /** + * @var string The image's bitmap data (if it is a Windows BMP). + */ + protected $imgBmpData; + + /** + * @var string image data in rows: 1 for black, 0 for white. + */ + protected $imgData; + + /** + * @var string cached raster format data to avoid re-computation + */ + protected $imgRasterData; + + /** + * @var int height of the image + */ + protected $imgHeight; + + /** + * @var int width of the image + */ + protected $imgWidth; + + /** + * Load up an image from a filename + * + * @param string $imgPath The path to the image to load, or null to skip + * loading the image (some other functions are available for + * populating the data). Supported graphics types depend on your PHP configuration. + */ + public function __construct($imgPath = null) { + /* Can't use bitmaps yet */ + $this -> imgBmpData = null; + $this -> imgRasterData = null; + if($imgPath === null) { + // Blank image + $this -> imgHeight = 0; + $this -> imgWidth = 0; + $this -> imgData = ""; + return; + } + + /* Load up using GD */ + if(!file_exists($imgPath)) { + throw new Exception("File '$imgPath' does not exist."); + } + $ext = pathinfo($imgPath, PATHINFO_EXTENSION); + if($ext == "bmp") { + // The plan is to implement BMP handling directly in + // PHP, as some printers understand this format themselves. + // TODO implement PHP bitmap handling + throw new Exception("Native bitmaps not yet supported. Please convert the file to a supported raster format."); + } + if($this -> isGdSupported()) { + // Prefer to use gd. It is installed by default, so + // most systems will have it, giving a consistent UX. + switch($ext) { + case "png": + $im = @imagecreatefrompng($imgPath); + $this -> readImageFromGdResource($im); + return; + case "jpg": + $im = @imagecreatefromjpeg($imgPath); + $this -> readImageFromGdResource($im); + return; + case "gif": + $im = @imagecreatefromgif($imgPath); + $this -> readImageFromGdResource($im); + return; + } + } + if($this -> isImagickSupported()) { + $im = new Imagick(); + try { + // Throws an ImagickException if the format is not supported or file is not found + $im -> readImage($imgPath); + } catch(ImagickException $e) { + // Wrap in normal exception, so that classes which call this do not themselves require imagick as a dependency. + throw new Exception($e); + } + /* Flatten by doing a composite over white, in case of transparency */ + $flat = new Imagick(); + $flat -> newImage($im -> getimagewidth(), $im -> getimageheight(), "white"); + $flat -> compositeimage($im, Imagick::COMPOSITE_OVER, 0, 0); + $this -> readImageFromImagick($flat); + return; + } + throw new Exception("Images are not supported on your PHP. Please install either the gd or imagick extension."); + } + + /** + * @return int height of the image in pixels + */ + public function getHeight() { + return $this -> imgHeight; + } + + /** + * @return int Number of bytes to represent a row of this image + */ + public function getHeightBytes() { + return (int)(($this -> imgHeight + 7) / 8); + } + + /** + * @return int Width of the image + */ + public function getWidth() { + return $this -> imgWidth; + } + + /** + * @return int Number of bytes to represent a row of this image + */ + public function getWidthBytes() { + return (int)(($this -> imgWidth + 7) / 8); + } + + /** + * @return string binary data of the original file, for function which accept bitmaps. + */ + public function getWindowsBMPData() { + return $this -> imgBmpData; + } + + /** + * @return boolean True if the image was a windows bitmap, false otherwise + */ + public function isWindowsBMP() { + return $this -> imgBmpData != null; + } + + /** + * Load actual image pixels from GD resource. + * + * @param resouce $im GD resource to use + * @throws Exception Where the image can't be read. + */ + public function readImageFromGdResource($im) { + if(!is_resource($im)) { + throw new Exception("Failed to load image."); + } else if(!$this -> isGdSupported()) { + throw new Exception(__FUNCTION__ . " requires 'gd' extension."); + } + /* Make a string of 1's and 0's */ + $this -> imgHeight = imagesy($im); + $this -> imgWidth = imagesx($im); + $this -> imgData = str_repeat("\0", $this -> imgHeight * $this -> imgWidth); + for($y = 0; $y < $this -> imgHeight; $y++) { + for($x = 0; $x < $this -> imgWidth; $x++) { + /* Faster to average channels, blend alpha and negate the image here than via filters (tested!) */ + $cols = imagecolorsforindex($im, imagecolorat($im, $x, $y)); + $greyness = (int)(($cols['red'] + $cols['green'] + $cols['blue']) / 3) >> 7; // 1 for white, 0 for black + $black = (1 - $greyness) >> ($cols['alpha'] >> 6); // 1 for black, 0 for white, taking into account transparency + $this -> imgData[$y * $this -> imgWidth + $x] = $black; + } + } + } + + /** + * Load actual image pixels from Imagick object + * + * @param Imagick $im Image to load from + */ + public function readImageFromImagick(Imagick $im) { + /* Threshold */ + $im -> setImageType(Imagick::IMGTYPE_TRUECOLOR); // Remove transparency (good for PDF's) + $max = $im->getQuantumRange(); + $max = $max["quantumRangeLong"]; + $im -> thresholdImage(0.5 * $max); + /* Make a string of 1's and 0's */ + $geometry = $im -> getimagegeometry(); + $this -> imgHeight = $im -> getimageheight(); + $this -> imgWidth = $im -> getimagewidth(); + $this -> imgData = str_repeat("\0", $this -> imgHeight * $this -> imgWidth); + + for($y = 0; $y < $this -> imgHeight; $y++) { + for($x = 0; $x < $this -> imgWidth; $x++) { + /* Faster to average channels, blend alpha and negate the image here than via filters (tested!) */ + $cols = $im -> getImagePixelColor($x, $y); + $cols = $cols -> getcolor(); + $greyness = (int)(($cols['r'] + $cols['g'] + $cols['b']) / 3) >> 7; // 1 for white, 0 for black + $this -> imgData[$y * $this -> imgWidth + $x] = (1 - $greyness); // 1 for black, 0 for white + } + } + + } + + /** + * Output the image in raster (row) format. This can result in padding on the right of the image, if its width is not divisible by 8. + * + * @throws Exception Where the generated data is unsuitable for the printer (indicates a bug or oversized image). + * @return string The image in raster format. + */ + public function toRasterFormat() { + if($this -> imgRasterData != null) { + /* Use previous calculation */ + return $this -> imgRasterData; + } + /* Loop through and convert format */ + $widthPixels = $this -> getWidth(); + $heightPixels = $this -> getHeight(); + $widthBytes = $this -> getWidthBytes(); + $heightBytes = $this -> getHeightBytes(); + $x = $y = $bit = $byte = $byteVal = 0; + $data = str_repeat("\0", $widthBytes * $heightPixels); + if(strlen($data) == 0) { + return $data; + } + do { + $byteVal |= (int)$this -> imgData[$y * $widthPixels + $x] << (7 - $bit); + $x++; + $bit++; + if($x >= $widthPixels) { + $x = 0; + $y++; + $bit = 8; + if($y >= $heightPixels) { + $data[$byte] = chr($byteVal); + break; + } + } + if($bit >= 8) { + $data[$byte] = chr($byteVal); + $byteVal = 0; + $bit = 0; + $byte++; + } + } while(true); + if(strlen($data) != ($this -> getWidthBytes() * $this -> getHeight())) { + throw new Exception("Bug in " . __FUNCTION__ . ", wrong number of bytes."); + } + $this -> imgRasterData = $data; + return $this -> imgRasterData; + } + + /** + * Output image in column format. This format results in padding at the base and right of the image, if its height and width are not divisible by 8. + */ + private function toColumnFormat() { + /* Note: This function is marked private, as it is not yet used/tested and may be buggy. */ + $widthPixels = $this -> getWidth(); + $heightPixels = $this -> getHeight(); + $widthBytes = $this -> getWidthBytes(); + $heightBytes = $this -> getHeightBytes(); + $x = $y = $bit = $byte = $byteVal = 0; + $data = str_repeat("\0", $widthBytes * $heightBytes * 8); + do { + $byteVal |= (int)$this -> imgData[$y * $widthPixels + $x] << (8 - $bit); + $y++; + $bit++; + if($y >= $heightPixels) { + $y = 0; + $x++; + $bit = 8; + if($x >= $widthPixels) { + $data[$byte] = chr($byteVal); + break; + } + } + if($bit >= 8) { + $data[$byte] = chr($byteVal); + $byteVal = 0; + $bit = 0; + $byte++; + } + } while(true); + if(strlen($data) != ($widthBytes * $heightBytes * 8)) { + throw new Exception("Bug in " . __FUNCTION__ . ", wrong number of bytes. Should be " . ($widthBytes * $heightBytes * 8) . " but was " . strlen($data)); + } + return $data; + } + + /** + * @return boolean True if GD is supported, false otherwise (a wrapper for the static version, for mocking in tests) + */ + protected function isGdSupported() { + return self::isGdLoaded(); + } + + /** + * @return boolean True if Imagick is supported, false otherwise (a wrapper for the static version, for mocking in tests) + */ + protected function isImagickSupported() { + return self::isImagickLoaded(); + } + + + /** + * @return boolean True if GD is loaded, false otherwise + */ + public static function isGdLoaded() { + return extension_loaded('gd'); + } + + /** + * @return boolean True if Imagick is loaded, false otherwise + */ + public static function isImagickLoaded() { + return extension_loaded('imagick'); + } + + /** + * Load a PDF for use on the printer + * + * @param string $pdfFile The file to load + * @param string $pageWidth The width, in pixels, of the printer's output. The first page of the PDF will be scaled to approximately fit in this area. + * @param array $range array indicating the first and last page (starting from 0) to load. If not set, the entire document is loaded. + * @throws Exception Where Imagick is not loaded, or where a missing file or invalid page number is requested. + * @return multitype:EscposImage Array of images, retrieved from the PDF file. + */ + public static function loadPdf($pdfFile, $pageWidth = 550, array $range = null) { + if(!extension_loaded('imagick')) { + throw new Exception(__FUNCTION__ . " requires imagick extension."); + } + /* + * Load first page at very low density (resolution), to figure out what + * density to use to achieve $pageWidth + */ + try { + $image = new Imagick(); + $testRes = 2; // Test resolution + $image -> setresolution($testRes, $testRes); + $image -> readimage($pdfFile."[0]"); + $geo = $image -> getimagegeometry(); + $image -> destroy(); + $width = $geo['width']; + $newRes = $pageWidth / $width * $testRes; + /* Load actual document (can be very slow!) */ + $rangeStr = ""; // Set to [0] [0-1] page range if $range is set + if($range != null) { + if(count($range) != 2 || !isset($range[0]) || !is_integer($range[0]) || !isset($range[1]) || !is_integer($range[1]) || $range[0] > $range[1]) { + throw new Exception("Invalid range. Must be two numbers in the array: The start and finish page indexes, starting from 0."); + } + $rangeStr = "[" . ($range[0] == $range[1] ? $range[0] : implode($range, "-")) . "]"; + } + $image -> setresolution($newRes, $newRes); + $image -> readImage($pdfFile."$rangeStr"); + $pages = $image -> getNumberImages(); + /* Convert images to Escpos objects */ + $ret = array(); + for($i = 0;$i < $pages; $i++) { + $image -> setIteratorIndex($i); + $ep = new EscposImage(); + $ep -> readImageFromImagick($image); + $ret[] = $ep; + } + return $ret; + } catch(ImagickException $e) { + // Wrap in normal exception, so that classes which call this do not themselves require imagick as a dependency. + throw new Exception($e); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php new file mode 100644 index 00000000000..703bc0a57c0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php @@ -0,0 +1,304 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * This class manages newlines and character encoding for the target printer, and + * can be interchanged for an image-bassed buffer (ImagePrintBuffer) if you can't + * get it operating properly on your machine. + */ +class EscposPrintBuffer implements PrintBuffer { + /** + * @var boolean True to cache output as .gz, false to leave un-compressed (useful for debugging) + */ + const COMPRESS_CACHE = true; + + /** + * @var string The input encoding of the buffer. + */ + const INPUT_ENCODING = "UTF-8"; + + /** + * @var string Un-recorgnised characters will be replaced with this. + */ + const REPLACEMENT_CHAR = "?"; + + /** + * This array Maps ESC/POS character tables to names iconv encodings + */ + private $available = null; + + /** + * @var array Maps of UTF-8 to code-pages + */ + private $encode = null; + + /** + * @var Escpos Printer for output + */ + private $printer; + + /** + * Empty print buffer. + */ + function __construct() { + $this -> printer = null; + } + + public function flush() { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + // TODO Not yet implemented for this buffer: This indicates that the printer needs the current line to be ended. + } + + public function getPrinter() { + return $this -> printer; + } + + public function setPrinter(Escpos $printer = null) { + $this -> printer = $printer; + if($printer != null) { + $this -> loadAvailableCharacters(); + } + } + + public function writeText($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if($text == null) { + return; + } + if(!mb_detect_encoding($text, self::INPUT_ENCODING, true)) { + // Assume that the user has already put non-UTF8 into the target encoding. + return $this -> writeTextRaw($text); + } + $i = 0; + $j = 0; + $len = mb_strlen($text, self::INPUT_ENCODING); + while($i < $len) { + $matching = true; + if(($encoding = $this -> identifyText(mb_substr($text, $i, 1, self::INPUT_ENCODING))) === false) { + // Un-encodeable text + $encoding = $this -> getPrinter() -> getCharacterTable(); + } + $i++; + $j = 1; + do { + $char = mb_substr($text, $i, 1, self::INPUT_ENCODING); + $matching = !isset($this -> available[$char]) || isset($this -> available[$char][$encoding]); + if($matching) { + $i++; + $j++; + } + } while($matching && $i < $len); + $this -> writeTextUsingEncoding(mb_substr($text, $i - $j, $j, self::INPUT_ENCODING), $encoding); + } + } + + public function writeTextRaw($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if(strlen($text) == 0) { + return; + } + // Pass only printable characters + for($i = 0; $i < strlen($text); $i++) { + $c = substr($text, $i, 1); + if(!self::asciiCheck($c, true)) { + $text[$i] = self::REPLACEMENT_CHAR; + } + } + $this -> write($text); + } + + /** + * Return an encoding which we can start to use for outputting this text. Later parts of the text need not be included in the returned code page. + * + * @param string $text Input text to check. + * @return boolean|integer Code page number, or FALSE if the text is not printable on any supported encoding. + */ + private function identifyText($text) { + // TODO Replace this with an algorithm to choose the encoding which will encode the farthest into the string, to minimise code page changes. + $char = mb_substr($text, 0, 1, self::INPUT_ENCODING); + if(!isset($this -> available[$char])) { + /* Character not available anywhere */ + return false; + } + foreach($this -> available[$char] as $encodingNo => $true) { + /* Return first code-page where it is available */ + return $encodingNo; + } + return false; + } + + /** + * Based on the printer's connector, compute (or load a cached copy of) maps of UTF character to unicode characters for later use. + */ + private function loadAvailableCharacters() { + $supportedCodePages = $this -> printer -> getPrinterCapabilityProfile() -> getSupportedCodePages(); + $capabilityClassName = get_class($this -> printer -> getPrinterCapabilityProfile()); + $cacheFile = dirname(__FILE__) . "/cache/Characters-" . $capabilityClassName . ".ser" . (self::COMPRESS_CACHE ? ".gz" : ""); + $cacheKey = md5(serialize($supportedCodePages)); + /* Check for pre-generated file */ + if(file_exists($cacheFile)) { + $cacheData = file_get_contents($cacheFile); + if(self::COMPRESS_CACHE) { + $cacheData = gzdecode($cacheData); + } + if($cacheData) { + $dataArray = unserialize($cacheData); + if(isset($dataArray["key"]) && isset($dataArray["available"]) && isset($dataArray["encode"]) && $dataArray["key"] == $cacheKey) { + $this -> available = $dataArray["available"]; + $this -> encode = $dataArray["encode"]; + return; + } + } + } + /* Generate conversion tables */ + $encode = array(); + $available = array(); + $custom = $this -> printer -> getPrinterCapabilityProfile() -> getCustomCodePages(); + + foreach($supportedCodePages as $num => $characterMap) { + $encode[$num] = array(); + if($characterMap === false) { + continue; + } else if(strpos($characterMap, ":") !== false) { + /* Load a pre-defined custom map (vendor-specific code pages) */ + $i = strpos($characterMap, ":"); + if(substr($characterMap, 0, $i) !== "custom") { + continue; + } + $i++; + $mapName = substr($characterMap, $i, strlen($characterMap) - $i); + if(!isset($custom[$mapName]) || mb_strlen($custom[$mapName], self::INPUT_ENCODING) != 128) { + throw new Exception("Capability profile referenced invalid custom map '$mapName'."); + } + $map = $custom[$mapName]; + for($char = 128; $char <= 255; $char++) { + $utf8 = mb_substr($map, $char - 128, 1, self::INPUT_ENCODING); + if($utf8 == " ") { // Skip placeholders + continue; + } + if(!isset($available[$utf8])) { + $available[$utf8] = array(); + } + $available[$utf8][$num] = true; + $encode[$num][$utf8] = chr($char); + } + } else { + /* Generate map using iconv */ + for($char = 128; $char <= 255; $char++) { + $utf8 = @iconv($characterMap, self::INPUT_ENCODING, chr($char)); + if($utf8 == '') { + continue; + } + if(iconv(self::INPUT_ENCODING, $characterMap, $utf8) != chr($char)) { + // Avoid non-canonical conversions + continue; + } + if(!isset($available[$utf8])) { + $available[$utf8] = array(); + } + $available[$utf8][$num] = true; + $encode[$num][$utf8] = chr($char); + } + } + } + /* Use generated data */ + $dataArray = array("available" => $available, "encode" => $encode, "key" => $cacheKey); + $this -> available = $dataArray["available"]; + $this -> encode = $dataArray["encode"]; + $cacheData = serialize($dataArray); + if(self::COMPRESS_CACHE) { + $cacheData = gzencode($cacheData); + } + /* Attempt to cache, but don't worry if we can't */ + @file_put_contents($cacheFile, $cacheData); + } + + /** + * Encode a block of text using the specified map, and write it to the printer. + * + * @param string $text Text to print, UTF-8 format. + * @param integer $encodingNo Encoding number to use- assumed to exist. + */ + private function writeTextUsingEncoding($text, $encodingNo) { + $encodeMap = $this -> encode[$encodingNo]; + $len = mb_strlen($text, self::INPUT_ENCODING); + $rawText = str_repeat(self::REPLACEMENT_CHAR, $len); + for($i = 0; $i < $len; $i++) { + $char = mb_substr($text, $i, 1, self::INPUT_ENCODING); + if(isset($encodeMap[$char])) { + $rawText[$i] = $encodeMap[$char]; + } else if(self::asciiCheck($char)) { + $rawText[$i] = $char; + } + } + if($this -> printer -> getCharacterTable() != $encodingNo) { + $this -> printer -> selectCharacterTable($encodingNo); + } + $this -> writeTextRaw($rawText); + } + + /** + * Write data to the underlying printer. + * + * @param string $data + */ + private function write($data) { + $this -> printer -> getPrintConnector() -> write($data); + } + + /** + * Return true if a character is an ASCII printable character. + * + * @param string $char Character to check + * @param boolean $extended True to allow 128-256 values also (excluded by default) + * @return boolean True if the character is printable, false if it is not. + */ + private static function asciiCheck($char, $extended = false) { + if(strlen($char) != 1) { + // Multi-byte string + return false; + } + $num = ord($char); + if($num > 31 && $num < 127) { // Printable + return true; + } + if($num == 10) { // New-line (printer will take these) + return true; + } + if($extended && $num > 127) { + return true; + } + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php b/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php new file mode 100644 index 00000000000..8d87f0a527a --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php @@ -0,0 +1,80 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * PrintConnector for passing print data to a file. + */ +class FilePrintConnector implements PrintConnector { + /** + * @var resource The file pointer to send data to. + */ + protected $fp; + + /** + * Construct new connector, given a filename + * + * @param string $filename + */ + public function __construct($filename) { + $this -> fp = fopen($filename, "wb+"); + if($this -> fp === false) { + throw new Exception("Cannot initialise FilePrintConnector."); + } + } + + public function __destruct() { + if($this -> fp !== false) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + /** + * Close file pointer + */ + public function finalize() { + fclose($this -> fp); + $this -> fp = false; + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + rewind($this -> fp); + return fgets($this -> fp, $len + 1); + } + + /** + * Write data to the file + * + * @param string $data + */ + public function write($data) { + fwrite($this -> fp, $data); + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php new file mode 100644 index 00000000000..08ca64ac43e --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php @@ -0,0 +1,99 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * This class renders text to small images on-the-fly. It attempts to mimic the + * behaviour of text output, whilst supporting any fonts & character encodings + * which your system can handle. This class currently requires Imagick. + */ +class ImagePrintBuffer implements PrintBuffer { + private $printer; + + function __construct() { + if(!EscposImage::isImagickLoaded()) { + throw new Exception("ImagePrintBuffer requires the imagick extension"); + } + } + + function flush() { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + } + + function getPrinter() { + return $this -> printer; + } + + function setPrinter(Escpos $printer = null) { + $this -> printer = $printer; + } + + function writeText($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if($text == null) { + return; + } + $text = trim($text, "\n"); + /* Create Imagick objects */ + $image = new Imagick(); + $draw = new ImagickDraw(); + $color = new ImagickPixel('#000000'); + $background = new ImagickPixel('white'); + + /* Create annotation */ + //$draw -> setFont('Arial');// (not necessary?) + $draw -> setFontSize(24); // Size 21 looks good for FONT B + $draw -> setFillColor($color); + $draw -> setStrokeAntialias(true); + $draw -> setTextAntialias(true); + $metrics = $image -> queryFontMetrics($draw, $text); + $draw -> annotation(0, $metrics['ascender'], $text); + + /* Create image & draw annotation on it */ + $image -> newImage($metrics['textWidth'], $metrics['textHeight'], $background); + $image -> setImageFormat('png'); + $image -> drawImage($draw); + //$image -> writeImage("test.png"); + + /* Save image */ + $escposImage = new EscposImage(); + $escposImage -> readImageFromImagick($image); + $size = Escpos::IMG_DEFAULT; + $this -> printer -> bitImage($escposImage, $size); + } + + function writeTextRaw($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + $this -> printer -> getPrintConnector() -> write($data); + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php new file mode 100644 index 00000000000..4dd2c39f115 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php @@ -0,0 +1,39 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * PrintConnector for directly opening a network socket to a printer to send it commands. + */ +class NetworkPrintConnector extends FilePrintConnector { + public function __construct($ip, $port = "9100") { + $this -> fp = @fsockopen($ip, $port, $errno, $errstr); + if($this -> fp === false) { + throw new Exception("Cannot initialise NetworkPrintConnector: " . $errstr); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php new file mode 100644 index 00000000000..7c3b5abe6dd --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php @@ -0,0 +1,90 @@ + CodePage::CP437, + 1 => false, // Katakana + 2 => CodePage::CP850, + 3 => CodePage::CP860, + 4 => CodePage::CP863, + 5 => CodePage::CP865, + 6 => false, // Western Europe + 7 => false, // Greek + 8 => false, // Hebrew + 9 => false, // Eastern europe + 10 => false, // Iran + 16 => CodePage::CP1252 , + 17 => CodePage::CP866 , + 18 => CodePage::CP852 , + 19 => CodePage::CP858, + 20 => false, // Iran II + 21 => false, // latvian + 22 => false, //Arabic + 23 => false, // PT151, 1251 + 24 => CodePage::CP747, + 25 => CodePage::CP1257, + 27 => false, // Vietnam, + 28 => CodePage::CP864, + 29 => CodePage::CP1001, + 30 => false, // Uigur + 31 => false, // Hebrew + 32 => CodePage::CP1255, + 33 => CodePage::CP720, + 34 => CodePage::CP1256, + 35 => CodePage::CP1257, + 255 => false, // Thai + + 50 => CodePage::CP437, + 51 => false, // Jatakana, + 52 => CodePage::CP437, + 53 => CodePage::CP858, + 54 => CodePage::CP852, + 55 => CodePage::CP860, + 56 => CodePage::CP861, + 57 => CodePage::CP863, + 58 => CodePage::CP865, + 59 => CodePage::CP866, + 60 => CodePage::CP855, + 61 => CodePage::CP857, + 62 => CodePage::CP862, + 63 => CodePage::CP864, + 64 => CodePage::CP737, + 65 => CodePage::CP851, + 66 => CodePage::CP869, + 67 => CodePage::CP928, + 68 => CodePage::CP772, + 69 => CodePage::CP774, + 70 => CodePage::CP874, + 71 => CodePage::CP1252, + 72 => CodePage::CP1250, + 73 => CodePage::CP1251, + 74 => CodePage::CP3840, + 75 => CodePage::CP3841, + 76 => CodePage::CP3843, + 77 => CodePage::CP3844, + 78 => CodePage::CP3845, + 79 => CodePage::CP3846, + 80 => CodePage::CP3847, + 81 => CodePage::CP3848, + 82 => CodePage::CP1001, + 83 => CodePage::CP2001, + 84 => CodePage::CP3001, + 85 => CodePage::CP3002, + 86 => CodePage::CP3011, + 87 => CodePage::CP3012, + 88 => CodePage::CP3021, + 89 => CodePage::CP3041 + ); + } + + public function getSupportsGraphics() { + /* Ask the driver to use bitImage wherever possible instead of graphics */ + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php new file mode 100644 index 00000000000..9e3b110085f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php @@ -0,0 +1,75 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * Print buffers manage newlines and character encoding for the target printer. + * They are used as a swappable component: text or image-based output. + * + * - Text output (EscposPrintBuffer) is the fast default, and is recommended for + * most people, as the text output can be more directly manipulated by ESC/POS + * commands. + * - Image output (ImagePrintBuffer) is designed to accept more encodings than the + * physical printer supports, by rendering the text to small images on-the-fly. + * This takes a lot more CPU than sending text, but is necessary for some users. + * - If your use case fits outside these, then a further speed/flexibility trade-off + * can be made by printing directly from generated HTML or PDF. + */ +interface PrintBuffer { + /** + * Cause the buffer to send any partial input and wait on a newline. + * If the printer is already on a new line, this does nothing. + */ + function flush(); + + /** + * Used by Escpos to check if a printer is set. + */ + function getPrinter(); + + /** + * Used by Escpos to hook up one-to-one link between buffers and printers. + * + * @param Escpos $printer New printer + */ + function setPrinter(Escpos $printer = null); + + /** + * Accept UTF-8 text for printing. + * + * @param string $text Text to print + */ + function writeText($text); + + /** + * Accept 8-bit text in the current encoding and add it to the buffer. + * + * @param string $text Text to print, already the target encoding. + */ + function writeTextRaw($text); +} +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/src/PrintConnector.php b/htdocs/includes/mike42/escpos-php/src/PrintConnector.php new file mode 100644 index 00000000000..f1d37be9c55 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/PrintConnector.php @@ -0,0 +1,56 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * Interface passed to Escpos class for receiving print data. Print connectors + * are responsible for transporting this to the actual printer. + */ +interface PrintConnector { + /** + * Print connectors should cause a NOTICE if they are deconstructed + * when they have not been finalized. + */ + public function __destruct(); + + /** + * Finish using this print connector (close file, socket, send + * accumulated output, etc). + */ + public function finalize(); + + /** + * @param string $data + * @return Data read from the printer, or false where reading is not possible. + */ + public function read($len); + + /** + * @param string $data + */ + public function write($data); +} diff --git a/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php new file mode 100644 index 00000000000..7076b5e16cb --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php @@ -0,0 +1,17 @@ + CodePage::CP437); + } + + public function getSupportsGraphics() { + /* Ask the driver to use bitImage wherever possible instead of graphics */ + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php new file mode 100644 index 00000000000..f61774a5e30 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php @@ -0,0 +1,82 @@ + "ÇüéâäàåçêëèïîìÄÅ" . + "ÉæÆôöòûùÿÖÜ¢£¥₧ƒ" . + "áíóúñѪº¿⌐¬½¼¡«»" . + "░▒▓│┤Ā╢ņ╕╣║╗╝╜╛┐" . + "└┴┬├─┼ā╟╚╔╩╦╠═╬╧" . + "Š╤čČ╘╒ģĪī┘┌█▄ūŪ▀" . + "αßΓπΣσµτΦΘΩδ∞φε∩" . + "ĒēĢķĶļĻžŽ∙·√Ņš■ ", + 'CP3012' => "АБВГДЕЖЗИЙКЛМНОП" . + "РСТУФХЦЧШЩЪЫЬЭЮЯ" . + "абвгдежзийклмноп" . + "░▒▓│┤Ā╢ņ╕╣║╗╝Ō╛┐" . + "└┴┬├─┼ā╟╚╔╩╦╠═╬╧" . + "Š╤čČ╘╒ģĪī┘┌█▄ūŪ▀" . + "рстуфхцчшщъыьэюя" . + "ĒēĢķĶļĻžŽ∙·√Ņš■ " + ); + } + + function getSupportedCodePages() { + return array( + 0 => CodePage::CP437, // "Normal" + 1 => CodePage::CP437, + 2 => CodePage::CP932, + 3 => CodePage::CP437, + 4 => CodePage::CP858, + 5 => CodePage::CP852, + 6 => CodePage::CP860, + 7 => CodePage::CP861, + 8 => CodePage::CP863, + 9 => CodePage::CP865, + 10 => CodePage::CP866, + 11 => CodePage::CP855, + 12 => CodePage::CP857, + 13 => CodePage::CP862, + 14 => CodePage::CP864, + 15 => CodePage::CP737, + 16 => CodePage::CP851, + 17 => CodePage::CP869, + 18 => CodePage::CP928, + 19 => CodePage::CP772, + 20 => CodePage::CP774, + 21 => CodePage::CP874, + 32 => CodePage::CP1252, + 33 => CodePage::CP1250, + 34 => CodePage::CP1251, + 64 => CodePage::CP3840, + 65 => CodePage::CP3841, + 66 => CodePage::CP3843, + 67 => CodePage::CP3844, + 68 => CodePage::CP3845, + 69 => CodePage::CP3846, + 70 => CodePage::CP3847, + 71 => CodePage::CP3848, + 72 => CodePage::CP1001, + 73 => CodePage::CP2001, + 74 => CodePage::CP3001, + 75 => CodePage::CP3002, + 76 => 'custom:CP3011', + 77 => 'custom:CP3012', + 78 => CodePage::CP3021, + 79 => CodePage::CP3041, + 96 => false, // Thai Character Code 42 + 97 => false, // Thai Character Code 11 + 98 => false, // Thai Character Code 13 + 99 => false, // Thai Character Code 14 + 100 => false, // Thai Character Code 16 + 101 => false, // Thai Character Code 17 + 102 => false, // Thai Character Code 18 + 255 => false); + } + + function getSupportsStarCommands() { + /* Allows Escpos.php to substitute emulated ESC/POS commands with native ones for this printer. */ + return true; + } +} \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php new file mode 100644 index 00000000000..717e9c1a572 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php @@ -0,0 +1,356 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * 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. + * + * Connector for sending print jobs to + * - local ports on windows (COM1, LPT1, etc) + * - shared (SMB) printers from any platform (\\server\foo) + * For USB printers or other ports, the trick is to share the printer with a generic text driver, then access it locally. + */ +class WindowsPrintConnector implements PrintConnector { + /** + * @var array Accumulated lines of output for later use. + */ + private $buffer; + + /** + * @var string The hostname of the target machine, or null if this is a local connection. + */ + private $hostname; + + /** + * @var boolean True if a port is being used directly (must be Windows), false if network shares will be used. + */ + private $isLocal; + + /** + * @var int Platform we're running on, for selecting different commands. See PLATFORM_* constants. + */ + private $platform; + + /** + * @var string The name of the target printer (eg "Foo Printer") or port ("COM1", "LPT1"). + */ + private $printerName; + + /** + * @var string Login name for network printer, or null if not using authentication. + */ + private $userName; + + /** + * @var string Password for network printer, or null if no password is required. + */ + private $userPassword; + + /** + * @var string Workgroup that the printer is located on + */ + private $workgroup; + + /** + * @var int represents Linux + */ + const PLATFORM_LINUX = 0; + + /** + * @var int represents Mac + */ + const PLATFORM_MAC = 1; + + /** + * @var int represents Windows + */ + const PLATFORM_WIN = 2; + + /** + * @var string Valid local ports. + */ + const REGEX_LOCAL = "/^(LPT\d|COM\d)$/"; + + /** + * @var string Valid printer name. + */ + const REGEX_PRINTERNAME = "/^[\w-]+(\s[\w-]+)*$/"; + + /** + * @var string Valid smb:// URI containing hostname & printer with optional user & optional password only. + */ + const REGEX_SMB = "/^smb:\/\/([\s\w-]+(:[\s\w-]+)?@)?[\w-]+\/([\w-]+\/)?[\w-]+(\s[\w-]+)*$/"; + + /** + * @param string $dest + * @throws BadMethodCallException + */ + public function __construct($dest) { + $this -> platform = $this -> getCurrentPlatform(); + $this -> isLocal = false; + $this -> buffer = null; + $this -> userName = null; + $this -> userPassword = null; + $this -> workgroup = null; + if(preg_match(self::REGEX_LOCAL, $dest) == 1) { + // Straight to LPT1, COM1 or other local port. Allowed only if we are actually on windows. + if($this -> platform !== self::PLATFORM_WIN) { + throw new BadMethodCallException("WindowsPrintConnector can only be used to print to a local printer ('".$dest."') on a Windows computer."); + } + $this -> isLocal = true; + $this -> hostname = null; + $this -> printerName = $dest; + } else if(preg_match(self::REGEX_SMB, $dest) == 1) { + // Connect to samba share, eg smb://host/printer + $part = parse_url($dest); + $this -> hostname = $part['host']; + /* Printer name and optional workgroup */ + $path = ltrim($part['path'], '/'); + if(strpos($path, "/") !== false) { + $pathPart = explode("/", $path); + $this -> workgroup = $pathPart[0]; + $this -> printerName = $pathPart[1]; + } else { + $this -> printerName = $path; + } + /* Username and password if set */ + if(isset($part['user'])) { + $this -> userName = $part['user']; + if(isset($part['pass'])) { + $this -> userPassword = $part['pass']; + } + } + } else if(preg_match(self::REGEX_PRINTERNAME, $dest) == 1) { + // Just got a printer name. Assume it's on the current computer. + $hostname = gethostname(); + if(!$hostname) { + $hostname = "localhost"; + } + $this -> hostname = $hostname; + $this -> printerName = $dest; + } else { + throw new BadMethodCallException("Printer '" . $dest . "' is not a valid printer name. Use local port (LPT1, COM1, etc) or smb://computer/printer notation."); + } + $this -> buffer = array(); + } + + public function __destruct() { + if($this -> buffer !== null) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + public function finalize() { + $data = implode($this -> buffer); + $this -> buffer = null; + if($this -> platform == self::PLATFORM_WIN) { + $this -> finalizeWin($data); + } else if($this -> platform == self::PLATFORM_LINUX) { + $this -> finalizeLinux($data); + } else { + $this -> finalizeMac($data); + } + } + + /** + * Send job to printer -- platform-specific Linux code. + * + * @param string $data Print data + * @throws Exception + */ + protected function finalizeLinux($data) { + /* Non-Windows samba printing */ + $device = "//" . $this -> hostname . "/" . $this -> printerName; + if($this -> userName !== null) { + $user = ($this -> workgroup != null ? ($this -> workgroup . "\\") : "") . $this -> userName; + if($this -> userPassword == null) { + // No password + $command = sprintf("smbclient %s -U %s -c %s -N", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg("print -")); + $redactedCommand = $command; + } else { + // With password + $command = sprintf("smbclient %s %s -U %s -c %s", + escapeshellarg($device), + escapeshellarg($this -> userPassword), + escapeshellarg($user), + escapeshellarg("print -")); + $redactedCommand = sprintf("smbclient %s %s -U %s -c %s", + escapeshellarg($device), + escapeshellarg("*****"), + escapeshellarg($user), + escapeshellarg("print -")); + } + } else { + // No authentication information at all + $command = sprintf("smbclient %s -c %s -N", + escapeshellarg($device), + escapeshellarg("print -")); + $redactedCommand = $command; + } + $retval = $this -> runCommand($command, $outputStr, $errorStr, $data); + if($retval != 0) { + throw new Exception("Failed to print. Command \"$redactedCommand\" failed with exit code $retval: " . trim($outputStr)); + } + } + + protected function finalizeMac($data) { + throw new Exception("Mac printing not implemented."); + } + + /** + * Send data to printer -- platform-specific Windows code. + * + * @param string $data + */ + protected function finalizeWin($data) { + /* Windows-friendly printing of all sorts */ + if(!$this -> isLocal) { + /* Networked printing */ + $device = "\\\\" . $this -> hostname . "\\" . $this -> printerName; + if($this -> userName !== null) { + /* Log in */ + $user = "/user:" . ($this -> workgroup != null ? ($this -> workgroup . "\\") : "") . $this -> userName; + if($this -> userPassword == null) { + $command = sprintf("net use %s %s", + escapeshellarg($device), + escapeshellarg($user)); + $redactedCommand = $command; + } else { + $command = sprintf("net use %s %s %s", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg($this -> userPassword)); + $redactedCommand = sprintf("net use %s %s %s", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg("*****")); + } + $retval = $this -> runCommand($command, $outputStr, $errorStr); + if($retval != 0) { + throw new Exception("Failed to print. Command \"$redactedCommand\" failed with exit code $retval: " . trim($errorStr)); + } + } + /* Final print-out */ + $filename = tempnam(sys_get_temp_dir(), "escpos"); + file_put_contents($filename, $data); + if(!$this -> runCopy($filename, $device)){ + throw new Exception("Failed to copy file to printer"); + } + unlink($filename); + } else { + /* Drop data straight on the printer */ + if(!$this -> runWrite($data, $this -> printerName)) { + throw new Exception("Failed to write file to printer at " . $this -> printerName); + } + } + } + + /** + * @return string Current platform. Separated out for testing purposes. + */ + protected function getCurrentPlatform() { + if(PHP_OS == "WINNT") { + return self::PLATFORM_WIN; + } + if(PHP_OS == "Darwin") { + return self::PLATFORM_MAC; + } + return self::PLATFORM_LINUX; + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + /* Two-way communication is not supported */ + return false; + } + + /** + * Run a command, pass it data, and retrieve its return value, standard output, and standard error. + * + * @param string $command the command to run. + * @param string $outputStr variable to fill with standard output. + * @param string $errorStr variable to fill with standard error. + * @param string $inputStr text to pass to the command's standard input (optional). + * @return number + */ + protected function runCommand($command, &$outputStr, &$errorStr, $inputStr = null) { + $descriptors = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w"), + ); + $process = proc_open($command, $descriptors, $fd); + if (is_resource($process)) { + /* Write to input */ + if($inputStr !== null) { + fwrite($fd[0], $inputStr); + } + fclose($fd[0]); + /* Read stdout */ + $outputStr = stream_get_contents($fd[1]); + fclose($fd[1]); + /* Read stderr */ + $errorStr = stream_get_contents($fd[2]); + fclose($fd[2]); + /* Finish up */ + $retval = proc_close($process); + return $retval; + } else { + /* Method calling this should notice a non-zero exit and print an error */ + return -1; + } + } + + /** + * Copy a file. Separated out so that nothing is actually printed during test runs. + * + * @param string $from Source file + * @param string $to Destination file + * @return boolean True if copy was successful, false otherwise + */ + protected function runCopy($from, $to) { + return copy($from, $to); + } + + /** + * Write data to a file. Separated out so that nothing is actually printed during test runs. + * + * @param string $data Data to print + * @param string $to Destination file + * @return boolean True if write was successful, false otherwise + */ + protected function runWrite($data, $to) { + return file_put_contents($data, $to) !== false; + } + + public function write($data) { + $this -> buffer[] = $data; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz new file mode 100644 index 0000000000000000000000000000000000000000..43ad39d229582af5ad7121088bf2c6287e067e11 GIT binary patch literal 12928 zcmd701yJ12wsO(hteDgWbot`Nswnypxw6vA;Fl-I9~WFV=cEKW z75Vc>N7Y3IBGpr(Ib`C482S0}2~Hk5VpX1-W&ubTJ%^u;gkG}`!A@Wq$$j5oAuCpqc>`!S7D=fV51iVx|4%N&fnE% zIF?JcB^f$}#a+gh2@L8DU@OU(?XBpwx*Y1Bu2?ll>pBMHyVcfS+Sg9CcUv^dXTU33 zLc6SZ=S!%@Euz1U!u=#H#y&9yQ_dt2;%M>A0U?^ZotY2kCqI{bY4I=k zk`d&|!@Y=bHK`$$*#j~Dc4GcK*GtMQ8Nz&wn#Nb8~B%6}38)k4`Dm zl$JS<)#~kHl)X6D(m3S(GjwC9DJw;2Mo&z!m659c`;GGED>oq*43L=XRnXGW1H$yy zziR&v2KV>mCh|3H(N>SnMFh7G zsx*{}E_^9}mumXaX0*5e>hS5sKAuaR-LFYLHuzyjO3=zoG1BS2h~AO2GGy0!e|X8g zf0(eCaq0V3?Y4t-X3a5mF~--B%4|okbt_omK)5Z5@vGa+#VhYO6(nYY@(H3DvLjE! z(kL!Eu)ReeN30G#LU$e(lrmLLaYDhD_s$eyuRd6?(iu2r>IqX&dk%!Q7rVQBfJob2 z2Hj+I9Fp#Q|9O11Wh8R`tsu1gT9M+tkoo;xUD@HfLeIvDmi;D&wFl{pwsI@p`5c{A z;>8a-trvTr-DxE$!6RASI2YBhg)5P;noJn&f&CUn@bdLdSlUQbXh|U+%}M1(?h$~! z5^bte>n)<-TU{|N^>fEoS^ypZv;e5wHm>e7t#OM@O3bUxhjsvs4mpX$W{kSKR{(r9 z>Xw=_>Rw+3;=kOQ+C@M~Cd_irev1@XY{o)3tT+>PdJ3!^w0wO5q}7=x-%@*+e2KO~ zmyZ=z|2z+-T>-jK<2e%g(y>N4;4xTs`L|n0uJHbTYxWrF-5RAsfq5~hNxbN|ZyMeY>|tzYUqfobBK^2CzmhP;g?^}~|M_}9#Vr>XWq zr}F@!>;pUoiru`Ud|kG>M8qMZW|e0B%ToJy8KWWW9(=Ssi&h<#jvD?^%Txo(daF~V zv-%2X8?d}%qS&S~P1EwD@O`t>!rfSUBn9p`VZX(SZHC-t3n z;u5Vy7VPUkLV4s5&QI_=-=qibX|6*R+Bm+gH9v>C3M~#vz*?Ww9K4_4-Vq;6>HmT* zrqRCU*QC;8_BD}6=m2m7>>#ndxBe1o@|meBGZO_>6@aw zMg#RaAu^xbHAGrIJ*z|yo9wHlyv70bIwM-2+_gk*K0WJ151Z;crMw1#!krMwPwwg? zWuBgOMQTw5!29wjt_48h?g-wMttl0pkf_LH@@r+#rX3;&C*)0}htQcDde3^FKiRb= zh*spx9KC0??~3f&0<`IfxW&1Ph}0sxmIKMyA!2dv;v#8<&TP@c*7^YQIY`DH@o6An z;8W17kE9)@eCd<}jRpwf_2FWau-nl(`?cvN5ocm=T+!l3vqd_Z*6%*bp-#VtiEW$p zu)%Km%>q$Rm7Ut6B7bTPW!?7|knx^ehZZG}4~LuQ`2NU&qmk5i+&|&zpBRwrV)YKP zpikdp$9d4NjfchOuyqjg?!G-HGjsMD4L9bHuja z80(@DM)s=cN3K}U38pV0&AVhoYOYI{thCLsrKlqeOp(YxDWsO(DChZCnP~am4c34j`&HcTF$Xa^urRHOmTEjf_e$0D4PV!g?&^_fh_AQwk)g8Li@T1Yp05dS+Ir1n zZeqq2ZeRVVw9&XqDA_3mZKX1BLudlH65vfl^gm2vCN8pE=3 z_qZzUL)y5?$pw$gbL%o@;*gp8+5I^1m*$;k!_;?&)H1qM^M*Q&4s<===GX8$cZ`p+ zDQy?mh&y+7mV{adYubhUCf({?+>_0{RgSVHY!|73$ml4$6EG2V?rbaxT@BT=%lJ)# z>RmJkYXbTFCKc*kXp+o-KRe1+v0aoT>D>9bB%}pM{C<;q^)9(d26H&Ww9fAC(YBG3 zFMezs-**xoyfND%pmOX2 z4BxU(4wHpItCMc68&#Ql+(g8SebmR2oZygY%lnoxHK?!GUf+-_9+Rq^`Mbb~~l zxU!3wrN}9lfyn3M;hxDPGoxMPoRj3+qF-v4MVGDQx1Or90Y{Wm9W-|xf+LK4PgqTl zpl-Ms>Ig02RzcE3O%IDOck-smNc(`@!P%b1f^bbi>M5o3WUXUY@m4#+6-~UWXUFP1 z%gN#?vd5CA1uM=?kHwR(j3w17X766<{NCHw-t$#}r=G)}+gnn=jJX<)o6C(Vv18Vt~jBi24+;4}d5Dh{7=CFMxO>NYS36%t#7| zx)iV%fXE4mLNMhTK)e7%pi%@74Jlx2fG7coA~59>K)e^E2m~te08xhmCJBhF&Vp&c zX2t|5*o$XI=KwuJnQ;oxmB284l{qIn1qxUqP&q6}vHVV%F$ZAOC}6G{MX7Gif(=qI zWj~fGNO^WSG<81UbcC><5Hzw9Ftv=QQWK~AAG#!Zwq`5DO+QIgx0(4be?KC zt3e%q*Q*2r7s;{SMLJnZdW3Ld@h9q(Mcn$p8{m>YoLm@xi{;=cIlt-4by>m5lmh*P6?SSo$HJYqyxO6syRvk{cq<-m=dqi7lvKTZ zIhdPSUB4du~@3N6#?rjUbXk>>suG(CAGWbnK0c5=S?~t|ZTFqhLBeFo~MqOj1 zVj~jPv^D(M-!@tK$o*aNaD|u_XZTHVV7ZNZpAnw}#I*@5Y=V|k4dph38&+E77}TwE zELlU2n!v-=P;*1Ld!<#fLER$9k|X4(6^v_)Mq~(=t3;$2crI~>+CZq9!AT})Bh^rV zeqM=qXW+TQA!-Zx&+;|M3)spxmok8#m~;Km2? zq~#rkZySyzzdQAP7;XL{;n((C#od3S{`RBHtM1<_!$mA7I{GmqIuNi9Bv+Xm!L7r6 zl>E}#TQTP4@ADUYebj4MWN*wGgXsKnC~aE<>uwZQFs?*Y^vhbF*-TuA*sF~s>nAH- zc$?|VBoEVcAJ?m#8XpP!54!&j_{zIZexatN*vv;W-@m`q>!P)m-P}Ey$*~@2?h+HK z1G!W&5XCpJW~k_gmx2E|Lxm?(MgL40cmz05a)l?LjFo{u6>@w?s_3V0Wp5*HalzambZ$$i9`YZ!Q15}cE%-#t4Hta z!y+A*Hmk`{c=jz+=nBo{1bbnPw5pxgSnow$X`7XX7yQF_#|5G-!$^AraKeq1e@tY5Ie&WmAeChi*m53w*&nb>27l`prqgDBOKVxMde@={F#uB&Fr{B96-lhAb zOqaWL8fy+2qFm_KipSP)V2VL3$L5Z`LHIn@_ zS;&^gWms}yWzyEx8g}B&)7>{?`|`-7N?E~3aAY=lcXi97mF$`TwCR9o#ks49Javic z?DbsZSh8;tbVdw63+XJ7lJ;Ndn%-FdpY}`L8yhs?fx~?sl*g`uzyZTHoF1zpH=Uz8eRhIw zF&wg2VLhV<6UC9h0~Y$vd-(XLh{}_@gqbg4pkDhX@AG-QwKq0W4MCaPLNlTYXbUkBUWkZh!7bViJ-N!2y>(yNh^^) z2fQUv-8$$J<;AavP$V-+E15nJo+#4UD2N5=B(P5|0*XO}G8)`x9YIRtiHbi=ZX8hsIruq$Yd#;=b<6Bu`f!%bD=nh_ZTlQth_dn5R2=mR?F)sRu%5Q zHxzsxlJL=wYf?Zoukh?_CKOJvhZsES4KV)#BKN7v!fV2kb1Yen<@45U8~)E%|P zW5*QRr`L+98_dNLJE5Pt;LM64m1mLMCZc~f`bj>kq7Z*Wh}rI|f~tI0Ps1PYaRm#o z*fD(`me>w`9(F;jC8pe1C_8FwOim^hEQ^ky)paAC{sSb`&c!&y+D-oC(8KgXt*kla?Imyp^{$Jak` zq3@L3=A`eG8YE~KQ7DbcFXNQY$2XwQ$|0wB0{&Bc&6e1aH6e_sssfy-uXKr^zdjN{ z7s|2a#=0OV<~N3zf?FQZvcX|Is3rFw@ztk!Ukttd{G#k?E+$)E?LE&|;=?m0B(>%# z$ujglyIxXUeOqnqZ+OT7I|N8CAD?zB2B}h zYy5x$Eo{MOpbQJuw>pOO6S40!6VH6wGZD%X*}a^cbE zp;qXROZ5Wc$Y-VP0+_}ys#S!Z?^}?+_7dmm=pJZ`KQFX(+Lbx+A*ImQ(r9^p) zqI>#p7e%!RQ3P>8sMTI%@=lY2GPI^!`S0_E<`OZQ9J0A(onTQqF$O^i}zVjq^<1hP&bV?zdN zTcAKKEz@x^*?XTf8@pOkYrehbWQkEy#8GgAD7fg}^QUBYv;Z;KZ$(9d_heX(Hx7

    AN>wkF3^lWB>v&YPaMdeJ# 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 literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz new file mode 100644 index 0000000000000000000000000000000000000000..288b7d3740782fbcc9d9a28d71a6ecc330683eb4 GIT binary patch literal 1116 zcmV-i1f%;OiwFP!0000019jGCa}-Aq2H^j4zu=@ppj-W#6S@x+l>LD!S5XvI%G|-( zDBPZM&N)j+LdafhIRGb;P0kqyYz!D2xgGlLa@lWxQt7$b*`As1-br{d@8Etf?nM$G zCHCw~lRM>N;gO`CvM?<}uDCNAj@HmhwhZCfOE_CYw{>ZDVf! zrP{9H4RVQ(<`;%MzXIgRu7}KTVtJ#+Q=|sNQ26~KPXc$d7?qYbz8mBu+;Ah|(?)@0 zjDpP=pY|1FdXkS}l(WWSN%@7tjoDL1*Q(+dr+{3_TvGGPaqvGb_)ag9{9yNo70G}7 zmiW)zet+SINM-m}ZuJhT0Am8xfPR4*z@R`apjV&{FeFe97!f!O7#BzZCIr%eQGp|X z8G)mKX@Lx2Qs84it-vRMI)P(=v_J!3SfCM5FVF;76lex43uFQF0zP0}AO~0#XaQ^r zv;uMhZGcXJc7Xur(g7F{=mg*xy28J>Cp+nuAzMuXJ$Ox@KrdiQpbxMh&<|J>7y!%( z3<4Sjh7cK!e;9x{MgZ85QN*5U5E;V|9QQb2Ltp}cI&S!5oPtrl5OLD?)qd_=RPf-_xKK_0BA_#($uaPX@th1r_p{3XUpJ!MjeWREm7Ag5&!_BEvnp6uUIi7lt%62&P6e58UImqUL8VRPqDrgCB^Bi1w<^fP?^KZB zmsL=ES9G5-^Hr6M$TiLIl*n}zG}jv{cYw-2>5zioy zpY$0_`y7%+U-%i(V~>8(AxM}PDoCndAsHm_OBGD~3VXdG@LH$AIsb-t&kDTJfRUPS zF$A;xu0v3Re?Y3x&;P_j_(=YxseyR^#t^_i5qG&M4BDGK;D#4|jh0lzkjbXRFnB3hb^MN8s^XWjvL>zN7b6aWBu>k}&g literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz new file mode 100644 index 0000000000000000000000000000000000000000..e0bb0e5fec5c59a4131ae1f2f4151d392f435b91 GIT binary patch literal 9416 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 $c) { + $code = ord($c); + if($code < 32 || $code > 126) { + $chars[$i] = "\\x" . bin2hex($c); + } + } + return implode($chars); +} diff --git a/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php b/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php new file mode 100644 index 00000000000..689891e2432 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php @@ -0,0 +1,126 @@ + exampleDir = dirname(__FILE__) . "/../../example/"; + } + + public function testBitImage() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("bit-image.php"); + $this -> outpTest($outp, "bit-image.bin"); + } + + public function testCharacterEncodings() { + $outp = $this -> runExample("character-encodings.php"); + $this -> outpTest($outp, "character-encodings.bin"); + } + + public function testCharacterTables() { + $outp = $this -> runExample("character-tables.php"); + $this -> outpTest($outp, "character-tables.bin"); + } + + private function outpTest($outp, $fn) { + $file = dirname(__FILE__) . "/resources/output/".$fn; + if(!file_exists($file)) { + file_put_contents($file, $outp); + } + $this -> assertEquals($outp, file_get_contents($file)); + } + + public function testDemo() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("demo.php"); + $this -> outpTest($outp, "demo.bin"); + } + + public function testGraphics() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("graphics.php"); + $this -> outpTest($outp, "graphics.bin"); + } + + public function testReceiptWithLogo() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("receipt-with-logo.php"); + $this -> outpTest($outp, "receipt-with-logo.bin"); + } + + public function testQrCode() { + $outp = $this -> runExample("qr-code.php"); + $this -> outpTest($outp, "qr-code.bin"); + } + + public function testBarcode() { + $outp = $this -> runExample("barcode.php"); + $this -> outpTest($outp, "barcode.bin"); + } + + public function testTextSize() { + $outp = $this -> runExample("text-size.php"); + $this -> outpTest($outp, "text-size.bin"); + } + + /** + * @large + */ + public function testPrintFromPdf() { + if(!EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $outp = $this -> runExample("print-from-pdf.php"); + $this -> outpTest(gzcompress($outp, 9), "print-from-pdf.bin.z"); // Compressing output because it's ~1MB + } + + public function testInterfaceEthernet() { + // Test attempts DNS lookup on some machine + $outp = $this -> runExample("interface/ethernet.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceLinuxUSB() { + $outp = $this -> runExample("interface/linux-usb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceWindowsUSB() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/windows-usb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceSMB() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/smb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceWindowsLPT() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/windows-lpt.php"); + $this -> outpTest($outp, "interface.bin"); + } + + private function runExample($fn) { + // Change directory and check script + chdir($this -> exampleDir); + $this -> assertTrue(file_exists($fn), "Script $fn not found."); + // Run command and save output + ob_start(); + passthru("php " . escapeshellarg($fn), $retval); + $outp = ob_get_contents(); + ob_end_clean(); + // Check return value + $this -> assertEquals(0, $retval, "Example $fn exited with status $retval"); + return $outp; + } + + protected function requireGraphicsLibrary() { + if(!EscposImage::isGdLoaded() && !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("This test requires a graphics library."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin new file mode 100644 index 0000000000000000000000000000000000000000..79a2daf803edde452d5e0e47aa7e2545bb108962 GIT binary patch literal 2342 zcmbVOOK;Oa5N<#~apB0Rsx*}VrKNc|DSdDO$4wy|BBZnx*Nwd-t2$oGyJ=eG&og6h z5^s}~7F9~u-kope`{uFU{3uKkWF_~=6 z1i%v|qvqI-U>&ea$oDE>xBk=Xy3Jafe%B`J;d*>LHHzR~kC9YO zOJ8`fzSoj#5cbU$lQ;>}L>W ziHYl6oUfw0V9IZJZ1{lulTSZ&;Gi2lk_bmSU4phK zfEDE7-@i`Dr$Y?$tRCnfMO8 zp5N(!P{3}Oh%z}5y+pNbc|lOp6NCIgeVMHdy4%P`!BgO=`9N`vx{Q;ArvXzOK7zU% z^<`vt!<6_?KGAjO_2LPGoTId0z+;|bduCrA#rt~NGUg`w&RhFU5ypqv9cr4|0lLlU myC4{*<%Ub&1!=k2$p0TU_biE~{#W=!QNEb^?`U=9J^u^w*`<2` literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin new file mode 100644 index 0000000000000000000000000000000000000000..462eb1a0bf32bbe4bd68b8e9dc8304bc6559549b GIT binary patch literal 9789 zcmeI2F>l*O6o4N~Km-iK^i~;FL=GL2p@BvZhRz)ebP9|X-K1KqB~%s#T524iFxjbN zmdwphF^m3y{)0q7hm2K`Au8;Meed0)cvM`p2s#yahROTx9q-<~dptee>7(P5VRR9N z(PjU9I*!88dH*cB2>VGCPLt7O77fD3quDT=4Wlp~529q}>(NZt(iOtf`24&-89WTX zis#|QFrJSGVLTZ>3FjBl&d1a6lWvHQd`9iwe-Mto33b_7(w`1Trx#c@h_S~>JllC3 zC*R_;;gfitNcYLZot=B%bzmQ!!M{wZs8w`|A~y^CqF9<76veH{X;G|AE-)F4%@svf z6)@)&yCgT*BVJVUZANu!3jGrYRDV^q_g-uan4hon52iuX(*kPjtsyn)cimf11|K=^ z@P1H}uZ~PUoUXnP41*soS63!CSF5$jt!=(}T9@BCb-sjJ`|T;Tl+j;s3|?V!22f)( zwOPN792oUmTYLcEn(~7T_Dnv?p;_Y|^pw$Fx9Q}u;c#lthhI(pdFA?s177u@<(a$> z9nWyU^)BQ!Zn&9eIOH1V%dOrBBpg7+IJ*E&<#58A3H8VoonkWppI!4E-V7k%cdvSb z6mz;!I2B%Cvy{>WUDZZmol&KNPRT2(Mw;hz3rY_y`C1efOZhKFVX>tDyC~?tq{qQ~ zBOF|poN-2Jc5lSu>WYiqw=#fKvs3bw?$=q%0M?>M$!|sPwH~)qaw`0%ELExEitdfD z+GagK@AZQ90ky@!bMDxClWV!KUojiReKDy_2zOSRW%$tRrX>|`~zUU!CCqU&>(9&$6hIJ*4?W<6r* zkBq{zoQqj5|&yb^2Rw+y?1WjC-? z`9_o*R`gYpQpwlCb^}Y>#cp7|AVb&w__dkCx&QG7)=7UnzB{~fZFr^E zMH^n(^Fez)XwL@~f5WQI3w8Wx&j)3dwC95uEKs;$&j;=Kpu7|Cc|SSR+gGCr{;VrW ZqSG1v3@e_-$!vr_%c`9j-uvQH_b;K8L%;w4 literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin new file mode 100644 index 0000000000000000000000000000000000000000..0a762c8c160e39da6be9fc7c587b76a53e73c74e GIT binary patch literal 1780 zcmZuyTWl0n7zTZ-F;NqrHYQ&a6ELYh_(Xg`2<0M;K_p^)J3DiB=X7W0tmn*ZFHiIa zR0;;u2!aW1xoo-1r9iotise>dmtH}zh0+Txkg&LkVxs@9`xS6bN@--@RpuT>_RtePo=rc;uJs_ z5d@ViaS@WGDE6JGhKvz?%7H{GFM}y_(Y}nnkf_i`RJh>^;Yuv$uEDj>!*n6Et%W3X*MCK$>PQjM|zY@1Xp$iSd|BEyw1B@AX*QYo3uaXzJN z&m`@Y-0Npem*<2SOszxC% z4KpPstw9EBQL)ByFW|lNOs#=e5vf<9t*R+p8O;$fICaO@hHne6)}&BlC$Ijs$PCoU-ww=1cT}wtLqjp z%WIYIR3RaFv>0=l#SoEHbmfKYx*9(2BUVKRAd9s`J~!AB!*Z{$LJ7PiRyUd3$;jKt z0e%p2qXQjL>BRywtp24{b=X(=*hYntBIo5Rztyhj8ZpVQWN4t~PPvY?98>dg*&ZCAi zYq?Yr4dIgSWLR-M)rFRk9We8AIs+C{%*dJ6kS3XzJR@utwD}Z)cUzO^E+yhS&L$2W zn83i*`01{riN=c)rxK%aoa`SPI5?Wz-7^|*>+VeM>OMViVx)C@Yg6*v){etC(%;x} zqrIc~#Qv`1iI%R$WP5Ys_?fZJ_=V(o{E}=vpJ;4NUZL;X`VL**+ZErLXldWIFNr4V_g}cU#PTDC>AD_a?EDGbmigY!AtM6^IjRe0{2|>kZ;>Bi$1v}hdGIydrYCd zGBta0NrvnkYBR#X%%#hU=q!8GZlY1C=todMU4aCqW(J|P&-IkNA9ts3<7#T~PKxfs z*V6bSbuWE+FpZAEuGGC7X`D}A$Awffbt`oV8}KmI**`&ZJ*g`XZ~lo3>H8fRN?jT3 zXiMQ<`et88BAwx}kOvhn`ClHkpq~xRk{MrR@_tn%)^FJOJ$_n^irTeUx1wgv$_muf j)o=O%>wa8;x*Dvi*jSAfwcnw7LtX9a>b&>9n)CX9MQ`KH literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin new file mode 100644 index 0000000000000000000000000000000000000000..f3fb990cea3c56a9631360b2d12cb0e5b64254c6 GIT binary patch literal 8174 zcmdT}Rdf?k5N#=TaJL*_3)C%}WF1NkjpY#0?C(h z45ESa7c5+~c*)Xb%U7&iwR+9kb?Y~5+_ZVi)@|E&?A*0`&)$7B&_Vhzhj=gwcace>RFz*0_>*o&=LuZ-FAn_8 zyq&8A!x+ev&DZTp0><9q6%(p+E(QF#JA7%tkPgq6r!s&cU6l`07BHlf@nOmVhIha9fTyV6?T6 zf^0nT0sapA!}aF*0)Nf^L~@ZqRsVh=e6D|S4On}V27_b0_`zt|hz;J&iumXmASyhK&6z-)(UBGZpkRgEKURs0#hI>I2 l1{m%IQ8-|@7erAEqjKxXWEf&yyxX3ve?UC!%Etz|`~{yY7?A(~ literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin new file mode 100644 index 0000000000000000000000000000000000000000..6b426d91ff0563a2ec43c93217f26a492e8d9396 GIT binary patch literal 73643 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 M5AW>X{`!mPPivkA#sB~S literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin new file mode 100644 index 00000000000..729d4f4481b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin @@ -0,0 +1,2 @@ +@Hello World! +VA \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z new file mode 100644 index 0000000000000000000000000000000000000000..f89eb5d2115e388de90586ff30d80a47fc0ad083 GIT binary patch literal 17081 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 baI$Cav;BIBA!wfEw|@W6|LqUE=-dSWqat^g literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin new file mode 100644 index 0000000000000000000000000000000000000000..a1e687f151c8f8db633da3fa63fcc0d67fbb6671 GIT binary patch literal 1551 zcmbW1%}>HG6u_t8We4M(m^^M6J#6Dcy@^WVK@6zzUbhOBwVhq3gTLN4zC;SLLcwb^4Fs%YZ^axwI=)>$o;5TW;ISE+xyoDb3D~gXf{;_MB%LYbFNZdi7bT z?{sCXA!a-ZDbSQ;r)^ir9>$GXPlPWaXW&zz6%7z(p`3F|Gyy757!7vWo@TZVYBrj! z^NY*vx5ckKB%CVd+x9m1DVeteJ5hw$8y10bHnYCKyln$-3me8OwupM3J#v(WyfXo0TM?&e18G=`?b5 z4$KhE97Hy8CJALw;# k<|_&F8YT^J0>~w5QL=Ip$*1cQ(OqxX~7|PHI^$;+biUCaG-b< zbkNsYg1nc0z4RhHU7dT3|(d)C_d?ac6hZ+6UB)o-dT=fRgd zpFgbk>W0|726xg0-U0->UoL0HJX{(VYKP7ZxnIhPHVA``3=CP%Cj z)(PW)x!Rogn@(h!TjJOMkctJMfLO3m27V5VHXZk-fGi$t`eOX{#ni!!#C|Pk>xBS` z`hakY$!44yirQVwNsRVY#6gS$aOrxBBEZa0ZkBtZ@UUQr4q}q~iKG(5G@xC(jF2bJ zy=0SWmd0-8Cz(eSp*Tu|iz#Z8C8U6fV3@LNMP*DBZW;^<)0b>dK-K;*^{!=<-P9Ag zn+3#_BdT7ABKafXUCZ59QO*qI72ppfbtSwM6QTt5^=AsvL!l^kCEQfHG9)USrCiJ= zRE&2}fcb>-aX7y(2#N{i;pIF^&Q?=u_7LCPK|wrrLVzcf6Em}?zJQD+NmuWnJnIe$ZtAnUuvO3s6 zW`qyyf~HMMi4s&ve+DMMx*chEjGd7H?6XwD8}cEb5r-e6X4 z0!BL8Oh74l(p1V*rzfh`l%GN=0dx$f#ZoZRRFFZb6apikPAW=iDy9PttqfwLAO|B& z$s;?hoQ|7H#dO+NN>TKmGCHzTDjY0DM}1>ys=(ot+yY5Xf$xk88FtL$t0|50m^94e zOCwEX!S7Rgp9Q3=Ecjrgsno}X(v)CylCwaXN<3UBO$}VUQw}w03LIQ0MNzdt=c*Op zmxbC;tpK0R(-#2KlJ47L9k333yaV*Ln0rufR#*A?xaAL znZFHB`9av{CltG$bgC6i$F}kvwMG5bzHI7*r~Q-1oWkBBgWhycx)E>dJgB`6k1Evf uZg{-kddr_i9UdO=tzN%-$eZ=XIv)hMO>9TUc%2ib6th}+ZV#S^)@eM5#$Umje%7B$&R5Dh^tV~#$w#q`7 z2J74y?a_(7i}%morNHl9wfe**k7``&J#KbiTZ{Tf7<4>V`Jur=mv`Y)0pk1OKhu8^ fN@?`ksDeWFnwwHshg)rJwNxjy4|Ni2SUh!)7zJM* literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/phpunit.xml b/htdocs/includes/mike42/escpos-php/test/phpunit.xml new file mode 100644 index 00000000000..f9a6579cf45 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/phpunit.xml @@ -0,0 +1,16 @@ + + + + unit + + + integration + + + \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php b/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php new file mode 100644 index 00000000000..b3e72452a9f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php @@ -0,0 +1,69 @@ + profiles = array('DefaultCapabilityProfile', 'EposTepCapabilityProfile', 'SimpleCapabilityProfile', 'StarCapabilityProfile', 'P822DCapabilityProfile'); + $this -> checklist = array(); + foreach($this -> profiles as $profile) { + $this-> checklist[] = $profile::getInstance(); + } + } + + function testSupportedCodePages() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportedCodePages(); + $this -> assertTrue(is_array($check) && isset($check[0]) && $check[0] == 'CP437'); + $custom = $obj -> getCustomCodePages(); + foreach($check as $num => $page) { + $this -> assertTrue(is_numeric($num) && ($page === false || is_string($page))); + if($page === false || strpos($page, ":") === false) { + continue; + } + $part = explode(":", $page); + if(!array_shift($part) == "custom") { + continue; + } + $this -> assertTrue(isset($custom[implode(":", $part)])); + } + } + } + + function testCustomCodePages() { + foreach($this -> checklist as $obj) { + $check = $obj -> getCustomCodePages(); + $this -> assertTrue(is_array($check)); + foreach($check as $name => $customMap) { + $this -> assertTrue(is_string($name)); + $this -> assertTrue(is_string($customMap) && mb_strlen($customMap, 'UTF-8') == 128); + } + } + } + + function testSupportsBitImage() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsBitImage(); + $this -> assertTrue(is_bool($check)); + } + } + + function testSupportsGraphics() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsGraphics(); + $this -> assertTrue(is_bool($check)); + } + } + + function testSupportsQrCode() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsQrCode(); + $this -> assertTrue(is_bool($check)); + } + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php new file mode 100644 index 00000000000..f152f400a2f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php @@ -0,0 +1,235 @@ + loadAndCheckImg(null, false, false, 0, 0, ""); + } + + /** + * BMP handling not yet implemented, but these will use + * a native PHP bitmap reader. + * This just tests that they are not being passed on to another library. + */ + public function testBmpBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.bmp', false, false, 1, 1, "\x80"); + } + + public function testBmpBlack() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("canvas_black.bmp", false, false, 0, 0, "\x80"); + } + + public function testBmpBlackWhite() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("black_white.bmp", false, false, 0, 0, "\xc0\x00"); + } + + public function testBmpWhite() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("canvas_white.bmp", false, false, 0, 0, "\x00"); + } + + /** + * GD tests - Load tiny images and check how they are printed. + * These are skipped if you don't have gd. + */ + public function testGdBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.png', true, false, 1, 1, "\x80"); + } + + public function testGdBlack() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_black.' . $format, true, false, 1, 1, "\x80"); + } + } + + public function testGdBlackTransparent() { + foreach(array('png', 'gif') as $format) { + $this -> loadAndCheckImg('black_transparent.' . $format, true, false, 2, 2, "\xc0\x00"); + } + } + + public function testGdBlackWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('black_white.' . $format, true, false, 2, 2, "\xc0\x00"); + } + } + + public function testGdWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_white.' . $format, true, false, 1, 1, "\x00"); + } + } + + /** + * Imagick tests - Load tiny images and check how they are printed + * These are skipped if you don't have imagick + */ + public function testImagickBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.png', false, true, 1, 1, "\x80"); + } + + public function testImagickBlack() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_black.' . $format, false, true, 1, 1, "\x80"); + } + } + + public function testImagickBlackTransparent() { + foreach(array('png', 'gif') as $format) { + $this -> loadAndCheckImg('black_transparent.' . $format, false, true, 2, 2, "\xc0\x00"); + } + } + + public function testImagickBlackWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('black_white.' . $format, false, true, 2, 2, "\xc0\x00"); + } + } + + public function testImagickWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_white.' . $format, false, true, 1, 1, "\x00"); + } + } + + /** + * Mixed test - Same as above, but confirms that each tiny image can be loaded + * under any supported library configuration with the same results. + * These are skipped if you don't have gd AND imagick + */ + public function testLibraryDifferences() { + if(!EscposImage::isGdLoaded() || !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("both gd and imagick plugin are required for this test"); + } + $inFile = array('black_white.png', 'canvas_black.png', 'canvas_white.png'); + foreach($inFile as $fn) { + // Default check + $im = new EscposImage(dirname(__FILE__) . "/resources/$fn"); + $width = $im -> getWidth(); + $height = $im -> getHeight(); + $data = $im -> toRasterFormat(); + // Gd check + $this -> loadAndCheckImg($fn, true, false, $width, $height, $data); + // Imagick check + $this -> loadAndCheckImg($fn, false, true, $width, $height, $data); + } + } + + /** + * PDF tests - load tiny PDF and check for well-formedness + * These are also skipped if you don't have imagick + * @medium + */ + public function testPdfAllPages() { + $this -> loadAndCheckPdf('doc.pdf', null, 1, 1, array("\x00", "\x80")); + } + + public function testPdfBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckPdf('not a real file', null, 1, 1, array()); + } + + /** + * @medium + */ + public function testPdfBadRange() { + // Start page is after end page. + $this -> setExpectedException('Exception'); + $this -> loadAndCheckPdf('doc.pdf', array(1, 0), 1, 1, array("\x00", "\x80")); + } + + /** + * @medium + */ + public function testPdfFirstPage() { + $this -> loadAndCheckPdf('doc.pdf', array(0, 0), 1, 1, array("\x00")); + } + + /** + * @medium + */ + public function testPdfMorePages() { + $this -> loadAndCheckPdf('doc.pdf', array(1, 20), 1, 1, array("\x80")); + } + + /** + * @medium + */ + public function testPdfSecondPage() { + $this -> loadAndCheckPdf('doc.pdf', array(1, 1), 1, 1, array("\x80")); + } + + /** + * @medium + */ + public function testPdfStartPastEndOfDoc() { + // Doc only has pages 0 and 1, can't start reading from 2. + $this -> markTestIncomplete("Test needs revising- produces output due to apparent imagick bug."); + $this -> setExpectedException('ImagickException'); + $this -> loadAndCheckPdf('doc.pdf', array(2, 3), 1, 1, array()); + } + + /** + * Load an EscposImage with (optionally) certain libraries disabled and run a check. + */ + private function loadAndCheckImg($fn, $gd, $imagick, $width, $height, $rasterFormat = null) { + $img = $this -> getMockImage($fn === null ? null : dirname(__FILE__) . "/resources/$fn", $gd, $imagick); + $this -> checkImg($img, $width, $height, $rasterFormat); + } + + /** + * Same as above, loading document and checking pages against some expected values. + */ + private function loadAndCheckPdf($fn, array $range = null, $width, $height, array $rasterFormat = null) { + if(!EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $pdfPages = EscposImage::loadPdf(dirname(__FILE__) . "/resources/$fn", $width, $range); + $this -> assertTrue(count($pdfPages) == count($rasterFormat), "Got back wrong number of pages"); + foreach($pdfPages as $id => $img) { + $this -> checkImg($img, $width, $height, $rasterFormat[$id]); + } + } + + /** + * Check image against known width, height, output. + */ + private function checkImg(EscposImage $img, $width, $height, $rasterFormat = null) { + if($rasterFormat === null) { + echo "\nImage was: " . $img -> getWidth() . "x" . $img -> getHeight() . ", data \"" . friendlyBinary($img -> toRasterFormat()) . "\""; + } + $this -> assertTrue($img -> getHeight() == $height); + $this -> assertTrue($img -> getWidth() == $width); + $this -> assertTrue($img -> toRasterFormat() == $rasterFormat); + } + + /** + * Load up an EsposImage with given libraries disabled or enabled. Marks the test + * as skipped if you ask for a library which is not loaded. + */ + private function getMockImage($path, $gd, $imagick) { + /* Sanity checks */ + if($gd && !EscposImage::isGdLoaded()) { + $this -> markTestSkipped("gd plugin required for this test"); + } + if($imagick && !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $stub = $this -> getMockBuilder('EscposImage') + -> setMethods(array('isGdSupported', 'isImagickSupported')) + -> disableOriginalConstructor() + -> getMock(); + $stub -> method('isGdSupported') + -> willReturn($gd); + $stub -> method('isImagickSupported') + -> willReturn($imagick); + $stub -> __construct($path); + return $stub; + } +} \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php new file mode 100644 index 00000000000..2bbdb34c20c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php @@ -0,0 +1,150 @@ + outputConnector = new DummyPrintConnector(); + $printer = new Escpos($this -> outputConnector); + $this -> buffer = $printer -> getPrintBuffer(); + } + + protected function checkOutput($expected = null) { + /* Check those output strings */ + $outp = $this -> outputConnector -> getData(); + if($expected === null) { + echo "\nOutput was:\n\"" . friendlyBinary($outp) . "\"\n"; + } + $this -> assertEquals($expected, $outp); + } + + protected function tearDown() { + $this -> outputConnector -> finalize(); + } + + public function testRawTextNonprintable() { + $this -> buffer -> writeTextRaw("Test" . Escpos::ESC . "v1\n"); + $this -> checkOutput("\x1b@Test?v1\x0a"); // ASCII ESC character is substituted for '?' + } + + public function testDanish() { + $this -> buffer -> writeText("Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.\n"); + $this -> checkOutput("\x1b@Quizdeltagerne spiste jordb\x91r med fl\x1bt\x02\x9bde, mens cirkusklovnen Wolther spillede p\x86 xylofon.\x0a"); + } + + public function testGerman() { + $this -> buffer -> writeText("Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.\n"); + $this -> checkOutput("\x1b@Falsches \x9aben von Xylophonmusik qu\x84lt jeden gr\x94\xe1eren Zwerg.\x0a"); + } + + public function testGreek() { + $this -> buffer -> writeText("Ξεσκεπάζω την ψυχοφθόρα βδελυγμία"); + $this -> checkOutput("\x1b@\x1bt\x0b\xbd\xde\xec\xe4\xde\xea\x9b\xe0\xfa \xee\xe1\xe7 \xf6\xf2\xf4\xe9\xf3\xe2\xa2\xeb\xd6 \xd7\xdd\xde\xe5\xf2\xd8\xe6\x9f\xd6"); + } + + public function testGreekWithDiacritics() { + // This is a string which is known to be un-printable in ESC/POS (the grave-accented letters are not in any code page), + // so we are checking the substitution '?' for unknown characters. + $this -> buffer -> writeText("Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο.\n"); + $this -> checkOutput("\x1b@\xe2\xe0\x1bt\x0b\xe0\x9d\xde\xed \xe4\xd6? \xe6\xf2\xeb\xee\xe3?\xed \xdd?\xe7 \xe2? \xd7\xeb? \xea\xe3? \xec\xee? \xf4\xeb\xf2\xec\xd6\xf3? \xe8\x9d\xf3\xfa\xee\xe9.\x0a"); + } + + public function testEnglish() { + $this -> buffer -> writeText("The quick brown fox jumps over the lazy dog.\n"); + $this -> checkOutput("\x1b@The quick brown fox jumps over the lazy dog.\n"); + } + + public function testSpanish() { + // This one does not require changing code-pages at all, so characters are just converted from Unicode to CP437. + $this -> buffer -> writeText("El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n"); + $this -> checkOutput("\x1b@El ping\x81ino Wenceslao hizo kil\xa2metros bajo exhaustiva lluvia y fr\xa1o, a\xa4oraba a su querido cachorro.\x0a"); + } + + public function testFrench() { + $this -> buffer -> writeText("Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë au delà des îles, près du mälström où brûlent les novæ.\n"); + $this -> checkOutput("\x1b@Le c\x1bt\x10\x9cur d\xe9\xe7u mais l'\xe2me plut\xf4t na\xefve, Lou\xffs r\xeava de crapa\xfcter en cano\xeb au del\xe0 des \xeeles, pr\xe8s du m\xe4lstr\xf6m o\xf9 br\xfblent les nov\xe6.\x0a"); + } + + public function testIrishGaelic() { + // Note that some letters with diacritics cannot be printed for Irish Gaelic text, so text may need to be simplified. + $this -> buffer -> writeText("D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.\n"); + $this -> checkOutput("\x1b@D'fhuascail \x1bt\x02\xd6osa, \xe9rmhac na h\xe0ighe Beannaithe, p\xa2r \x90ava agus \xb5dhaimh.\x0a"); + } + + public function testHungarian() { + $this -> buffer -> writeText("Árvíztűrő tükörfúrógép.\n"); + $this -> checkOutput("\x1b@\x1bt\x02\xb5rv\xa1zt\x1bt\x12\xfbr\x8b t\x81k\x94rf\xa3r\xa2g\x82p.\x0a"); + } + + public function testIcelandic() { + $this -> buffer -> writeText("Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa."); + $this -> checkOutput("\x1b@K\x91mi n\x1bt\x02\xec \x94xi h\x82r ykist \xe7j\xa2fum n\xa3 b\x91\xd0i v\xa1l og \xa0drepa."); + } + + public function testJapaneseHiragana() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText(implode("\n", array("いろはにほへとちりぬるを", " わかよたれそつねならむ", "うゐのおくやまけふこえて", "あさきゆめみしゑひもせす")) . "\n"); + $this -> checkOutput(); + } + + public function testJapaneseKatakana() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText(implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン")) . "\n"); + $this -> checkOutput("\x1b@\x1bt\x01\xb2\xdb\xca\xc6\xce\xcd\xc4 \xc1\xd8\xc7\xd9\xa6 \xdc\xb6\xd6\xc0\xda\xbf \xc2\xc8\xc5\xd7\xd1\x0a\xb3\xb2\xc9\xb5\xb8\xd4\xcf \xb9\xcc\xba\xb4\xc3 \xb1\xbb\xb7\xd5\xd2\xd0\xbc \xb4\xcb\xd3\xbe\xbd\xdd\x0a"); + } + + public function testJapaneseKataKanaHalfWidth() { + $this -> buffer -> writeText(implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウイノオクヤマ ケフコエテ アサキユメミシ エヒモセスン")) . "\n"); + $this -> checkOutput("\x1b@\x1bt\x01\xb2\xdb\xca\xc6\xce\xcd\xc4 \xc1\xd8\xc7\xd9\xa6 \xdc\xb6\xd6\xc0\xda\xbf \xc2\xc8\xc5\xd7\xd1\x0a\xb3\xb2\xc9\xb5\xb8\xd4\xcf \xb9\xcc\xba\xb4\xc3 \xb1\xbb\xb7\xd5\xd2\xd0\xbc \xb4\xcb\xd3\xbe\xbd\xdd\x0a"); + } + + public function testLatvian() { + $this -> buffer -> writeText("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus.\n"); + $this -> checkOutput("\x1b@Gl\x1bt!\x83\xd8\xd5\xe9\xd7\xeca r\xd7\xe9\x8c\xd5i dz\x89rum\x83 \xd1iepj Baha koncertfl\x8c\x85e\xebu v\x83kus.\x0a"); + } + + public function testPolish() { + $this -> buffer -> writeText("Pchnąć w tę łódź jeża lub ośm skrzyń fig.\n"); + $this -> checkOutput("\x1b@Pchn\x1bt\x12\xa5\x86 w t\xa9 \x88\xa2d\xab je\xbea lub o\x98m skrzy\xe4 fig.\x0a"); + } + + public function testRussian() { + $this -> buffer -> writeText("В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!\n"); + $this -> checkOutput("\x1b@\x1bt\x11\x82 \xe7\xa0\xe9\xa0\xe5 \xee\xa3\xa0 \xa6\xa8\xab \xa1\xeb \xe6\xa8\xe2\xe0\xe3\xe1? \x84\xa0, \xad\xae \xe4\xa0\xab\xec\xe8\xa8\xa2\xeb\xa9 \xed\xaa\xa7\xa5\xac\xaf\xab\xef\xe0!\x0a"); + } + + public function testThai() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText("นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ\n"); // Quotation from Wikipedia + $this -> checkOutput(); + } + + public function testTurkish() { + $this -> buffer -> writeText("Pijamalı hasta, yağız şoföre çabucak güvendi.\n"); + $this -> checkOutput("\x1b@Pijamal\x1bt\x02\xd5 hasta, ya\x1bt\x0d\xa7\x8dz \x9fof\x94re \x87abucak g\x81vendi.\x0a"); + } + + public function testArabic() { + $this -> markTestIncomplete("Right-to-left text not yet supported."); + $this -> buffer -> writeText("صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ" . "\n"); // Quotation from Wikipedia + $this -> checkOutput(); + } + + public function testHebrew() { + // RTL text is more complex than the above. + $this -> markTestIncomplete("Right-to-left text not yet supported."); + $this -> buffer -> writeText("דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה" . "\n"); + $this -> checkOutput(); + } +} + diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php new file mode 100644 index 00000000000..6fd0bd7d204 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php @@ -0,0 +1,765 @@ + outputConnector = new DummyPrintConnector(); + $this -> printer = new Escpos($this -> outputConnector); + } + + protected function checkOutput($expected = null) { + /* Check those output strings */ + $outp = $this -> outputConnector -> getData(); + if($expected === null) { + echo "\nOutput was:\n\"" . friendlyBinary($outp) . "\"\n"; + } + $this -> assertEquals($expected, $outp); + } + + protected function tearDown() { + $this -> outputConnector -> finalize(); + } + + protected function requireGraphicsLibrary() { + if(!EscposImage::isGdLoaded() && !EscposImage::isImagickLoaded()) { + // If the test is about to do something which requires a library, + // something must throw an exception. + $this -> setExpectedException('Exception'); + } + } + + public function testInitializeOutput() { + $this -> checkOutput("\x1b\x40"); + } + + public function testTextStringOutput() { + $this -> printer -> text("The quick brown fox jumps over the lazy dog\n"); + $this -> checkOutput("\x1b@The quick brown fox jumps over the lazy dog\n"); + } + + public function testTextDefault() { + $this -> printer -> text(); + $this -> checkOutput("\x1b@"); + } + + public function testTextString() { + $this -> printer -> text("String"); + $this -> printer -> text(123); + $this -> printer -> text(); + $this -> printer -> text(null); + $this -> printer -> text(1.2); + $this -> printer -> text(new FooBar("FooBar")); + $this -> checkOutput("\x1b@String1231.2FooBar"); + } + + public function testTextObject() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> text(new DateTime()); + } + + public function testFeedDefault() { + $this -> printer -> feed(); + $this -> checkOutput("\x1b@\x0a"); + } + + public function testFeed3Lines() { + $this -> printer -> feed(3); + $this -> checkOutput("\x1b@\x1bd\x03"); + } + + public function testFeedZero() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed(0); + } + + public function testFeedNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed("ab"); + } + + public function testFeedTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed(256); + } + + /* Print mode */ + public function testSelectPrintModeDefault() { + $this -> printer -> selectPrintMode(); + $this -> checkOutput("\x1b@\x1b!\x00"); + } + + public function testSelectPrintModeAcceptedValues() { + /* This iterates over a bunch of numbers, figures out which + ones contain invalid flags, and checks that the driver + rejects those, but accepts the good inputs */ + + for($i = -1; $i <= 256; $i++) { + $invalid = ($i < 0) || ($i > 255) || (($i & 2) == 2) || (($i & 4) == 4) || (($i & 64) == 64); + $failed = false; + try { + $this -> printer -> selectPrintMode($i); + } catch(Exception $e) { + $failed = true; + } + $this -> assertEquals($failed, $invalid); + } + } + + /* Underline */ + public function testSetUnderlineDefault() { + $this -> printer -> setUnderline(); + $this -> checkOutput("\x1b@\x1b-\x01"); + } + + public function testSetUnderlineOff() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_NONE); + $this -> checkOutput("\x1b@\x1b-\x00"); + } + + public function testSetUnderlineOn() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_SINGLE); + $this -> checkOutput("\x1b@\x1b-\x01"); + } + + public function testSetUnderlineDbl() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_DOUBLE); + $this -> checkOutput("\x1b@\x1b-\x02"); + } + + public function testSetUnderlineAcceptedValues() { + $this -> printer -> setUnderline(0); + $this -> printer -> setUnderline(1); + $this -> printer -> setUnderline(2); + /* These map to 0 & 1 for interchangeability with setEmphasis */ + $this -> printer -> setUnderline(true); + $this -> printer -> setUnderline(false); + $this -> checkOutput("\x1b@\x1b-\x00\x1b-\x01\x1b-\x02\x1b-\x01\x1b-\x00"); + } + + public function testSetUnderlineTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline(3); + } + + public function testSetUnderlineNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline(-1); + } + + public function testSetUnderlineNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline("Hello"); + } + + /* Emphasis */ + public function testSetEmphasisDefault() { + $this -> printer -> setEmphasis(); + $this -> checkOutput("\x1b@\x1bE\x01"); + } + + public function testSetEmphasisOn() { + $this -> printer -> setEmphasis(true); + $this -> checkOutput("\x1b@\x1bE\x01"); + } + + public function testSetEmphasisOff() { + $this -> printer -> setEmphasis(false); + $this -> checkOutput("\x1b@\x1bE\x00"); + } + + public function testSetEmphasisNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setEmphasis(7); + } + + /* Double strike */ + public function testSetDoubleStrikeDefault() { + $this -> printer -> setDoubleStrike(); + $this -> checkOutput("\x1b@\x1bG\x01"); + } + + public function testSetDoubleStrikeOn() { + $this -> printer -> setDoubleStrike(true); + $this -> checkOutput("\x1b@\x1bG\x01"); + } + + public function testSetDoubleStrikeOff() { + $this -> printer -> setDoubleStrike(false); + $this -> checkOutput("\x1b@\x1bG\x00"); + } + + public function testSetDoubleStrikeNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setDoubleStrike(4); + } + + /* Font */ + public function testSetFontDefault() { + $this -> printer -> setFont(); + $this -> checkOutput("\x1b@\x1bM\x00"); + } + + public function testSetFontAcceptedValues() { + $this -> printer -> setFont(Escpos::FONT_A); + $this -> printer -> setFont(Escpos::FONT_B); + $this -> printer -> setFont(Escpos::FONT_C); + $this -> checkOutput("\x1b@\x1bM\x00\x1bM\x01\x1bM\x02"); + } + + public function testSetFontNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(-1); + } + + + public function testSetFontTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(3); + } + + public function testSetFontNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont('hello'); + } + + /* Justification */ + public function testSetJustificationDefault() { + $this -> printer -> setJustification(); + $this -> checkOutput("\x1b@\x1ba\x00"); + } + + public function testSetJustificationLeft() { + $this -> printer -> setJustification(Escpos::JUSTIFY_LEFT); + $this -> checkOutput("\x1b@\x1ba\x00"); + } + + public function testSetJustificationRight() { + $this -> printer -> setJustification(Escpos::JUSTIFY_RIGHT); + $this -> checkOutput("\x1b@\x1ba\x02"); + } + + public function testSetJustificationCenter() { + $this -> printer -> setJustification(Escpos::JUSTIFY_CENTER); + $this -> checkOutput("\x1b@\x1ba\x01"); + } + + public function testSetJustificationNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setJustification(-1); + } + + + public function testSetJustificationTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(3); + } + + public function testSetJustificationNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setJustification('hello'); + } + + /* Reverse feed */ + public function testFeedReverseDefault() { + $this -> printer -> feedReverse(); + $this -> checkOutput("\x1b@\x1be\x01"); + } + + public function testFeedReverse3() { + $this -> printer -> feedReverse(3); + $this -> checkOutput("\x1b@\x1be\x03"); + } + + public function testFeedReverseNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse(-1); + } + + public function testFeedReverseTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse(256); + } + + public function testFeedReverseNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse('hello'); + } + + /* Cut */ + public function testCutDefault() { + // TODO check what the accepted range of values should be for $line + // cut($mode = self::CUT_FULL, $lines = 3) + $this -> printer -> cut(); + $this -> checkOutput("\x1b@\x1dVA\x03"); + } + + /* Set barcode height */ + public function testSetBarcodeHeightDefault() { + $this -> printer -> setBarcodeHeight(); + $this -> checkOutput("\x1b@\x1dh\x08"); + } + + public function testBarcodeHeight10() { + $this -> printer -> setBarcodeHeight(10); + $this -> checkOutput("\x1b@\x1dh\x0a"); + } + + public function testSetBarcodeHeightNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight(-1); + } + + public function testSetBarcodeHeightTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight(256); + } + + public function tesSetBarcodeHeightNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight('hello'); + } + + /* Barcode text position */ + public function testSetBarcodeTextPositionDefault() { + $this -> printer -> setBarcodeTextPosition(); + $this -> checkOutput("\x1b@\x1dH\x00"); + } + + public function testSetBarcodeTextPositionBelow() { + $this -> printer -> setBarcodeTextPosition(Escpos::BARCODE_TEXT_BELOW); + $this -> checkOutput("\x1b@\x1dH\x02"); + } + + public function testSetBarcodeTextPositionBoth() { + $this -> printer -> setBarcodeTextPosition(Escpos::BARCODE_TEXT_BELOW | Escpos::BARCODE_TEXT_ABOVE); + $this -> checkOutput("\x1b@\x1dH\x03"); + } + + public function testSetBarcodeTextPositionNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition(-1); + } + + public function testSetBarcodeTextPositionTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition(4); + } + + public function tesSetBarcodeTextPositionNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition('hello'); + } + + /* Barcode - UPC-A */ + public function testBarcodeUpcaNumeric11Char() { + $this -> printer -> barcode("01234567890", Escpos::BARCODE_UPCA); + $this -> checkOutput("\x1b@\x1dkA\x0b01234567890"); + } + + public function testBarcodeUpcaNumeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_UPCA); + $this -> checkOutput("\x1b@\x1dkA\x0c012345678901"); + } + + public function testBarcodeUpcaNumeric13Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("0123456789012", Escpos::BARCODE_UPCA); + } + + public function testBarcodeUpcaNonNumeric12Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A12345678901", Escpos::BARCODE_UPCA); + } + + /* Barcode - UPC-E */ + public function testBarcodeUpceNumeric6Char() { + $this -> printer -> barcode("123456", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x06123456"); + } + + public function testBarcodeUpceNumeric7Char() { + $this -> printer -> barcode("0123456", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x070123456"); + } + + public function testBarcodeUpceNumeric8Char() { + $this -> printer -> barcode("01234567", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0801234567"); + } + + public function testBarcodeUpceNumeric11Char() { + $this -> printer -> barcode("01234567890", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0b01234567890"); + } + + public function testBarcodeUpceNumeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0c012345678901"); + } + + public function testBarcodeUpceNumeric9Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345678", Escpos::BARCODE_UPCE); + } + + public function testBarcodeUpceNonNumeric12Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A12345678901", Escpos::BARCODE_UPCE); + } + + /* Barcode - JAN13 */ + public function testBarcodeJan13Numeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_JAN13); + $this -> checkOutput("\x1b@\x1dkC\x0c012345678901"); + } + + public function testBarcodeJan13Numeric13Char() { + $this -> printer -> barcode("0123456789012", Escpos::BARCODE_JAN13); + $this -> checkOutput("\x1b@\x1dkC\x0d0123456789012"); + } + + public function testBarcodeJan13Numeric11Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("01234567890", Escpos::BARCODE_JAN13); + } + + public function testBarcodeJan13NonNumeric13Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A123456789012", Escpos::BARCODE_JAN13); + } + + /* Barcode - JAN8 */ + public function testBarcodeJan8Numeric7Char() { + $this -> printer -> barcode("0123456", Escpos::BARCODE_JAN8); + $this -> checkOutput("\x1b@\x1dkD\x070123456"); + } + + public function testBarcodeJan8Numeric8Char() { + $this -> printer -> barcode("01234567", Escpos::BARCODE_JAN8); + $this -> checkOutput("\x1b@\x1dkD\x0801234567"); + } + + public function testBarcodeJan8Numeric9Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345678", Escpos::BARCODE_JAN8); + } + + public function testBarcodeJan8NonNumeric8Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A1234567", Escpos::BARCODE_JAN8); + } + + /* Barcode - Code39 */ + public function testBarcodeCode39AsDefault() { + $this -> printer -> barcode("1234"); + $this -> checkOutput("\x1b@\x1dkE\x041234"); + } + + public function testBarcodeCode39Text() { + $this -> printer -> barcode("ABC 012", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x07ABC 012"); + } + + public function testBarcodeCode39SpecialChars() { + $this -> printer -> barcode("$%+-./", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x06$%+-./"); + } + + public function testBarcodeCode39Asterisks() { + $this -> printer -> barcode("*TEXT*", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x06*TEXT*"); + } + + public function testBarcodeCode39AsterisksUnmatched() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("*TEXT", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39AsteriskInText() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("12*34", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39Lowercase() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("abcd", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39Empty() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("**", Escpos::BARCODE_CODE39); + } + + /* Barcode - ITF */ + public function testBarcodeItfNumericEven() { + $this -> printer -> barcode("1234", Escpos::BARCODE_ITF); + $this -> checkOutput("\x1b@\x1dkF\x041234"); + } + + public function testBarcodeItfNumericOdd() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("123", Escpos::BARCODE_ITF); + } + + public function testBarcodeItfNonNumericEven() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A234", Escpos::BARCODE_ITF); + } + + /* Barcode - Codabar */ + public function testBarcodeCodabarNumeric() { + $this -> printer -> barcode("A012345A", Escpos::BARCODE_CODABAR); + $this -> checkOutput("\x1b@\x1dkG\x08A012345A"); + } + + public function testBarcodeCodabarSpecialChars() { + $this -> printer -> barcode("A012$+-./:A", Escpos::BARCODE_CODABAR); + $this -> checkOutput("\x1b@\x1dkG\x0bA012$+-./:A"); + } + + public function testBarcodeCodabarNotWrapped() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345", Escpos::BARCODE_CODABAR); + } + + public function testBarcodeCodabarStartStopWrongPlace() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012A45", Escpos::BARCODE_CODABAR); + } + + /* Barcode - Code93 */ + public function testBarcodeCode93Valid() { + $this -> printer -> barcode("012abcd", Escpos::BARCODE_CODE93); + $this -> checkOutput("\x1b@\x1dkH\x07012abcd"); + } + + public function testBarcodeCode93Empty() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("", Escpos::BARCODE_CODE93); + } + + /* Barcode - Code128 */ + public function testBarcodeCode128ValidA() { + $this -> printer -> barcode("{A" . "012ABCD", Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x09{A012ABCD"); + } + + public function testBarcodeCode128ValidB() { + $this -> printer -> barcode("{B" . "012ABCDabcd", Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x0d{B012ABCDabcd"); + } + + public function testBarcodeCode128ValidC() { + $this -> printer -> barcode("{C" . chr ( 21 ) . chr ( 32 ) . chr ( 43 ), Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x05{C\x15 +"); + } + + public function testBarcodeCode128NoCodeSet() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("ABCD", Escpos::BARCODE_CODE128); + } + + /* Pulse */ + function testPulseDefault() { + $this -> printer -> pulse(); + $this -> checkOutput("\x1b@\x1bp0 printer -> pulse(1); + $this -> checkOutput("\x1b@\x1bp1 printer -> pulse(0, 2, 2); + $this -> checkOutput("\x1b@\x1bp0\x01\x01"); + } + + function testPulseOddMs() { + $this -> printer -> pulse(0, 3, 3); // Should be rounded down and give same output + $this -> checkOutput("\x1b@\x1bp0\x01\x01"); + } + + function testPulseTooHigh() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse(0, 512, 2); + } + + function testPulseTooLow() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse(0, 0, 2); + } + + function testPulseNotANumber() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse("fish"); + } + + /* Set reverse */ + public function testSetReverseColorsDefault() { + $this -> printer -> setReverseColors(); + $this -> checkOutput("\x1b@\x1dB\x01"); + } + + public function testSetReverseColorsOn() { + $this -> printer -> setReverseColors(true); + $this -> checkOutput("\x1b@\x1dB\x01"); + } + + public function testSetReverseColorsOff() { + $this -> printer -> setReverseColors(false); + $this -> checkOutput("\x1b@\x1dB\x00"); + } + + public function testSetReverseColorsNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setReverseColors(7); + } + + /* Bit image print */ + public function testBitImageBlack() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_black.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x01\x00\x80"); + } + + public function testBitImageWhite() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_white.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x01\x00\x00"); + } + + public function testBitImageBoth() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_white.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x02\x00\xc0\x00"); + } + + public function testBitImageTransparent() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_transparent.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x02\x00\xc0\x00"); + } + + /* Graphics print */ + public function testGraphicsWhite() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_white.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x00\x1d(L\x02\x0002"); + } + + public function testGraphicsBlack() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_black.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x80\x1d(L\x02\x0002"); + } + + public function testGraphicsBoth() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_white.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002"); + } + + public function testGraphicsTransparent() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_transparent.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002"); + } + + /* QR code */ + public function testQRCodeDefaults() { + // Test will fail if default values change + $this -> printer -> qrCode("1234"); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeDefaultsAreCorrect() { + // Below tests assume that defaults are as written here (output string should be same as above) + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 3, Escpos::QR_MODEL_2); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeEmpty() { + $this -> printer -> qrCode(''); + $this -> checkOutput("\x1b@"); // No commands actually sent + } + + public function testQRCodeChangeEC() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_H); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E3\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeChangeSize() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 7); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x07\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeChangeModel() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 3, Escpos::QR_MODEL_1); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A1\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + /* Feed form - Required on page-mode only printers */ + public function testFeedForm() { + $this -> printer -> feedForm(); + $this -> checkOutput("\x1b@\x0c"); + } + + /* Get status */ + public function testGetStatus() { + $this -> markTestIncomplete("Status check test code not implemented."); + // TODO some unit testing here on statuses + // $a = $this -> printer -> getPrinterStatus(Escpos::STATUS_PRINTER); + } + + /* Set text size */ + public function testSetTextSizeNormal() { + $this -> printer -> setTextSize(1, 1); + $this -> checkOutput("\x1b@\x1d!\x00"); + } + + public function testSetTextSizeWide() { + $this -> printer -> setTextSize(4, 1); + $this -> checkOutput("\x1b@\x1d!0"); + } + + public function testSetTextSizeNarrow() { + $this -> printer -> setTextSize(1, 4); + $this -> checkOutput("\x1b@\x1d!\x03"); + } + + public function testSetTextSizeLarge() { + $this -> printer -> setTextSize(4, 4); + $this -> checkOutput("\x1b@\x1d!3"); + } + + public function testSetTextSizeInvalid() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setTextSize(0, 9); + } +} + +/* + * For testing that string-castable objects are handled + */ +class FooBar { + private $foo; + public function __construct($foo) { + $this -> foo = $foo; + } + + public function __toString() { + return $this -> foo; + } +} +?> diff --git a/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php b/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php new file mode 100644 index 00000000000..392775d3277 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php @@ -0,0 +1,278 @@ + getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runWrite') + -> with($this -> equalTo(''), $this -> equalTo("LPT1")); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLptMac() { + // Cannot print to local printer on Mac with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLptLinux() { + // Cannot print to local printer on Linux with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComWindows() { + // Simple file write + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runWrite') + -> with($this -> equalTo(''), $this -> equalTo("COM1")); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComMac() { + // Cannot print to local printer on Mac with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComLinux() { + // Cannot print to local printer on Linux with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLocalShareWindows() { + $connector = $this -> getMockConnector("Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> stringContains('\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindows() { + $connector = $this -> getMockConnector("smb://example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsername() { + $connector = $this -> getMockConnector("smb://bob@example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:bob\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsernameDomain() { + $connector = $this -> getMockConnector("smb://bob@example-pc/home/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:home\\bob\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsernamePassword() { + $connector = $this -> getMockConnector("smb://bob:secret@example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:bob\' \'secret\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterMac() { + // Not implemented + $this -> setExpectedException('Exception'); + $connector = $this -> getMockConnector("smb://Guest@example-pc/Printer", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testSharedPrinterLinux() { + $connector = $this -> getMockConnector("smb://example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsername() { + $connector = $this -> getMockConnector("smb://bob@example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -U \'bob\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsernameDomain() { + $connector = $this -> getMockConnector("smb://bob@example-pc/home/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -U \'home\\bob\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsernamePassword() { + $connector = $this -> getMockConnector("smb://bob:secret@example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' \'secret\' -U \'bob\' -c \'print -\'')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + private function getMockConnector($path, $platform) { + $stub = $this -> getMockBuilder('WindowsPrintConnector') + -> setMethods(array('runCopy', 'runCommand', 'getCurrentPlatform', 'runWrite')) + -> disableOriginalConstructor() + -> getMock(); + $stub -> method('runCommand') + -> willReturn(0); + $stub -> method('runCopy') + -> willReturn(true); + $stub -> method('runWrite') + -> willReturn(true); + $stub -> method('getCurrentPlatform') + -> willReturn($platform); + $stub -> __construct($path); + return $stub; + } + + /** + * Test for correct identification of bogus or non-supported Samba strings. + */ + public function testSambaRegex() { + $good = array("smb://foo/bar", + "smb://foo/bar baz", + "smb://bob@foo/bar", + "smb://bob:secret@foo/bar", + "smb://foo-computer/FooPrinter", + "smb://foo-computer/workgroup/FooPrinter", + "smb://foo-computer/Foo-Printer", + "smb://foo-computer/workgroup/Foo-Printer", + "smb://foo-computer/Foo Printer"); + $bad = array("", + "http://google.com", + "smb:/foo/bar", + "smb://", + "smb:///bar", + "smb://@foo/bar", + "smb://bob:@foo/bar", + "smb://:secret@foo/bar", + "smb://foo/bar/baz/quux", + "smb://foo-computer//FooPrinter"); + foreach($good as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_SMB, $item) == 1, "Windows samba regex should pass '$item'."); + } + foreach($bad as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_SMB, $item) != 1, "Windows samba regex should fail '$item'."); + } + } + + public function testPrinterNameRegex() { + $good = array("a", + "ab", + "a b", + "a-b", + "Abcd Efg-", + "-a" + ); + $bad = array("", + " ", + "a ", + " a", + " a ", + "a/B", + "A:b" + ); + foreach($good as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_PRINTERNAME, $item) == 1, "Windows printer name regex should pass '$item'."); + } + foreach($bad as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_PRINTERNAME, $item) != 1, "Windows printer name regex should fail '$item'."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif new file mode 100644 index 0000000000000000000000000000000000000000..6c54bad9eea0d1cd39e55504d181b6607b6028d4 GIT binary patch literal 65 zcmZ?wbhEHbWMW`q_`m=Kia%Kx85kHD6#of27o{eaq^2m8XO?6rxO@5rFzA33fs`{a LF|!D<>&kwgnNLhfd<&=O9H5Y7iEBiObAE1aYF-J0b5UwyNotBh zd1gt5g1e`0KzJjcI8YJ4r;B3<$MxitgoGdG8+aJ_#26XR&Dh)tlw$C7^>bP0l+XkK D$`C0q literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0acbf66506f8ae152df4bfabc835e03505112878 GIT binary patch literal 138 ycmZ?r?P7p{Dj<~u#7t1k$RGih5CD?G+z<>F01{5_LB%jOu{6kBWbhvh5F!BXKMAt{ literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif new file mode 100644 index 0000000000000000000000000000000000000000..0a044a671f59c3c4a5f859eb6a09a71d809be683 GIT binary patch literal 65 zcmZ?wbhEHbWMW`q_`t{j1poj4SNzEWVlgQG6Lc<0O)N=GQ7F$W$xv|j^bKIp0m*=r NGcYl;2(dC)0{}064pjgE literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6539cece59e6c5ce41bf50f1ced121096ead5aed GIT binary patch literal 175 zcmex=>ukC3pCfH06P@c#e52OWR?s5zX<@pgBjQW literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png new file mode 100644 index 0000000000000000000000000000000000000000..33ba331fa1e8642247b0cd1e48e03e7bb24a624b GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScv!3p^r$G`BDaGcwGYBLNg-FY)ws zWxvnN$08&+x9_71P)M@GHKN2hKQ}iuuY|$5C^fMpHASI3vm`^o-P1Q9ypc~Fs7TDy o#W95AdU65;{Q3Xio|&1MfuD`(|Am>%Vn7uPp00i_>zopr06?22>i_@% literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2ee723c59d87be01c0901a2662fc414e7898d904 GIT binary patch literal 142 zcmZ?r?PGv|E+AC{#Eft(0hV9^ljy{M1_lTMN&I(m4=Qdr^i?Bahn%QF>uW{_V`Jfl cv@|t`v^2H_GiP$za9w9siRm{&HU*ox0Ab1$MF0Q* literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif new file mode 100644 index 0000000000000000000000000000000000000000..49b19dbc179f5995e4ff5338e7039d3a47b4a888 GIT binary patch literal 72 zcmZ?wbhEHbWMp7u_`t{j1poj4SNzEWVlgQG=l0A^Oi%SqOwUZt=1ot`%}um5&@(YL UF*Rk-0jU6KV_;%(VPvod0K2{rbpQYW literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d059f431b267596da878937214280915c89f51ee GIT binary patch literal 160 zcmZ9GK@P$|3!+y-7X048hz>mPHCTnRfR>gOhxmUMTR+ao=IO?3^7bqx(d49u-eEv*bK owG9oe3=Ayq=dVT4kei>9nO2EggGbYKL7)Z(Pgg&ebxsLQ05fwqK>z>% literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e47e0ef617583acde08ae6d8ddf8045fb1a4e55e GIT binary patch literal 142 zcmZ?r?PGv|E+AC{#Eft(0hV9^ljy{M1_lTMN&I(m4=Qdr^i?Bahn%QF>uW{_V`Jfl hv@|t`v^2H_GiP$za9w9siRm{&HU-_>|Ns9p003&i7eW94 literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif new file mode 100644 index 0000000000000000000000000000000000000000..7881ce629acf8d6918eb5ac07169bc67b99a904b GIT binary patch literal 72 zcmZ?wbhEHbWMp7u_`t{j1poj4SNzEWVlgQG=l0A^Oi%SqOwUZt=1ot`%}um5&@(YL UF*Rk-0jU6KV_;(PVPvod0K480eEnh z;e>7%gm;ZT^#P}}Pr|CgB3h=RdCVe19mdJ5nwEK)ZE4y3hRm@?RN;3VV)+M&*8W@g M3*4UIi0j**UsypDH2?qr literal 0 HcmV?d00001 diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png new file mode 100644 index 0000000000000000000000000000000000000000..4231a4bb5400c5554d146b0f8b32b717c109c1bf GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^j35jm7|ip2ssJgLbVpxD28NCO++aH!(Ecb readImage("doc.pdf[5]"); + $im -> destroy(); +} catch(ImagickException $e) { + echo "Error: " . $e -> getMessage() . "\n"; +} + +$im = new Imagick(); +try { + ob_start(); + @$im -> readImage("doc.pdf[5]"); + ob_end_clean(); + $im -> destroy(); +} catch(ImagickException $e) { + echo "Error: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf b/htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf new file mode 100644 index 0000000000000000000000000000000000000000..22247b918dbbcbfb0981d9442c966c99a47361b4 GIT binary patch literal 6681 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 bQ5=#CZ9g8L&gBcKi39DzR8)+usIdP4$C)m7 literal 0 HcmV?d00001 From bba3dc7dcd63f1c668c659f1f760b373c472c185 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 May 2016 13:39:57 +0200 Subject: [PATCH 726/834] Make composer cleaner --- build/composer/README | 7 +- composer.json | 4 +- composer.lock | 1233 ++++++- htdocs/includes/ckeditor/ckeditor/CHANGES.md | 2145 +++++------ htdocs/includes/ckeditor/ckeditor/LICENSE.md | 2840 +++++++------- .../ckeditor/ckeditor/build-config.js | 102 +- htdocs/includes/ckeditor/ckeditor/ckeditor.js | 2122 +++++------ .../includes/ckeditor/ckeditor/contents.css | 270 +- htdocs/includes/ckeditor/ckeditor/lang/af.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ar.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/bg.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/bn.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/bs.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ca.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/cs.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/cy.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/da.js | 8 +- .../includes/ckeditor/ckeditor/lang/de-ch.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/de.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/el.js | 8 +- .../includes/ckeditor/ckeditor/lang/en-au.js | 8 +- .../includes/ckeditor/ckeditor/lang/en-ca.js | 8 +- .../includes/ckeditor/ckeditor/lang/en-gb.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/en.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/eo.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/es.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/et.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/eu.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/fa.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/fi.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/fo.js | 8 +- .../includes/ckeditor/ckeditor/lang/fr-ca.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/fr.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/gl.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/gu.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/he.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/hi.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/hr.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/hu.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/id.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/is.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/it.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ja.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ka.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/km.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ko.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ku.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/lt.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/lv.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/mk.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/mn.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ms.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/nb.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/nl.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/no.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/pl.js | 8 +- .../includes/ckeditor/ckeditor/lang/pt-br.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/pt.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ro.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ru.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/si.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/sk.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/sl.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/sq.js | 8 +- .../ckeditor/ckeditor/lang/sr-latn.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/sr.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/sv.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/th.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/tr.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/tt.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/ug.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/uk.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/vi.js | 8 +- .../includes/ckeditor/ckeditor/lang/zh-cn.js | 8 +- htdocs/includes/ckeditor/ckeditor/lang/zh.js | 8 +- .../includes/ckeditor/ckeditor/package.json | 2 +- .../dialogs/lang/_translationstatus.txt | 50 +- .../colordialog/dialogs/colordialog.css | 20 + .../colordialog/dialogs/colordialog.js | 19 +- .../ckeditor/plugins/find/dialogs/find.js | 21 +- .../ckeditor/ckeditor/plugins/icons.png | Bin 16152 -> 20634 bytes .../ckeditor/ckeditor/plugins/icons_hidpi.png | Bin 56750 -> 67753 bytes .../ckeditor/plugins/preview/preview.html | 26 +- .../ckeditor/plugins/scayt/CHANGELOG.md | 40 +- .../ckeditor/plugins/scayt/LICENSE.md | 56 +- .../ckeditor/ckeditor/plugins/scayt/README.md | 50 +- .../plugins/scayt/dialogs/toolbar.css | 142 +- .../dialogs/lang/_translationstatus.txt | 40 +- .../plugins/templates/dialogs/templates.css | 168 +- .../ckeditor/ckeditor/plugins/wsc/LICENSE.md | 56 +- .../ckeditor/ckeditor/plugins/wsc/README.md | 50 +- .../ckeditor/plugins/wsc/dialogs/ciframe.html | 132 +- .../plugins/wsc/dialogs/tmpFrameset.html | 104 +- .../ckeditor/plugins/wsc/dialogs/wsc.css | 164 +- .../ckeditor/ckeditor/samples/css/samples.css | 3280 ++++++++--------- .../ckeditor/ckeditor/samples/index.html | 256 +- .../ckeditor/ckeditor/samples/js/sample.js | 106 +- .../ckeditor/ckeditor/samples/old/ajax.html | 170 +- .../ckeditor/ckeditor/samples/old/api.html | 420 +-- .../ckeditor/samples/old/appendto.html | 118 +- .../old/assets/outputxhtml/outputxhtml.css | 408 +- .../samples/old/assets/posteddata.php | 118 +- .../ckeditor/samples/old/datafiltering.html | 1016 ++--- .../samples/old/dialog/assets/my_dialog.js | 96 +- .../ckeditor/samples/old/dialog/dialog.html | 380 +- .../ckeditor/samples/old/divreplace.html | 288 +- .../samples/old/enterkey/enterkey.html | 212 +- .../old/htmlwriter/outputforflash.html | 566 +-- .../samples/old/htmlwriter/outputhtml.html | 448 +-- .../ckeditor/ckeditor/samples/old/index.html | 224 +- .../ckeditor/samples/old/inlineall.html | 628 ++-- .../ckeditor/samples/old/inlinebycode.html | 248 +- .../ckeditor/samples/old/inlinetextarea.html | 226 +- .../ckeditor/ckeditor/samples/old/jquery.html | 206 +- .../samples/old/magicline/magicline.html | 418 +-- .../ckeditor/samples/old/readonly.html | 152 +- .../ckeditor/samples/old/replacebyclass.html | 120 +- .../ckeditor/samples/old/replacebycode.html | 118 +- .../ckeditor/ckeditor/samples/old/sample.css | 714 ++-- .../ckeditor/ckeditor/samples/old/sample.js | 100 +- .../samples/old/sample_posteddata.php | 32 +- .../ckeditor/samples/old/tabindex.html | 156 +- .../ckeditor/samples/old/toolbar/toolbar.html | 470 +-- .../ckeditor/samples/old/uicolor.html | 144 +- .../ckeditor/samples/old/uilanguages.html | 244 +- .../samples/old/wysiwygarea/fullpage.html | 160 +- .../ckeditor/samples/old/xhtmlstyle.html | 468 +-- .../toolbarconfigurator/css/fontello.css | 110 +- .../toolbarconfigurator/font/LICENSE.txt | 20 +- .../samples/toolbarconfigurator/index.html | 892 ++--- .../lib/codemirror/codemirror.css | 650 ++-- .../lib/codemirror/neo.css | 72 +- .../lib/codemirror/show-hint.css | 76 +- .../ckeditor/ckeditor/skins/moono/dialog.css | 10 +- .../ckeditor/skins/moono/dialog_ie.css | 10 +- .../ckeditor/skins/moono/dialog_ie7.css | 10 +- .../ckeditor/skins/moono/dialog_ie8.css | 10 +- .../ckeditor/skins/moono/dialog_iequirks.css | 10 +- .../ckeditor/ckeditor/skins/moono/editor.css | 10 +- .../ckeditor/skins/moono/editor_gecko.css | 10 +- .../ckeditor/skins/moono/editor_ie.css | 10 +- .../ckeditor/skins/moono/editor_ie7.css | 10 +- .../ckeditor/skins/moono/editor_ie8.css | 10 +- .../ckeditor/skins/moono/editor_iequirks.css | 10 +- .../ckeditor/ckeditor/skins/moono/icons.png | Bin 16152 -> 20634 bytes .../ckeditor/skins/moono/icons_hidpi.png | Bin 56750 -> 67753 bytes .../ckeditor/ckeditor/skins/moono/readme.md | 98 +- htdocs/includes/ckeditor/ckeditor/styles.js | 222 +- 148 files changed, 13279 insertions(+), 12080 deletions(-) create mode 100644 htdocs/includes/ckeditor/ckeditor/plugins/colordialog/dialogs/colordialog.css diff --git a/build/composer/README b/build/composer/README index 2464b0d246e..d58359defec 100644 --- a/build/composer/README +++ b/build/composer/README @@ -1,5 +1,10 @@ -To upgrade a lib with composer: +To test upgrade of a lib with composer: composer update --no-dev --no-autoloader --dry-run ccampbell/chromephp +To upgrade a lib with composer: + +composer update --no-dev --no-autoloader ccampbell/chromephp + + diff --git a/composer.json b/composer.json index ab60ce4f1b2..113a04e3163 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,9 @@ "require": { "php": ">=5.3.0", "ext-curl": "*", - "ccampbell/chromephp": "^4.1", + "ccampbell/chromephp": "4.1.0", "ckeditor/ckeditor": "dev-full/stable", - "mike42/escpos-php": "dev-master", + "mike42/escpos-php": "1.2.1", "mobiledetect/mobiledetectlib": "2.8.17", "phpoffice/phpexcel": "1.8.1", "restler/framework": "3.0.0-RC6", diff --git a/composer.lock b/composer.lock index df333f5571c..b0771bd8054 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c2b53c577364dbe3a56137043081b511", - "content-hash": "8f7a86cfbc13f45e13b73c49531818cb", + "hash": "b4b2182f7db9f00a023fce81370be0bb", + "content-hash": "8d110e7d8fca6eca1aa814ee35d0032b", "packages": [ { "name": "ccampbell/chromephp", @@ -56,12 +56,12 @@ "source": { "type": "git", "url": "https://github.com/ckeditor/ckeditor-releases.git", - "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0" + "reference": "4a7a6d717f9a408fa8f9ea53ef2dba4d64b83e91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", - "reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0", + "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/4a7a6d717f9a408fa8f9ea53ef2dba4d64b83e91", + "reference": "4a7a6d717f9a408fa8f9ea53ef2dba4d64b83e91", "shasum": "" }, "type": "library", @@ -89,27 +89,28 @@ "text", "wysiwyg" ], - "time": "2016-03-31 16:19:25" + "time": "2016-05-12 15:36:04" }, { "name": "mike42/escpos-php", - "version": "dev-master", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/mike42/escpos-php.git", - "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788" + "reference": "cfea4c4fc95516ac953e1e5b623f854632afa2ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mike42/escpos-php/zipball/96f05cbf460f5b67c2184ee4e91aedfbcedeb788", - "reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788", + "url": "https://api.github.com/repos/mike42/escpos-php/zipball/cfea4c4fc95516ac953e1e5b623f854632afa2ee", + "reference": "cfea4c4fc95516ac953e1e5b623f854632afa2ee", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "4.5.*" + "phpunit/phpunit": "4.5.*", + "squizlabs/php_codesniffer": "2.*" }, "type": "library", "autoload": { @@ -152,7 +153,7 @@ "print", "receipt" ], - "time": "2016-03-27 23:08:27" + "time": "2016-04-25 01:14:07" }, { "name": "mobiledetect/mobiledetectlib", @@ -272,12 +273,12 @@ "source": { "type": "git", "url": "https://github.com/Luracast/Restler-Framework.git", - "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b" + "reference": "bfe1139b233852b745c6a0ec14d7244ceb3b3fc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/6ee10b3e5dbc6376916fed55ec2340a37cce436b", - "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b", + "url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/bfe1139b233852b745c6a0ec14d7244ceb3b3fc6", + "reference": "bfe1139b233852b745c6a0ec14d7244ceb3b3fc6", "shasum": "" }, "require": { @@ -286,23 +287,14 @@ "replace": { "luracast/restler": "3.*" }, - "require-dev": { - "bshaffer/oauth2-server-php": "v1.0", - "luracast/explorer": "*", - "mustache/mustache": "dev-master", - "rodneyrehm/plist": "dev-master", - "symfony/yaml": "*", - "twig/twig": "v1.13.0", - "zendframework/zendamf": "dev-master" - }, "suggest": { - "bshaffer/oauth2-server-php": "Restler can provide OAuth2 authentication using this library (see require-dev for details)", - "luracast/explorer": "Restler's very own api explorer (see require-dev for details)", - "mustache/mustache": "Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)", - "rodneyrehm/plist": "Restler supports tho Apple plist xml format (see require-dev for details)", - "symfony/yaml": "Restler can produce content in yaml format as well (see require-dev for details)", - "twig/twig": "Restler can render HtmlView using twig templates (see require-dev for details)", - "zendframework/zendamf": "Support for the amf document format (see require-dev for details)" + "bshaffer/oauth2-server-php": "If you want to use OAuth2 for authentication", + "illuminate/view": "If you want to use laravel blade templates with Html format", + "mustache/mustache": "If you want to use mustache/handlebar templates with Html format", + "rodneyrehm/plist": "If you need Apple plist binary/xml format", + "symfony/yaml": "If you need YAML format", + "twig/twig": "If you want to use twig templates with Html format", + "zendframework/zendamf": "If you need AMF format" }, "type": "library", "extra": { @@ -323,10 +315,6 @@ { "name": "Luracast", "email": "arul@luracast.com" - }, - { - "name": "Nick nickl- Lombard", - "email": "github@jigsoft.co.za" } ], "description": "Just the Restler Framework without the tests and examples", @@ -337,7 +325,7 @@ "rest", "server" ], - "time": "2015-08-04 07:52:49" + "time": "2016-02-28 15:57:37" }, { "name": "tecnickcom/tcpdf", @@ -403,12 +391,1181 @@ "time": "2015-09-12 10:08:34" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "jakub-onderka/php-console-color", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/e0b393dacf7703fc36a4efc3df1435485197e6c1", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "jakub-onderka/php-code-style": "1.0", + "jakub-onderka/php-parallel-lint": "0.*", + "jakub-onderka/php-var-dump-check": "0.*", + "phpunit/phpunit": "3.7.*", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleColor": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com", + "homepage": "http://www.acci.cz" + } + ], + "time": "2014-04-08 15:00:19" + }, + { + "name": "jakub-onderka/php-console-highlighter", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "shasum": "" + }, + "require": { + "jakub-onderka/php-console-color": "~0.1", + "php": ">=5.3.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "~1.0", + "jakub-onderka/php-parallel-lint": "~0.5", + "jakub-onderka/php-var-dump-check": "~0.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleHighlighter": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "time": "2015-04-20 18:58:01" + }, + { + "name": "jakub-onderka/php-parallel-lint", + "version": "v0.9.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git", + "reference": "2ead2e4043ab125bee9554f356e0a86742c2d4fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/2ead2e4043ab125bee9554f356e0a86742c2d4fa", + "reference": "2ead2e4043ab125bee9554f356e0a86742c2d4fa", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "jakub-onderka/php-console-highlighter": "~0.3", + "nette/tester": "~1.3" + }, + "suggest": { + "jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet" + }, + "bin": [ + "parallel-lint" + ], + "type": "library", + "autoload": { + "classmap": [ + "./" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com" + } + ], + "description": "This tool check syntax of PHP files about 20x faster than serial check.", + "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint", + "time": "2015-12-15 10:42:16" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/prophecy", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", + "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2016-02-15 07:46:21" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-06-21 13:08:43" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4|~5" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2016-05-12 18:03:57" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-09-15 10:49:45" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", + "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2016-05-17 03:09:28" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" + }, + { + "name": "sebastian/comparator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "1.3.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", + "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-05-17 03:18:57" + }, + { + "name": "sebastian/exporter", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-06-21 07:55:53" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-11-11 19:50:13" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "1bcdf03b068a530ac1962ce671dead356eeba43b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1bcdf03b068a530ac1962ce671dead356eeba43b", + "reference": "1bcdf03b068a530ac1962ce671dead356eeba43b", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "scripts/phpcs", + "scripts/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "classmap": [ + "CodeSniffer.php", + "CodeSniffer/CLI.php", + "CodeSniffer/Exception.php", + "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", + "CodeSniffer/Report.php", + "CodeSniffer/Reporting.php", + "CodeSniffer/Sniff.php", + "CodeSniffer/Tokens.php", + "CodeSniffer/Reports/", + "CodeSniffer/Tokenizers/", + "CodeSniffer/DocGenerators/", + "CodeSniffer/Standards/AbstractPatternSniff.php", + "CodeSniffer/Standards/AbstractScopeSniff.php", + "CodeSniffer/Standards/AbstractVariableSniff.php", + "CodeSniffer/Standards/IncorrectPatternException.php", + "CodeSniffer/Standards/Generic/Sniffs/", + "CodeSniffer/Standards/MySource/Sniffs/", + "CodeSniffer/Standards/PEAR/Sniffs/", + "CodeSniffer/Standards/PSR1/Sniffs/", + "CodeSniffer/Standards/PSR2/Sniffs/", + "CodeSniffer/Standards/Squiz/Sniffs/", + "CodeSniffer/Standards/Zend/Sniffs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2016-04-03 22:58:34" + }, + { + "name": "symfony/yaml", + "version": "v3.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "0047c8366744a16de7516622c5b7355336afae96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0047c8366744a16de7516622c5b7355336afae96", + "reference": "0047c8366744a16de7516622c5b7355336afae96", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-03-04 07:55:57" + } + ], "aliases": [], "minimum-stability": "stable", "stability-flags": { "ckeditor/ckeditor": 20, - "mike42/escpos-php": 20 + "restler/framework": 5 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/htdocs/includes/ckeditor/ckeditor/CHANGES.md b/htdocs/includes/ckeditor/ckeditor/CHANGES.md index f751de99327..e48aaac4dc2 100644 --- a/htdocs/includes/ckeditor/ckeditor/CHANGES.md +++ b/htdocs/includes/ckeditor/ckeditor/CHANGES.md @@ -1,1065 +1,1080 @@ -CKEditor 4 Changelog -==================== - -## CKEditor 4.5.8 - -New Features: - -* [#12440](http://dev.ckeditor.com/ticket/12440): Added the [`config.colorButton_enableAutomatic`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-colorButton_enableAutomatic) option to allow hiding the "Automatic" option in the [color picker](http://ckeditor.com/addon/colorbutton). - -Fixed Issues: - -* [#10448](http://dev.ckeditor.com/ticket/10448): Fixed: Lack of scrollbar in the [right-to-left text direction](http://ckeditor.com/addon/bidi). -* [#12707](http://dev.ckeditor.com/ticket/12707): Fixed: The order of table elements does not comply with the HTML specification. -* [#13756](http://dev.ckeditor.com/ticket/13756): [Edge] Fixed: Context menus are cut-off. - -## CKEditor 4.5.7 - -New Features: - -* [#14327](http://dev.ckeditor.com/ticket/14327): Added Swiss German localization. Thanks to [Miro Grenda](https://twitter.com/mirogrenda)! - -Fixed Issues: - -* [#13816](http://dev.ckeditor.com/ticket/13816): Introduced a new strategy for Filling Character handling to avoid changes in DOM. This fixes the following issues: - * [#12727](http://dev.ckeditor.com/ticket/12727): [Blink] `IndexSizeError` when using the [Div Editing Area](http://ckeditor.com/addon/divarea) and [Content Templates](http://ckeditor.com/addon/templates) plugins. - * [#13377](http://dev.ckeditor.com/ticket/13377): [Widget](http://ckeditor.com/addon/widget) plugin issue when typing in Korean. - * [#13389](http://dev.ckeditor.com/ticket/13389): [Blink] [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) fails when the cursor is next to an `


    ` tag. - * [#13513](http://dev.ckeditor.com/ticket/13513): [Blink, WebKit] [Div Editing Area](http://ckeditor.com/addon/divarea) and [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) throw an error when an image is the only data in the editor. -* [#13884](http://dev.ckeditor.com/ticket/13884): [Firefox] Fixed: Copying and pasting a table results in just the first cell being pasted. -* [#14234](http://dev.ckeditor.com/ticket/14234): Fixed: URL input field is not marked as required in the [Media Embed](http://ckeditor.com/addon/embed) dialog. - -## CKEditor 4.5.6 - -New Features: - -* Introduced the [`CKEDITOR.tools.getCookie()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-getCookie) and [`CKEDITOR.tools.setCookie()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-setCookie) methods for accessing cookies. -* Introduced the [`CKEDITOR.tools.getCsrfToken()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-getCsrfToken) method. The CSRF token is now automatically sent by the [File Browser](http://ckeditor.com/addon/filebrowser) and [File Tools](http://ckeditor.com/addon/filetools) plugins during file uploads. The server-side upload handlers may check it and use it to additionally secure the communication. - -Other Changes: - -* Updated [SCAYT](http://ckeditor.com/addon/scayt) (Spell Check As You Type): - - New features: - - CKEditor [Language](http://ckeditor.com/addon/language) plugin support. - - CKEditor [Placeholder](http://ckeditor.com/addon/placeholder) plugin support. - - [Drag&Drop](http://sdk.ckeditor.com/samples/fileupload.html) support. - - **Experimental** [GRAYT](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-grayt_autoStartup) (Grammar As You Type) functionality. - - Fixed issues: - * [#98](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/98): SCAYT affects dialog double-click. Fixed in SCAYT core. - * [#102](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/102): SCAYT core performance enhancements. - * [#104](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/104): SCAYT's spans leak into the clipboard and after pasting. - * [#105](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/105): A JavaScript error fired in case of multiple instances of CKEditor on one page. - * [#107](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/107): SCAYT should not check non-editable parts of content. - * [#108](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/108): Latest SCAYT copies the ID of the editor element to the iframe. - * SCAYT stops working when CKEditor [Undo plugin](http://ckeditor.com/addon/undo) not enabled. - * Issue with pasting SCAYT markup in CKEditor. - * SCAYT stops working after pressing the *Cancel* button in the WSC dialog. - -## CKEditor 4.5.5 - -Fixed Issues: - -* [#13887](https://dev.ckeditor.com/ticket/13887): Fixed: [Link](http://ckeditor.com/addon/link) plugin alters the `target` attribute value. Thanks to [SamZiemer](https://github.com/SamZiemer)! -* [#12189](http://dev.ckeditor.com/ticket/12189): Fixed: The [Link](http://ckeditor.com/addon/link) plugin dialog does not display the subject of email links if the subject parameter is not lowercase. -* [#9192](http://dev.ckeditor.com/ticket/9192): Fixed: An `undefined` string is appended to an email address added with the [Link](http://ckeditor.com/addon/link) plugin if subject and email body are empty and [`config.emailProtection`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-emailProtection) is set to `encode`. -* [#13790](https://dev.ckeditor.com/ticket/13790): Fixed: It is not possible to destroy the editor `