diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index e96c5ef4ba049..c685645c504c4 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -63,13 +63,21 @@ jobs: # files: | # **.php + - name: Setup PHPCS + uses: shivammathur/setup-php@v2 + if: steps.changed-php.outputs.any_changed == 'true' + with: + php-version: 8.1 + coverage: none # disable xdebug, pcov + tools: phpcs + - name: Run some pre-commit hooks on selected changed files only if: steps.changed-php.outputs.any_changed == 'true' env: ALL_CHANGED_FILES: ${{ steps.changed-php.outputs.all_changed_files }} run: | set -o pipefail - pre-commit run php-cs ${ALL_CHANGED_FILES} | tee -a ${RAW_LOG} + pre-commit run php-cs --files ${ALL_CHANGED_FILES} | tee -a ${RAW_LOG} # If error, we convert log in the checkstyle format - name: Convert Raw Log to CheckStyle format diff --git a/ChangeLog b/ChangeLog index 9b9664a1cf622..b19c131511eaf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -187,15 +187,8 @@ NEW: updating for display Help title when try to delete Don (issue #25314) NEW: Upgrade in module builder in menu section NEW: use account address in sepa mandate (#23642) NEW: VAT rate - Add entity -<<<<<<< HEAD -NEW: webportal site account NEW: When an user unset the batch management of products, transformation of each batch stock movement in global stock movement - -FIX: #25828 wrong odt style naming -======= -NEW: When an user unset the batch management of products, transformation of each batch stock mouvement in global stock mouvement NEW: PDF Generation for each Human Resource Evaluations. ->>>>>>> branch '19.0' of git@github.com:Dolibarr/dolibarr.git SEC: #25512 applicative anti bruteforce - security on too many login attempts (#25520) SEC: Add action confirm_... as sensitive to need a CSRF token diff --git a/build/docker/Dockerfile b/build/docker/Dockerfile index 69f4d27b26fb7..5cd4c60673b95 100644 --- a/build/docker/Dockerfile +++ b/build/docker/Dockerfile @@ -50,7 +50,7 @@ RUN echo 'xdebug.idekey="netbeans-xdebug"' >> ${PHP_INI_DIR}/php.ini # set up sendmail config, to use maildev RUN echo "account default" > /etc/msmtprc RUN echo "auth off" >> /etc/msmtprc -RUN echo "port 25" >> /etc/msmtprc +RUN echo "port 1025" >> /etc/msmtprc RUN echo "host mail" >> /etc/msmtprc RUN echo "from local@localdomain.com" >> /etc/msmtprc RUN echo "domain localhost.localdomain" >> /etc/msmtprc diff --git a/build/docker/docker-compose.yml b/build/docker/docker-compose.yml index ddcdc58560a3a..cc2988a67d19e 100644 --- a/build/docker/docker-compose.yml +++ b/build/docker/docker-compose.yml @@ -55,8 +55,8 @@ services: mail: image: maildev/maildev ports: - - "8081:80" - - "25:25" + - "8081:1080" + - "25:1025" networks: - internal-pod - external-pod diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index 1af279d33d466..7f41b76e564b9 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -2069,6 +2069,14 @@ function initfieldrequired() { // Show online payment link $useonlinepayment = (isModEnabled('paypal') || isModEnabled('stripe') || isModEnabled('paybox')); + $parameters = array(); + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } else { + $useonlinepayment = $reshook; + } + if ($useonlinepayment) { print '
'; if (empty($amount)) { // Take the maximum amount among what the member is supposed to pay / has paid in the past diff --git a/htdocs/adherents/subscription.php b/htdocs/adherents/subscription.php index 7c6647835d82f..3d479cd23c666 100644 --- a/htdocs/adherents/subscription.php +++ b/htdocs/adherents/subscription.php @@ -825,6 +825,14 @@ // Shon online payment link $useonlinepayment = (isModEnabled('paypal') || isModEnabled('stripe') || isModEnabled('paybox')); + $parameters = array(); + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment = $hookmanager->resArray['showonlinepaymenturl']; + } + } + if ($useonlinepayment) { print '
'; diff --git a/htdocs/admin/ticket_public.php b/htdocs/admin/ticket_public.php index e083c87d205e3..e58b0599b7601 100644 --- a/htdocs/admin/ticket_public.php +++ b/htdocs/admin/ticket_public.php @@ -476,12 +476,12 @@ // Url public interface $url_interface = getDolGlobalString("TICKET_URL_PUBLIC_INTERFACE"); - print ''.$langs->trans("TicketUrlPublicInterfaceLabelAdmin").''; + print ''.$langs->trans("UrlPublicInterfaceLabelAdmin").''; print ''; - print ''; + print ''; print ''; print ''; - print $form->textwithpicto('', $langs->trans("TicketUrlPublicInterfaceHelpAdmin"), 1, 'help'); + print $form->textwithpicto('', $langs->trans("UrlPublicInterfaceHelpAdmin"), 1, 'help'); print ''; print ''; diff --git a/htdocs/ai/admin/setup.php b/htdocs/ai/admin/setup.php index af410762a8840..33d4954ea2d01 100644 --- a/htdocs/ai/admin/setup.php +++ b/htdocs/ai/admin/setup.php @@ -61,7 +61,7 @@ $arrayofia = array('chatgpt'); -foreach($arrayofia as $ia) { +foreach ($arrayofia as $ia) { // Setup conf AI_PUBLIC_INTERFACE_TOPIC $item = $formSetup->newItem('AI_KEY_API_'.strtoupper($ia)); $item->defaultFieldValue = ''; diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index fc72d6a7f8a5b..cae96460a93bc 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -3090,6 +3090,15 @@ // Show online payment link $useonlinepayment = (isModEnabled('paypal') || isModEnabled('stripe') || isModEnabled('paybox')); + + $parameters = array(); + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment = $hookmanager->resArray['showonlinepaymenturl']; + } + } + if (getDolGlobalString('ORDER_HIDE_ONLINE_PAYMENT_ON_ORDER')) { $useonlinepayment = 0; } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 64d2f2637aefc..55f4876fee286 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -5993,6 +5993,14 @@ function js_recalculate_revenuestamp(){ // Show online payment link $useonlinepayment = (isModEnabled('paypal') || isModEnabled('stripe') || isModEnabled('paybox')); + $parameters = array(); + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment = $hookmanager->resArray['showonlinepaymenturl']; + } + } + if ($object->status != Facture::STATUS_DRAFT && $useonlinepayment) { print '
'."\n"; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; diff --git a/htdocs/core/class/CMailFile.class.php b/htdocs/core/class/CMailFile.class.php index a7eed92b727c7..d52e5139da72a 100644 --- a/htdocs/core/class/CMailFile.class.php +++ b/htdocs/core/class/CMailFile.class.php @@ -1644,9 +1644,9 @@ private function write_files($filename_list, $mimetype_list, $mimefilename_list, $filename_list_size = count($filename_list); for ($i = 0; $i < $filename_list_size; $i++) { if ($filename_list[$i]) { - dol_syslog("CMailFile::write_files: i=$i"); + dol_syslog("CMailFile::write_files: i=$i ".$filename_list[$i]); $encoded = $this->_encode_file($filename_list[$i]); - if ($encoded >= 0) { + if ($encoded !== -1) { if ($mimefilename_list[$i]) { $filename_list[$i] = $mimefilename_list[$i]; } diff --git a/htdocs/core/class/commondocgenerator.class.php b/htdocs/core/class/commondocgenerator.class.php index aa7ae9144ae5d..d87123a97c946 100644 --- a/htdocs/core/class/commondocgenerator.class.php +++ b/htdocs/core/class/commondocgenerator.class.php @@ -616,6 +616,9 @@ public function get_substitutionarray_object($object, $outputlangs, $array_key = $resarray[$array_key.'_bank_label'] = (empty($bank_account) ? '' : $bank_account->label); $resarray[$array_key.'_bank_number'] = (empty($bank_account) ? '' : $bank_account->number); $resarray[$array_key.'_bank_proprio'] =(empty($bank_account) ? '' : $bank_account->proprio); + $resarray[$array_key.'_bank_address'] =(empty($bank_account) ? '' : $bank_account->address); + $resarray[$array_key.'_bank_state'] =(empty($bank_account) ? '' : $bank_account->state); + $resarray[$array_key.'_bank_country'] =(empty($bank_account) ? '' : $bank_account->country); } if (method_exists($object, 'getTotalDiscount') && in_array(get_class($object), array('Propal', 'Proposal', 'Commande', 'Facture', 'SupplierProposal', 'CommandeFournisseur', 'FactureFournisseur'))) { @@ -808,10 +811,11 @@ public function get_substitutionarray_lines($line, $outputlangs, $linenumber = 0 } else { // Set unused placeholders as blank $extrafields->fetch_name_optionals_label("product"); - $extralabels = $extrafields->attributes["product"]['label']; - - foreach ($extralabels as $key => $label) { - $resarray['line_product_options_'.$key] = ''; + if ($extrafields->attributes["product"]['count'] > 0) { + $extralabels = $extrafields->attributes["product"]['label']; + foreach ($extralabels as $key => $label) { + $resarray['line_product_options_'.$key] = ''; + } } } @@ -946,7 +950,7 @@ public function fill_substitutionarray_with_extrafields($object, $array_to_fill, // phpcs:enable global $conf; - if (is_array($extrafields->attributes[$object->table_element]['label'])) { + if ($extrafields->attributes[$object->table_element]['count'] > 0) { foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) { $formatedarrayoption = $object->array_options; diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index 92afb54af522c..e8dd4158b600e 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -534,6 +534,9 @@ public function setValues($db) } // For mycompany storage + $this->mycompany->multidir_output = array($this->entity => $rootfordata."/mycompany"); + $this->mycompany->multidir_temp = array($this->entity => $rootfortemp."/mycompany/temp"); + // For backward compatibility $this->mycompany->dir_output = $rootfordata."/mycompany"; $this->mycompany->dir_temp = $rootfortemp."/mycompany/temp"; diff --git a/htdocs/core/class/html.formticket.class.php b/htdocs/core/class/html.formticket.class.php index 4a91f083f41df..23c2f0751f685 100644 --- a/htdocs/core/class/html.formticket.class.php +++ b/htdocs/core/class/html.formticket.class.php @@ -1502,7 +1502,7 @@ public function showMessageForm($width = '40%') } print ''.$langs->trans('Subject').''; if (empty($topic)) { - print 'ref.'] '.$langs->trans('TicketNewMessage').'" />'; + print 'ref.'] '. $ticketstat->subject .'" />'; } else { print 'ref.'] '.$topic.'" />'; } diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index c842276835df0..f5d930f26b6f9 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -2194,9 +2194,8 @@ function dol_convert_file($fileinput, $ext = 'png', $fileoutput = '', $page = '' */ function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring = null) { - global $conf; - $foundhandler = 0; + //var_dump(basename($inputfile)); exit; try { dol_syslog("dol_compress_file mode=".$mode." inputfile=".$inputfile." outputfile=".$outputfile); @@ -2265,6 +2264,7 @@ function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php'; $archive = new PclZip($outputfile); + $result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile)); if ($result === 0) { @@ -2439,7 +2439,7 @@ function dol_uncompress($inputfile, $outputdir) * @param string $inputdir Source dir name * @param string $outputfile Target file name (output directory must exists and be writable) * @param string $mode 'zip' - * @param string $excludefiles A regex pattern. For example: '/\.log$|\/temp\//' + * @param string $excludefiles A regex pattern to exclude files. For example: '/\.log$|\/temp\//' * @param string $rootdirinzip Add a root dir level in zip file * @param string $newmask Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666' * @return int Return integer <0 if KO, >0 if OK @@ -2447,8 +2447,6 @@ function dol_uncompress($inputfile, $outputdir) */ function dol_compress_dir($inputdir, $outputfile, $mode = "zip", $excludefiles = '', $rootdirinzip = '', $newmask = 0) { - global $conf; - $foundhandler = 0; dol_syslog("Try to zip dir ".$inputdir." into ".$outputfile." mode=".$mode); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index cff1b5b85aabe..37c95a9574eb2 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -8175,6 +8175,10 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, '__MYCOMPANY_PROFID4__' => $mysoc->idprof4, '__MYCOMPANY_PROFID5__' => $mysoc->idprof5, '__MYCOMPANY_PROFID6__' => $mysoc->idprof6, + '__MYCOMPANY_PROFID7__' => $mysoc->idprof7, + '__MYCOMPANY_PROFID8__' => $mysoc->idprof8, + '__MYCOMPANY_PROFID9__' => $mysoc->idprof9, + '__MYCOMPANY_PROFID10__' => $mysoc->idprof10, '__MYCOMPANY_CAPITAL__' => $mysoc->capital, '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass '__MYCOMPANY_ADDRESS__' => $mysoc->address, @@ -8217,6 +8221,10 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__'; $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__'; $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__'; + $substitutionarray['__MYCOMPANY_PROFID7__'] = '__MYCOMPANY_PROFID7__'; + $substitutionarray['__MYCOMPANY_PROFID8__'] = '__MYCOMPANY_PROFID8__'; + $substitutionarray['__MYCOMPANY_PROFID9__'] = '__MYCOMPANY_PROFID9__'; + $substitutionarray['__MYCOMPANY_PROFID10__'] = '__MYCOMPANY_PROFID10__'; $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__'; $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__'; $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__'; diff --git a/htdocs/core/lib/profid.lib.php b/htdocs/core/lib/profid.lib.php index b3d705a1860aa..4523c8ee2f6a3 100644 --- a/htdocs/core/lib/profid.lib.php +++ b/htdocs/core/lib/profid.lib.php @@ -165,12 +165,12 @@ function isValidTinForBE($str) /** * Check the syntax validity of a Spanish (ES) Tax Identification Number (TIN), where: - * - NIF = Número de Identificación Fiscal - * - CIF = Código de Identificación Fiscal + * - NIF = Número de Identificación Fiscal (used for residents only before 2008. Used for both residents and companies since 2008.) + * - CIF = Código de Identificación Fiscal (used for companies only before 2008. Replaced by NIF since 2008.) * - NIE = Número de Identidad de Extranjero * * @param string $str TIN to check - * @return int 1 if NIF ok, 2 if CIF ok, 3 if NIE ok, -1 if NIF bad, -2 if CIF bad, -3 if NIE bad, 0 if unexpected bad + * @return int 1 if NIF ok, 2 if CIF ok, 3 if NIE ok, -1 if NIF bad, -2 if CIF bad, -3 if NIE bad, -4 if unexpected bad * @since Dolibarr V20 */ function isValidTinForES($str) diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 09d631d6d1caf..f0ac60ee21212 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -1548,6 +1548,7 @@ function get_left_menu_billing($mainmenu, &$newmenu, $usemenuhider = 1, $leftmen if ($usemenuhider || empty($leftmenu) || $leftmenu == "donations") { $newmenu->add("/don/card.php?leftmenu=donations&action=create", $langs->trans("NewDonation"), 1, $user->hasRight('don', 'creer')); $newmenu->add("/don/list.php?leftmenu=donations", $langs->trans("List"), 1, $user->hasRight('don', 'lire')); + $newmenu->add("/don/paiement/list.php?leftmenu=donations", $langs->trans("Payments"), 1, $user->hasRight('don', 'lire')); } // if ($leftmenu=="donations") $newmenu->add("/don/stats/index.php",$langs->trans("Statistics"), 1, $user->hasRight('don', 'lire')); } diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index 6c58f02900ac9..03d02a41b88ae 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -264,11 +264,11 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede // Definition of $dir and $file if ($object->specimen) { - $dir = empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity]; + $dir = empty($conf->facture->multidir_output[$object->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$object->entity]; $file = $dir."/SPECIMEN.pdf"; } else { $objectref = dol_sanitizeFileName($object->ref); - $dir = (empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity])."/".$objectref; + $dir = (empty($conf->facture->multidir_output[$object->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$object->entity])."/".$objectref; $file = $dir."/".$objectref.".pdf"; } if (!file_exists($dir)) { @@ -1111,7 +1111,7 @@ protected function _tableau_versements_header($pdf, $object, $outputlangs, $defa protected function _tableau_info(&$pdf, $object, $posy, $outputlangs, $outputlangsbis) { // phpcs:enable - global $conf, $mysoc; + global $conf, $mysoc, $hookmanager; $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1238,6 +1238,14 @@ protected function _tableau_info(&$pdf, $object, $posy, $outputlangs, $outputlan if (isModEnabled('paybox')) { $useonlinepayment++; } + $parameters = array(); + $action = ''; + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment += $hookmanager->resArray['showonlinepaymenturl']; + } + } } if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) { diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php index 1d6b8104d873a..7f54da1fd99a2 100644 --- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -1204,7 +1204,7 @@ public function drawPaymentsTable(&$pdf, $object, $posy, $outputlangs) */ protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlangsbis) { - global $conf, $mysoc; + global $conf, $mysoc, $hookmanager; $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1333,6 +1333,14 @@ protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlan if (isModEnabled('paybox')) { $useonlinepayment++; } + $parameters = array(); + $action = ''; + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment += $hookmanager->resArray['showonlinepaymenturl']; + } + } } diff --git a/htdocs/core/modules/modWebPortal.class.php b/htdocs/core/modules/modWebPortal.class.php index 6bd2885f31a45..99f31d1907fe4 100644 --- a/htdocs/core/modules/modWebPortal.class.php +++ b/htdocs/core/modules/modWebPortal.class.php @@ -280,6 +280,7 @@ public function __construct($db) $this->menu = array(); $r = 0; // Add here entries to declare new menus + /* $this->menu[$r++] = array( 'fk_menu' => '', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode 'type' => 'top', // This is a Top menu entry @@ -295,6 +296,7 @@ public function __construct($db) 'target' => '', 'user' => 2, // 0=Menu for internal users, 1=external users, 2=both ); + */ /*$this->menu[$r++]=array( 'fk_menu'=>'fk_mainmenu=webportal', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode 'type'=>'left', // This is a Left menu entry diff --git a/htdocs/don/card.php b/htdocs/don/card.php index 3b39c59d52c78..4d352e6b65fa3 100644 --- a/htdocs/don/card.php +++ b/htdocs/don/card.php @@ -974,6 +974,14 @@ // Show online payment link $useonlinepayment = (isModEnabled('paypal') || isModEnabled('stripe') || isModEnabled('paybox')); + $parameters = array(); + $reshook = $hookmanager->executeHooks('doShowOnlinePaymentUrl', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + if (isset($hookmanager->resArray['showonlinepaymenturl'])) { + $useonlinepayment += $hookmanager->resArray['showonlinepaymenturl']; + } + } + if ($useonlinepayment) { //$object->statut != Facture::STATUS_DRAFT && print '
'."\n"; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; diff --git a/htdocs/don/paiement/list.php b/htdocs/don/paiement/list.php new file mode 100644 index 0000000000000..4f9b91530a20d --- /dev/null +++ b/htdocs/don/paiement/list.php @@ -0,0 +1,656 @@ + + * Copyright (C) 2004-2018 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2013 Cédric Salvador + * Copyright (C) 2019 Thibault FOUCART + * + * 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/don/list.php + * \ingroup donations + * \brief List of donations + */ + +// Load Dolibarr environment +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; +require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php'; + +// Load translation files required by the page +$langs->loadLangs(array('companies', 'donations')); + +$action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'create'/'add', 'edit'/'update', 'view', ... +$massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) +$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'sclist'; + +$paiementid = GETPOST('paiementid', 'int'); + +$search_ref = GETPOST("search_ref", "alpha"); +$search_date_startday = GETPOST('search_date_startday', 'int'); +$search_date_startmonth = GETPOST('search_date_startmonth', 'int'); +$search_date_startyear = GETPOST('search_date_startyear', 'int'); +$search_date_endday = GETPOST('search_date_endday', 'int'); +$search_date_endmonth = GETPOST('search_date_endmonth', 'int'); +$search_date_endyear = GETPOST('search_date_endyear', 'int'); +$search_date_start = dol_mktime(0, 0, 0, $search_date_startmonth, $search_date_startday, $search_date_startyear); +$search_date_end = dol_mktime(23, 59, 59, $search_date_endmonth, $search_date_endday, $search_date_endyear); +$search_company = GETPOST("search_company", 'alpha'); +$search_paymenttype = GETPOST("search_paymenttype"); +$search_account = GETPOST("search_account", "int"); +$search_payment_num = GETPOST('search_payment_num', 'alpha'); +$search_amount = GETPOST("search_amount", 'alpha'); +$search_status = GETPOST('search_status', 'intcomma'); +$search_sale = GETPOST('search_sale', 'int'); + +$limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); +$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); +$type = GETPOST('type', 'aZ'); +$mode = GETPOST('mode', 'alpha'); +if (empty($page) || $page == -1) { + $page = 0; +} // If $page is not defined, or '' or -1 +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (!$sortorder) { + $sortorder = "DESC"; +} +if (!$sortfield) { + $sortfield = "pd.rowid"; +} + +$search_all = trim(GETPOSTISSET("search_all") ? GETPOST("search_all", 'alpha') : GETPOST('sall')); + +// List of fields to search into when doing a "search in all" +$fieldstosearchall = array( + 'pd.rowid'=>"RefPayment", + 's.nom'=>"ThirdParty", + 'pd.num_paiement'=>"Numero", + 'pd.amount'=>"Amount", +); + +$arrayfields = array( + 'pd.rowid' => array('label'=>"RefPayment", 'checked'=>1, 'position'=>10), + 'pd.datep' => array('label'=>"Date", 'checked'=>1, 'position'=>20), + 's.nom' => array('label'=>"ThirdParty", 'checked'=>1, 'position'=>30), + 'c.code' => array('label'=>"Type", 'checked'=>1, 'position'=>40), + 'pd.num_paiement' => array('label'=>"Numero", 'checked'=>1, 'position'=>50, 'tooltip'=>"ChequeOrTransferNumber"), + 'transaction' => array('label'=>"BankTransactionLine", 'checked'=>1, 'position'=>60, 'enabled'=>(isModEnabled("banque"))), + 'ba.label' => array('label'=>"Account", 'checked'=>1, 'position'=>70, 'enabled'=>(isModEnabled("banque"))), + 'pd.amount' => array('label'=>"Amount", 'checked'=>1, 'position'=>80), +); +$arrayfields = dol_sort_array($arrayfields, 'position'); + +$optioncss = GETPOST('optioncss', 'alpha'); +$moreforfilter = GETPOST('moreforfilter', 'alpha'); + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('donationlist')); + +// Security check +$result = restrictedArea($user, 'don'); + +$permissiontoread = $user->hasRight('don', 'read'); +$permissiontoadd = $user->hasRight('don', 'write'); +$permissiontodelete = $user->hasRight('don', 'delete'); + +/* + * Actions + */ + +$parameters = array('socid'=>$paiementid); +$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)) { + include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + + // All tests are required to be compatible with all browsers + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { + $search_ref = ''; + $search_date_startday = ''; + $search_date_startmonth = ''; + $search_date_startyear = ''; + $search_date_endday = ''; + $search_date_endmonth = ''; + $search_date_endyear = ''; + $search_date_start = ''; + $search_date_end = ''; + $search_account = ''; + $search_amount = ''; + $search_paymenttype = ''; + $search_payment_num = ''; + $search_company = ''; + $option = ''; + $toselect = array(); + $search_array_options = array(); + } +} + +/* + * View + */ + +$form = new Form($db); +$donationstatic = new Don($db); +$companystatic = new Societe($db); +$bankline = new AccountLine($db); +$accountstatic = new Account($db); + +$title = $langs->trans("Donations"); +$help_url = 'EN:Module_Donations|FR:Module_Dons|ES:Módulo_Donaciones|DE:Modul_Spenden'; + +// Build and execute select +// -------------------------------------------------------------------- +$sql = "SELECT pd.rowid as payment_id, pd.amount, pd.datep, pd.fk_typepayment, pd.num_payment, pd.amount, pd.fk_bank, "; +$sql .= ' s.rowid as soc_id, s.nom, '; +$sql .= ' d.societe, '; +$sql .= ' c.code as paiement_code, '; +$sql .= ' d.rowid, ba.rowid as bid, ba.ref as bref, ba.label as blabel, ba.number, ba.account_number as account_number, ba.iban_prefix, ba.bic, ba.currency_code, ba.fk_accountancy_journal as accountancy_journal '; + +// 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; + +$sqlfields = $sql; // $sql fields to remove for count total + +$sql .= " FROM ".MAIN_DB_PREFIX."payment_donation as pd"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."don as d ON (d.rowid = pd.fk_donation)"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON (b.rowid = pd.fk_bank)"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON (ba.rowid = b.fk_account)"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (s.rowid = d.fk_soc)"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON (c.id = pd.fk_typepayment)"; +$sql .= " WHERE d.entity IN (".getEntity('donation').")"; + +if ($search_ref) { + $sql .= natural_search('pd.rowid', $search_ref); +} +if ($search_date_start) { + $sql .= " AND pd.datep >= '" . $db->idate($search_date_start) . "'"; +} +if ($search_date_end) { + $sql .= " AND pd.datep <= '" . $db->idate($search_date_end) . "'"; +} +if ($search_account > 0) { + $sql .= " AND b.fk_account=".((int) $search_account); +} +if ($search_paymenttype != '') { + $sql .= " AND c.code='".$db->escape($search_paymenttype)."'"; +} +if ($search_payment_num != '') { + $sql .= natural_search('pd.num_payment', $search_payment_num); +} +if ($search_amount) { + $sql .= natural_search('pd.amount', $search_amount, 1); +} +if ($search_company) { + $sql .= natural_search('s.nom', $search_company); +} +if ($search_all) { + $sql .= natural_search(array_keys($fieldstosearchall), $search_all); +} + +// 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; + +// Count total nb of records +$nbtotalofrecords = ''; +if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) { + /* The fast and low memory method to get and count full list converts the sql into a sql count */ + $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql); + $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount); + $resql = $db->query($sqlforcount); + if ($resql) { + $objforcount = $db->fetch_object($resql); + $nbtotalofrecords = $objforcount->nbtotalofrecords; + } else { + dol_print_error($db); + } + + if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 0 + $page = 0; + $offset = 0; + } + $db->free($resql); +} + +$sql .= $db->order($sortfield, $sortorder); + +// Complete request and execute it with limit +if ($limit) { + $sql .= $db->plimit($limit + 1, $offset); +} + +$resql = $db->query($sql); + +if (!$resql) { + dol_print_error($db); + exit; +} + +$num = $db->num_rows($resql); + +// Output page +// -------------------------------------------------------------------- + +llxHeader('', $title, $help_url, '', 0, 0, $morejs, $morecss, '', 'bodyforlist'); // Can use also classforhorizontalscrolloftabs instead of bodyforlist for no horizontal scroll + +$param = ''; +if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) { + $param .= '&contextpage='.urlencode($contextpage); +} +if ($limit > 0 && $limit != $conf->liste_limit) { + $param .= '&limit='.((int) $limit); +} +if ($search_ref) { + $param .= '&search_ref='.urlencode($search_ref); +} +if ($search_date_startday) { + $param .= '&search_date_startday='.urlencode($search_date_startday); +} +if ($search_date_startmonth) { + $param .= '&search_date_startmonth='.urlencode($search_date_startmonth); +} +if ($search_date_startyear) { + $param .= '&search_date_startyear='.urlencode($search_date_startyear); +} +if ($search_date_endday) { + $param .= '&search_date_endday='.urlencode($search_date_endday); +} +if ($search_date_endmonth) { + $param .= '&search_date_endmonth='.urlencode($search_date_endmonth); +} +if ($search_date_endyear) { + $param .= '&search_date_endyear='.urlencode($search_date_endyear); +} +if ($search_company) { + $param .= '&search_company='.urlencode($search_company); +} +if ($search_amount != '') { + $param .= '&search_amount='.urlencode($search_amount); +} +if ($search_paymenttype) { + $param .= '&search_paymenttype='.urlencode($search_paymenttype); +} +if ($search_account) { + $param .= '&search_account='.urlencode($search_account); +} +if ($search_payment_num) { + $param .= '&search_payment_num='.urlencode($search_payment_num); +} +if ($optioncss != '') { + $param .= '&optioncss='.urlencode($optioncss); +} + +print '
'; +if ($optioncss != '') { + print ''; +} +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +print_barre_liste($langs->trans("DonationsReglement"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'bill', 0, '', '', $limit, 0, 0, 1); + +if ($search_all) { + foreach ($fieldstosearchall as $key => $val) { + $fieldstosearchall[$key] = $langs->trans($val); + } + print '
'.$langs->trans("FilterOnInto", $search_all).join(', ', $fieldstosearchall).'
'; +} + +$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; +$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN', '')); // This also change content of $arrayfields +$massactionbutton = ''; +if ($massactionbutton) { + $selectedfields .= $form->showCheckAddButtons('checkforselect', 1); +} + +$moreforfilter = ''; +print '
'; +print ''; + +// Fields title search +// -------------------------------------------------------------------- +print ''; +// Action column +if (getDolGlobalInt('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; +} + +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER_IN_LIST')) { + print ''; +} + +if (!empty($arrayfields['pd.rowid']['checked'])) { + print ''; +} + +// Filter: Date +if (!empty($arrayfields['pd.datep']['checked'])) { + print ''; +} + +// Filter: Thirdparty +if (!empty($arrayfields['s.nom']['checked'])) { + print ''; +} + +// Filter: Payment type +if (!empty($arrayfields['c.code']['checked'])) { + print ''; +} + +// Filter: Bank transaction number +if (!empty($arrayfields['transaction']['checked'])) { + print ''; +} + +// Filter: Cheque number (fund transfer) +if (!empty($arrayfields['pd.num_paiement']['checked'])) { + print ''; +} + +// Filter: Bank account +if (!empty($arrayfields['ba.label']['checked'])) { + print ''; +} + +// Filter: Amount +if (!empty($arrayfields['pd.amount']['checked'])) { + print ''; +} + +// Fields from hook +$parameters = array('arrayfields'=>$arrayfields); +$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +// Action column +if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; +} + +print ''."\n"; + +$totalarray = array(); +$totalarray['nbfield'] = 0; + +// Fields title label +// -------------------------------------------------------------------- +print ''; +// Action column +if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', 'align="center"', $sortfield, $sortorder, 'maxwidthsearch '); + $totalarray['nbfield']++; +} +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER_IN_LIST')) { + print_liste_field_titre('#', $_SERVER['PHP_SELF'], '', '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['pd.rowid']['checked'])) { + print_liste_field_titre($arrayfields['pd.rowid']['label'], $_SERVER["PHP_SELF"], "pd.rowid", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['pd.datep']['checked'])) { + print_liste_field_titre($arrayfields['pd.datep']['label'], $_SERVER["PHP_SELF"], "pd.datep", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['s.nom']['checked'])) { + print_liste_field_titre($arrayfields['s.nom']['label'], $_SERVER["PHP_SELF"], "s.nom", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['c.code']['checked'])) { + print_liste_field_titre($arrayfields['c.code']['label'], $_SERVER["PHP_SELF"], "c.code", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['pd.num_paiement']['checked'])) { + print_liste_field_titre($arrayfields['pd.num_paiement']['label'], $_SERVER["PHP_SELF"], "pd.num_payment", '', $param, '', $sortfield, $sortorder, '', $arrayfields['p.num_paiement']['tooltip']); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['transaction']['checked'])) { + print_liste_field_titre($arrayfields['transaction']['label'], $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['ba.label']['checked'])) { + print_liste_field_titre($arrayfields['ba.label']['label'], $_SERVER["PHP_SELF"], "ba.label", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} +if (!empty($arrayfields['pd.amount']['checked'])) { + print_liste_field_titre($arrayfields['pd.amount']['label'], $_SERVER["PHP_SELF"], "pd.amount", '', $param, '', $sortfield, $sortorder); + $totalarray['nbfield']++; +} + +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; +// Hook fields +$parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder); +$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', 'align="center"', $sortfield, $sortorder, 'maxwidthsearch '); + $totalarray['nbfield']++; +} +print ''."\n"; + +// Detect if we need a fetch on each output line +$needToFetchEachLine = 0; +if (isset($extrafields->attributes[$object->table_element]['computed']) && is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0) { + foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val) { + if (!is_null($val) && preg_match('/\$object/', $val)) { + $needToFetchEachLine++; // There is at least one compute field that use $object + } + } +} + +// Loop on record +// -------------------------------------------------------------------- +$i = 0; +$savnbfield = $totalarray['nbfield']; +$totalarray = array(); +$totalarray['nbfield'] = 0; +$imaxinloop = ($limit ? min($num, $limit) : $num); +while ($i < $imaxinloop) { + $obj = $db->fetch_object($resql); + + $companystatic->id = $obj->soc_id; + $companystatic->name = $obj->nom; + + $company = new Societe($db); + $result = $company->fetch($obj->socid); + + print ''; + if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Ref + if (!empty($arrayfields['pd.rowid']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Date + if (!empty($arrayfields['pd.datep']['checked'])) { + $dateformatforpayment = 'dayhour'; + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Thirdparty + if (!empty($arrayfields['s.nom']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Payment type + if (!empty($arrayfields['c.code']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Filter: Cheque number (fund transfer) + if (!empty($arrayfields['pd.num_paiement']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Bank transaction + if (!empty($arrayfields['transaction']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Bank account + if (!empty($arrayfields['ba.label']['checked'])) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + $totalarray['pos'][$totalarray['nbfield']] = 'amount'; + } + if (empty($totalarray['val']['amount'])) { + $totalarray['val']['amount'] = $obj->amount; + } else { + $totalarray['val']['amount'] += $obj->amount; + } + } + + $i++; +} + +// Show total line +include DOL_DOCUMENT_ROOT.'/core/tpl/list_print_total.tpl.php'; + +// If no record found +if ($num == 0) { + $colspan = 1; + foreach ($arrayfields as $key => $val) { + if (!empty($val['checked'])) { + $colspan++; + } + } + print ''; +} + +$db->free($resql); + +$parameters = array('arrayfields' => $arrayfields, 'sql' => $sql); +$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print '
'; + $searchpicto = $form->showFilterButtons('left'); + print $searchpicto; + print ''; + print ''; + print ''; + print ''; + print '
'; + print $form->selectDate($search_date_start ? $search_date_start : -1, 'search_date_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From')); + print '
'; + print '
'; + print $form->selectDate($search_date_end ? $search_date_end : -1, 'search_date_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to')); + print '
'; + print '
'; + print ''; + print ''; + print $form->select_types_paiements($search_paymenttype, 'search_paymenttype', '', 2, 1, 1, 0, 1, 'maxwidth100', 1); + print ''; + print ''; + print ''; + print ''; + $form->select_comptes($search_account, 'search_account', 0, '', 1); + print ''; + print ''; + print ''; + $searchpicto = $form->showFilterButtons(); + print $searchpicto; + print '
'; + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($obj->id_paiement, $arrayofselected)) { + $selected = 1; + } + print ''; + } + print '' . img_object($langs->trans("Payment"), "payment") . ' ' . $obj->payment_id . ''.dol_print_date($db->jdate($obj->datep), $dateformatforpayment, 'tzuser').''; + if ($obj->soc_id > 0) { + print $companystatic->getNomUrl(1, '', 24); + } else { + print $donationstatic->societe = $obj->societe; + } + print ''.$langs->trans("PaymentTypeShort".$obj->paiement_code).''.$obj->num_payment.''; + if ($obj->fk_bank > 0) { + $test = $bankline->fetch($obj->fk_bank); + print $bankline->getNomUrl(1, 0); + } + print ''; + if ($obj->bid > 0) { + $accountstatic->id = $obj->bid; + $accountstatic->ref = $obj->bref; + $accountstatic->label = $obj->blabel; + $accountstatic->number = $obj->number; + $accountstatic->account_number = $obj->account_number; + + $accountingjournal = new AccountingJournal($db); + $accountingjournal->fetch($obj->accountancy_journal); + $accountstatic->accountancy_journal = $accountingjournal->code; + + print $accountstatic->getNomUrl(1); + } + if (!$i) { + $totalarray['nbfield']++; + } + } + + // Amount + if (!empty($arrayfields['pd.amount']['checked'])) { + print '' . price($obj->amount) . '
'.$langs->trans("NoRecordFound").'
'."\n"; +print '
'."\n"; + +print '
'."\n"; + +llxFooter(); +$db->close(); diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index d72a1454db746..4f4a06dc51252 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -1104,11 +1104,11 @@ public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_la /** - * Returns the following order reference not used depending on the numbering model activated - * defined within COMMANDE_SUPPLIER_ADDON_NUMBER + * Returns the next order reference not used, based on the + * numbering model defined within COMMANDE_SUPPLIER_ADDON_NUMBER * * @param Societe $soc company object - * @return string free reference for the invoice + * @return string|int free reference for the invoice. '', -1 or -2 if error. */ public function getNextNumRef($soc) { @@ -1864,13 +1864,13 @@ public function createFromClone(User $user, $socid = 0, $notrigger = 0) // Clear fields $this->user_author_id = $user->id; $this->user_validation_id = 0; - $this->date_creation = ''; - $this->date_validation = ''; - $this->ref_supplier = null; - $this->user_approve_id = ''; - $this->user_approve_id2 = ''; - $this->date_approve = ''; - $this->date_approve2 = ''; + $this->date_creation = 0; + $this->date_validation = 0; + $this->ref_supplier = ''; + $this->user_approve_id = 0; + $this->user_approve_id2 = 0; + $this->date_approve = 0; + $this->date_approve2 = 0; // Create clone $this->context['createfromclone'] = 'createfromclone'; @@ -2923,7 +2923,7 @@ public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $t $remise_percent = 0; } - $remise_percent = price2num($remise_percent); + $remise_percent = (float) price2num($remise_percent); $qty = price2num($qty); if (!$qty) { $qty = 1; diff --git a/htdocs/fourn/facture/list.php b/htdocs/fourn/facture/list.php index fef9a1288f330..966f9f0cc01e1 100644 --- a/htdocs/fourn/facture/list.php +++ b/htdocs/fourn/facture/list.php @@ -12,7 +12,7 @@ * Copyright (C) 2017 Josep Lluís Amador * Copyright (C) 2018-2022 Charlene Benke * Copyright (C) 2018-2020 Frédéric France - * Copyright (C) 2019-2021 Alexandre Spangaro + * Copyright (C) 2019-2023 Alexandre Spangaro * Copyright (C) 2023 Nick Fragoulis * * This program is free software; you can redistribute it and/or modify @@ -88,7 +88,7 @@ $search_town = GETPOST('search_town', 'alpha'); $search_zip = GETPOST('search_zip', 'alpha'); $search_state = GETPOST("search_state"); -$search_country = GETPOST("search_country", 'int'); +$search_country = GETPOST("search_country", 'alpha'); $search_type_thirdparty = GETPOST("search_type_thirdparty", 'int'); $search_user = GETPOST('search_user', 'int'); $search_sale = GETPOST('search_sale', 'int'); @@ -530,8 +530,26 @@ if ($search_state) { $sql .= natural_search("state.nom", $search_state); } -if ($search_country) { - $sql .= " AND s.fk_pays IN (".$db->sanitize($search_country).')'; +if (strlen(trim($search_country))) { + $arrayofcode = getCountriesInEEC(); + $country_code_in_EEC = $country_code_in_EEC_without_me = ''; + foreach ($arrayofcode as $key => $value) { + $country_code_in_EEC .= ($country_code_in_EEC ? "," : "")."'".$value."'"; + if ($value != $mysoc->country_code) { + $country_code_in_EEC_without_me .= ($country_code_in_EEC_without_me ? "," : "")."'".$value."'"; + } + } + if ($search_country == 'special_allnotme') { + $sql .= " AND country.code <> '".$db->escape($mysoc->country_code)."'"; + } elseif ($search_country == 'special_eec') { + $sql .= " AND country.code IN (".$db->sanitize($country_code_in_EEC, 1).")"; + } elseif ($search_country == 'special_eecnotme') { + $sql .= " AND country.code IN (".$db->sanitize($country_code_in_EEC_without_me, 1).")"; + } elseif ($search_country == 'special_noteec') { + $sql .= " AND country.code NOT IN (".$db->sanitize($country_code_in_EEC, 1).")"; + } else { + $sql .= natural_search("country.code", $search_country); + } } if ($search_type_thirdparty != '' && $search_type_thirdparty >= 0) { $sql .= " AND s.fk_typent IN (".$db->sanitize($search_type_thirdparty).')'; @@ -1138,7 +1156,7 @@ // Country if (!empty($arrayfields['country.code_iso']['checked'])) { print ''; - print $form->select_country($search_country, 'search_country', '', 0, 'minwidth100imp maxwidth100'); + print $form->select_country($search_country, 'search_country', '', 0, 'minwidth150imp maxwidth150', 'code2', 1, 0, 1, null, 1); print ''; } // Company type diff --git a/htdocs/hrm/job_card.php b/htdocs/hrm/job_card.php index e0cfb3636bf9e..3ca13ca1b7738 100644 --- a/htdocs/hrm/job_card.php +++ b/htdocs/hrm/job_card.php @@ -166,7 +166,8 @@ $object->fetch($id); $skillRequire = $object->getSkillRankForJob($originalId); if ($object->id > 0) { - $object->id = $object->ref = null; + $object->id = 0; + $object->ref = ''; if (GETPOST('clone_label', 'alphanohtml')) { $object->label = GETPOST('clone_label', 'alphanohtml'); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index fac5f950a3096..1a8c9492605be 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -2437,3 +2437,5 @@ ParametersForTestEnvironment=Parameters for test environment TryToKeepOnly=Try to keep only %s RecommendedForProduction=Recommended for Production RecommendedForDebug=Recommended for Debug +UrlPublicInterfaceLabelAdmin=Alternative URL for public interface +UrlPublicInterfaceHelpAdmin=It is possible to define an alias to the web server and thus make available the public interface with another URL (the virtual host server must act as a proxy on the standard URL) diff --git a/htdocs/langs/en_US/ticket.lang b/htdocs/langs/en_US/ticket.lang index 460e73e1cf107..e1493e9c14b4d 100644 --- a/htdocs/langs/en_US/ticket.lang +++ b/htdocs/langs/en_US/ticket.lang @@ -107,8 +107,6 @@ TicketsShowProgressionHelp=Enable this option to hide the progress of the ticket TicketCreateThirdPartyWithContactIfNotExist=Ask name and company name for unknown emails. TicketCreateThirdPartyWithContactIfNotExistHelp=Check if a third party or a contact exists for the email entered. If not, ask a name and a company name to create a third party with contact. PublicInterface=Public interface -TicketUrlPublicInterfaceLabelAdmin=Alternative URL for public interface -TicketUrlPublicInterfaceHelpAdmin=It is possible to define an alias to the web server and thus make available the public interface with another URL (the server must act as a proxy on this new URL) TicketPublicInterfaceTextHomeLabelAdmin=Welcome text of the public interface TicketPublicInterfaceTextHome=You can create a support ticket or view existing from its identifier tracking ticket. TicketPublicInterfaceTextHomeHelpAdmin=The text defined here will appear on the home page of the public interface. diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index 1b076f689b474..d97f4ff23ac6f 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -174,8 +174,6 @@ WebPortalSetup = WebPortal setup WebPortalCSS=Web portal CSS Settings = Settings WebPortalSetupPage = WebPortal setup page -WEBPORTAL_ROOT_URL = Alternative virtual host URL -WebPortalRootUrlHelp = Public access url (http or https) if you have a virtual host or keep empty WEBPORTAL_TITLE = Brand name on header of public page UserAccountForWebPortalAreInThirdPartyTabHelp = Users accounts for WebPortal can be set on each third party card in Website accounts tab WebPortalAccessHidden = Hidden @@ -234,3 +232,4 @@ WebPortalErrorFetchLoggedUser = Error when loading user (Id : %s) WebPortalErrorFetchLoggedThirdParty = Error when loading third-party (Id : %s) WebPortalErrorFetchLoggedMember = Error when loading member (Id : %s) WebPortalErrorFetchLoggedPartnership = Error when loading partnership (Third-party Id : %s, Member Id : %s) +ExportIntoGIT=Export into sources \ No newline at end of file diff --git a/htdocs/langs/fr_FR/admin.lang b/htdocs/langs/fr_FR/admin.lang index b9876492cddb2..9864434ff1428 100644 --- a/htdocs/langs/fr_FR/admin.lang +++ b/htdocs/langs/fr_FR/admin.lang @@ -2440,3 +2440,4 @@ ParametersForTestEnvironment=Paramètres pour l'environnement de test TryToKeepOnly=Essayez de ne conserver que %s RecommendedForProduction=Recommandé pour la production RecommendedForDebug=Recommandé pour le débogage +UrlPublicInterfaceHelpAdmin=Il est possible de définir un alias vers le serveur et de rendre ainsi l'interface publique accessible avec une autre URL (le serveur doit agir comme un proxy sur cette nouvelle URL) diff --git a/htdocs/langs/fr_FR/donations.lang b/htdocs/langs/fr_FR/donations.lang index bc39b4b7992d9..4cf3f4ca49335 100644 --- a/htdocs/langs/fr_FR/donations.lang +++ b/htdocs/langs/fr_FR/donations.lang @@ -32,5 +32,9 @@ DONATION_ART238=Afficher article 238 du CGI si vous êtes concernés DONATION_ART978=Afficher article 978 du CGI si vous êtes concernés DonationPayment=Paiement du don DonationValidated=Don %s validé + +DonationsReglement= Règlements reçus pour les dons + DonationUseThirdparties=Utiliser un tiers existant comme coordonnées du donateur DonationsStatistics=Statistiques des dons + diff --git a/htdocs/langs/fr_FR/ticket.lang b/htdocs/langs/fr_FR/ticket.lang index e8c90035e072c..d910042f8cafe 100644 --- a/htdocs/langs/fr_FR/ticket.lang +++ b/htdocs/langs/fr_FR/ticket.lang @@ -108,7 +108,6 @@ TicketCreateThirdPartyWithContactIfNotExist=Demandez le nom et le nom de l'entre TicketCreateThirdPartyWithContactIfNotExistHelp=Vérifiez s'il existe un tiers ou un contact pour l'e-mail saisi. Sinon, demandez un nom et un nom société pour créer un tiers avec contact. PublicInterface=Interface publique TicketUrlPublicInterfaceLabelAdmin=URL alternative pour l'interface publique -TicketUrlPublicInterfaceHelpAdmin=Il est possible de définir un alias vers le serveur et de rendre ainsi l'interface publique accessible avec une autre URL (le serveur doit agir comme un proxy sur cette nouvelle URL) TicketPublicInterfaceTextHomeLabelAdmin=Texte de bienvenue dans l'interface publique TicketPublicInterfaceTextHome=Vous pouvez créer un ticket ou consulter à partir d'un ID de ticket existant. TicketPublicInterfaceTextHomeHelpAdmin=Le texte défini ici apparaîtra sur la page d'accueil de l'interface publique. diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 3e0d45f75f40d..1858ed5f98598 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -2358,7 +2358,7 @@ public function updatePrice($newprice, $newpricebase, $user, $newvat = '', $newm $price_min_ttc = 0; } } else { - $price = price2num($newprice, 'MU'); + $price = (float) price2num($newprice, 'MU'); $price_ttc = ($newnpr != 1) ? (float) price2num($newprice) * (1 + ($newvat / 100)) : $price; $price_ttc = price2num($price_ttc, 'MU'); @@ -2905,8 +2905,8 @@ public function fetch($id = 0, $ref = '', $ref_ext = '', $barcode = '', $ignore_ if ($price_result >= 0) { $this->price = $price_result; // Calculate the VAT - $this->price_ttc = price2num($this->price) * (1 + ($this->tva_tx / 100)); - $this->price_ttc = price2num($this->price_ttc, 'MU'); + $this->price_ttc = (float) price2num($this->price) * (1 + ($this->tva_tx / 100)); + $this->price_ttc = (float) price2num($this->price_ttc, 'MU'); } } @@ -5062,7 +5062,7 @@ public function hasVariants() /** * Return if loaded product is a variant * - * @return int + * @return bool|int Return true if the product is a variant, false if not, -1 if error */ public function isVariant() { @@ -5587,7 +5587,7 @@ public function LibStatut($status, $mode = 0, $type = 0) /** * Retour label of nature of product * - * @return string Label + * @return string|int Return label or ''. -1 if error */ public function getLibFinished() { diff --git a/htdocs/product/price.php b/htdocs/product/price.php index 31fa5a5896381..a7557f75bb9ec 100644 --- a/htdocs/product/price.php +++ b/htdocs/product/price.php @@ -2192,7 +2192,7 @@ function on_change() { print ''; - print "".$staticsoc->getNomUrl(1).""; + print ''.$staticsoc->getNomUrl(1).""; print ''.$line->ref_customer.''; print "".dol_print_date($line->datec, "dayhour", 'tzuserrel').""; print ''.$langs->trans($line->price_base_type).""; @@ -2308,10 +2308,10 @@ function on_change() { $pu = $object->price_ttc; } - // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code. + // Local tax was not saved into table llx_product on old versions. So we will use the value linked to the VAT code. $localtaxarray = getLocalTaxesFromRate($object->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc); // Define part of HT, VAT, TTC - $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, $object->recuperableonly, $object->type, $mysoc, $localtaxarray); + $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, 0, $object->type, $mysoc, $localtaxarray); // Calcul du total ht sans remise $total_ht = $resultarray[0]; $total_vat = $resultarray[1]; @@ -2399,7 +2399,7 @@ function on_change() { print ''; - print "".$staticsoc->getNomUrl(1).""; + print ''.$staticsoc->getNomUrl(1).""; print ''.dol_escape_htmltag($line->ref_customer).''; print "".dol_print_date($line->datec, "dayhour", 'tzuserrel').""; print ''.$langs->trans($line->price_base_type).""; diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index 1c2b7af3f9ba5..f0480994e210e 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -158,36 +158,6 @@ $massaction = ''; } -// Definition of fields for list -$arrayfields = array(); -$arrayfields['t.element_date'] = array('label'=>$langs->trans("Date"), 'checked'=>1); -$arrayfields['p.fk_soc'] = array('label'=>$langs->trans("ThirdParty"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1','checked'=>1); -$arrayfields['s.name_alias'] = array('label'=>$langs->trans("AliasNameShort"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1'); -if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task - if (! empty($allprojectforuser)) { - $arrayfields['p.project_ref'] = ['label' => $langs->trans('RefProject'), 'checked' => 1]; - $arrayfields['p.project_label'] = ['label' => $langs->trans('ProjectLabel'), 'checked' => 1]; - } - $arrayfields['t.element_ref'] = array('label'=>$langs->trans("RefTask"), 'checked'=>1); - $arrayfields['t.element_label'] = array('label'=>$langs->trans("LabelTask"), 'checked'=>1); -} -$arrayfields['author'] = array('label' => $langs->trans("By"), 'checked' => 1); -$arrayfields['t.note'] = array('label' => $langs->trans("Note"), 'checked' => 1); -if (!getDolGlobalInt('PROJECT_HIDE_TASKS') && getDolGlobalInt('PROJECT_BILL_TIME_SPENT') && !$projectstatic->usage_bill_time ) { - $projectstatic->usage_bill_time=1; -} -if (isModEnabled('service') && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { - $arrayfields['t.fk_product'] = array('label' => $langs->trans("Product"), 'checked' => 1); -} -$arrayfields['t.element_duration'] = array('label'=>$langs->trans("Duration"), 'checked'=>1); -$arrayfields['value'] = array('label'=>$langs->trans("Value"), 'checked'=>1, 'enabled'=>isModEnabled("salaries")); -$arrayfields['valuebilled'] = array('label'=>$langs->trans("Billed"), 'checked'=>1, 'enabled'=>(((getDolGlobalInt('PROJECT_HIDE_TASKS') || !getDolGlobalInt('PROJECT_BILL_TIME_SPENT')) ? 0 : 1) && $projectstatic->usage_bill_time)); -// Extra fields -include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_array_fields.tpl.php'; - -$arrayfields = dol_sort_array($arrayfields, 'position'); - - $parameters = array('socid' => $socid, 'projectid' => $projectid); $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) { @@ -1296,7 +1266,31 @@ // Print form confirm print $formconfirm; + // Definition of fields for list + $arrayfields = array(); + $arrayfields['t.element_date'] = array('label'=>$langs->trans("Date"), 'checked'=>1); + $arrayfields['p.fk_soc'] = array('label'=>$langs->trans("ThirdParty"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1','checked'=>1); + $arrayfields['s.name_alias'] = array('label'=>$langs->trans("AliasNameShort"), 'type'=>'integer:Societe:/societe/class/societe.class.php:1'); + if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task + if (! empty($allprojectforuser)) { + $arrayfields['p.project_ref'] = ['label' => $langs->trans('RefProject'), 'checked' => 1]; + $arrayfields['p.project_label'] = ['label' => $langs->trans('ProjectLabel'), 'checked' => 1]; + } + $arrayfields['t.element_ref'] = array('label'=>$langs->trans("RefTask"), 'checked'=>1); + $arrayfields['t.element_label'] = array('label'=>$langs->trans("LabelTask"), 'checked'=>1); + } + $arrayfields['author'] = array('label' => $langs->trans("By"), 'checked' => 1); + $arrayfields['t.note'] = array('label' => $langs->trans("Note"), 'checked' => 1); + if (isModEnabled('service') && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { + $arrayfields['t.fk_product'] = array('label' => $langs->trans("Product"), 'checked' => 1); + } + $arrayfields['t.element_duration'] = array('label'=>$langs->trans("Duration"), 'checked'=>1); + $arrayfields['value'] = array('label'=>$langs->trans("Value"), 'checked'=>1, 'enabled'=>isModEnabled("salaries")); + $arrayfields['valuebilled'] = array('label'=>$langs->trans("Billed"), 'checked'=>1, 'enabled'=>(((getDolGlobalInt('PROJECT_HIDE_TASKS') || !getDolGlobalInt('PROJECT_BILL_TIME_SPENT')) ? 0 : 1) && $projectstatic->usage_bill_time)); + // Extra fields + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_array_fields.tpl.php'; + $arrayfields = dol_sort_array($arrayfields, 'position'); $param = ''; if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) { @@ -1840,8 +1834,8 @@ function setDetailVisibility() { if (isModEnabled("service") && !empty($projectstatic->thirdparty) && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { print ''; - print img_picto('', 'product'); - print $form->select_produits('', 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth150', 0, '', null, 1); + print img_picto('', 'service'); + print $form->select_produits((GETPOSTISSET('fk_product')?GETPOST("fk_product", 'int'):''), 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth150', 0, '', null, 1); print ''; } } @@ -2345,9 +2339,10 @@ function setDetailVisibility() { // Product if (!empty($arrayfields['t.fk_product']['checked'])) { - print ''; + print ''; if ($action == 'editline' && $_GET['lineid'] == $task_time->rowid) { - $form->select_produits($task_time->fk_product, 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); + print img_picto('', 'service'); + print $form->select_produits($task_time->fk_product, 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500', 0, '', null, 1); } elseif (!empty($task_time->fk_product)) { $product = new Product($db); $resultFetch = $product->fetch($task_time->fk_product); diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index cf7d9aa55b931..15e88771d2488 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -469,6 +469,30 @@ class Societe extends CommonObject */ public $idprof6; + /** + * Professional ID 7 + * @var string + */ + public $idprof7; + + /** + * Professional ID 8 + * @var string + */ + public $idprof8; + + /** + * Professional ID 9 + * @var string + */ + public $idprof9; + + /** + * Professional ID 10 + * @var string + */ + public $idprof10; + /** * Social object of the company * @var string diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 27d7888b9c872..9d8bb366be75d 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -1679,7 +1679,7 @@ $companystatic->code_compta_fournisseur = $obj->code_compta_fournisseur; $companystatic->fk_prospectlevel = $obj->fk_prospectlevel; - $companystatic->fk_parent = $obj->fk_parent; + $companystatic->parent = $obj->fk_parent; $companystatic->entity = $obj->entity; $object = $companystatic; diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 20a2ea638ae8b..b4497bda8dd6b 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -2659,7 +2659,7 @@ public function newMessage($user, &$action, $private = 1, $public_area = 0) if (!empty($sendto)) { $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name); - $subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage'); + $subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$this->subject; // Message send $message = $langs->trans('TicketMessageMailIntroText'); diff --git a/htdocs/webportal/README.md b/htdocs/webportal/README.md index 0dddedea0eb89..ad25491e3ce01 100644 --- a/htdocs/webportal/README.md +++ b/htdocs/webportal/README.md @@ -3,7 +3,7 @@ Module Web Portal This is a module to provide a ready to use Web Portal for your customers, suppliers, partners or members of the mebership module. -Accounts (login and pass) to acces this portal can be created for any thirdparty (from the tab "Web site accounts"). +Accounts (login and pass) to access this portal can be created for any thirdparty (from the tab "Web site accounts"). It is better to have a standalone web server with its own virtual host and domain name to use this module, so using the web portal does not reaveal the domain and url of your backoffice installation. @@ -15,8 +15,8 @@ If the Thirdparty module is enabled: * Read/modify Name, phone, email, addresses of thirdparty If the Patnership module is enabled: -* Read properties (status, stard date, end date) of its partnership. - +* Read properties (status, start date, end date) of its partnership. + If the Proposal module is enabled: * Read its orders @@ -42,4 +42,3 @@ Documentation [Module Web Portal](https://wiki.dolibarr.org/index.php/Module_Web_Portal) - \ No newline at end of file diff --git a/htdocs/webportal/admin/configcss.php b/htdocs/webportal/admin/configcss.php index 56842f4cf7436..8f68e8e81c897 100644 --- a/htdocs/webportal/admin/configcss.php +++ b/htdocs/webportal/admin/configcss.php @@ -29,7 +29,7 @@ require_once DOL_DOCUMENT_ROOT . "/webportal/lib/webportal.lib.php"; // Translations -$langs->loadLangs(array("admin", "hrm", "other")); +$langs->loadLangs(array("admin", "hrm", "other", "website")); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('webportalsetup', 'globalsetup')); diff --git a/htdocs/webportal/admin/setup.php b/htdocs/webportal/admin/setup.php index 334a97336cd4e..78e183d2a954e 100644 --- a/htdocs/webportal/admin/setup.php +++ b/htdocs/webportal/admin/setup.php @@ -57,15 +57,15 @@ if (!class_exists('FormSetup')) { require_once DOL_DOCUMENT_ROOT . '/core/class/html.formsetup.class.php'; } - $formSetup = new FormSetup($db); // root url $item = $formSetup->newItem('WEBPORTAL_ROOT_URL')->setAsString(); +$item->nameText = $langs->transnoentities('UrlPublicInterfaceLabelAdmin'); $item->fieldAttr = array('placeholder' => 'https://'); -$item->helpText = $langs->transnoentities('WebPortalRootUrlHelp'); +$item->helpText = $langs->transnoentities('UrlPublicInterfaceHelpAdmin'); require_once __DIR__ . '/../class/context.class.php'; -$context = Context::getInstance(); +//$context = Context::getInstance(); //$item->fieldOutputOverride = ''.img_picto('', 'globe', 'class="pictofixedwidth"').Context::getRootConfigUrl().''; @@ -270,7 +270,7 @@ // Setup page goes here print info_admin($langs->trans("UserAccountForWebPortalAreInThirdPartyTabHelp")); -print '
'; +print '

'; if ($action == 'edit') { print $formSetup->generateOutput(true); diff --git a/htdocs/webportal/admin/setup_theme.php b/htdocs/webportal/admin/setup_theme.php index 1e751389feade..9900f89d20159 100644 --- a/htdocs/webportal/admin/setup_theme.php +++ b/htdocs/webportal/admin/setup_theme.php @@ -28,7 +28,7 @@ require_once DOL_DOCUMENT_ROOT . "/webportal/lib/webportal.lib.php"; // Translations -$langs->loadLangs(array("admin", "webportal")); +$langs->loadLangs(array("admin", "webportal", "website")); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('webportalthemesetup', 'globalsetup')); diff --git a/htdocs/website/class/website.class.php b/htdocs/website/class/website.class.php index c90626da00359..dd01c2c723fb0 100644 --- a/htdocs/website/class/website.class.php +++ b/htdocs/website/class/website.class.php @@ -140,6 +140,10 @@ class Website extends CommonObject */ public $lines; + /** + * @var string name of template + */ + public $name_template; const STATUS_DRAFT = 0; const STATUS_VALIDATED = 1; @@ -330,7 +334,8 @@ public function fetch($id, $ref = null) $sql .= " t.fk_user_creat,"; $sql .= " t.fk_user_modif,"; $sql .= " t.date_creation,"; - $sql .= " t.tms as date_modification"; + $sql .= " t.tms as date_modification,"; + $sql .= " t.name_template"; $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; $sql .= " WHERE t.entity IN (".getEntity('website').")"; if (!empty($ref)) { @@ -361,6 +366,7 @@ public function fetch($id, $ref = null) $this->fk_user_modif = $obj->fk_user_modif; $this->date_creation = $this->db->jdate($obj->date_creation); $this->date_modification = $this->db->jdate($obj->date_modification); + $this->name_template = $obj->name_template; } $this->db->free($resql); @@ -952,7 +958,6 @@ public function exportWebSite() } $destdir = $conf->website->dir_temp.'/'.$website->ref; - dol_syslog("Clear temp dir ".$destdir); $count = 0; $countreallydeleted = 0; @@ -1149,7 +1154,8 @@ public function exportWebSite() $filename = $conf->website->dir_temp.'/'.$website->ref.'/website_'.$website->ref.'-'.dol_print_date(dol_now(), 'dayhourlog').'-V'.((float) DOL_VERSION).'.zip'; dol_delete_file($fileglob, 0); - $result = dol_compress_file($filedir, $filename, 'zip'); + + $result = dol_compress_dir($filedir, $filename, 'zip'); if ($result > 0) { return $filename; @@ -1174,7 +1180,6 @@ public function importWebSite($pathtofile) $error = 0; $pathtofile = dol_sanitizePathName($pathtofile); - $object = $this; if (empty($object->ref)) { $this->error = 'Function importWebSite called on object not loaded (object->ref is empty)'; @@ -1310,7 +1315,7 @@ public function importWebSite($pathtofile) // Regenerate index page to point to the new index page $pathofwebsite = $conf->website->dir_output.'/'.$object->ref; dolSaveIndexPage($pathofwebsite, $pathofwebsite.'/index.php', $pathofwebsite.'/page'.$object->fk_default_home.'.tpl.php', $pathofwebsite.'/wrapper.php', $object); - + $this->initFilesStatus($pathofwebsite); if ($error) { $this->db->rollback(); return -1; @@ -1602,4 +1607,514 @@ public function componentSelectLang($languagecodes, $weblangs, $morecss = '', $h return $out; } + + /** + * Overite template by copying all files + * + * @return int Return integer <0 if KO, >0 if OK + */ + public function overwriteTemplate() + { + global $conf; + + $website = $this; + if (empty($website->id) || empty($website->ref)) { + setEventMessages("Website id or ref is not defined", null, 'errors'); + return -1; + } + if (empty($website->name_template)) { + setEventMessages("To export the website template into the GIT sources directory, the name of the directory/template must be know. For this website, the variable 'name_template' is unknown, so export in GIT sources is not possible.", null, 'errors'); + return -1; + } + if (!is_writable($conf->website->dir_temp)) { + setEventMessages("Temporary dir ".$conf->website->dir_temp." is not writable", null, 'errors'); + return -1; + } + + $sourcedir = $conf->website->dir_output."/".$website->ref; + + $fichierEtat = $sourcedir . '/filelist-lastwrite-doctemplates.txt'; + + // Get array with hash of files + $etatPrecedent = $this->checkPreviousState($fichierEtat); + + // Get list of all source files of the website + $arraySourcedir = dol_dir_list($sourcedir); + + // Get list of modified files + $modifications = []; + foreach ($arraySourcedir as $file) { + if (substr($file['name'], -4) === '.old') { + continue; + } + $hashActuel = hash_file('md5', $file['fullname']); + + // Check whether the file is new or has been modified + if (!isset($etatPrecedent[$file['name']]) || $etatPrecedent[$file['name']] !== $hashActuel) { + $modifications[] = $file; + } + + $etatPrecedent[$file['name']] = $hashActuel; // we store he new hash to record it later on disk. + } + + // Replace modified files into the doctemplates directory. + $destdirrel = 'install/doctemplates/websites/'.$website->name_template; + $destdir = DOL_DOCUMENT_ROOT.'/'.$destdirrel; + $arraydestdir = dol_dir_list($destdir, "all", 1); + $differences = []; + $names = array_column($arraydestdir, 'name'); + $namesSource = array_column($arraySourcedir, 'name'); + + if (count($modifications) > 1) { + foreach ($modifications as $fichierModifie) { + $nomFichierModifie = $fichierModifie['name']; + if ($nomFichierModifie == basename($fichierEtat)) { + continue; + } + $success = 0; + + //check if it is a new file + if ((!preg_match('/^page\d+\.tpl\.php$/', $nomFichierModifie)) && (!in_array($nomFichierModifie, $names))) { + if (file_exists($fichierModifie['fullname']) && dol_is_dir($destdir.'/containers')) { + $cp = dol_copy($fichierModifie['fullname'], $destdir.'/containers/'.$nomFichierModifie, '0664'); + if ($cp > 0) { + if (file_exists($destdir.'/containers/'.$nomFichierModifie)) { + $tabnumpage = array(); + foreach ($arraydestdir as $fileDest) { + if ($this->extractNumberFromFilename($fileDest['name']) !== -1) { + $tabnumpage[] = $this->extractNumberFromFilename($fileDest['name']); + } + } + $getContentSource = file_get_contents($destdir.'/containers/'.$nomFichierModifie); + $nextpage = max($tabnumpage) + 1; + $chaineModifiee = preg_replace('/page\d+\.tpl\.php/', 'page' . $nextpage . '.tpl.php', $getContentSource); + $write = file_put_contents($destdir.'/containers/'.$nomFichierModifie, $chaineModifiee); + if ($write !== false) { + if (!touch($destdir.'/containers/'."page" . $nextpage . ".tpl.php")) { + setEventMessages("Please check permission to create page" . $nextpage . ".tpl.php in template ".$website->name_template."", null, 'errors'); + } + $fileFounded = ''; + foreach ($arraySourcedir as $file) { + if ($file['name'] == $nomFichierModifie) { + $fileContent = file_get_contents($file['fullname']); + if (preg_match("/page\d+\.tpl\.php/", $fileContent, $matches)) { + $fileFounded = $matches[0]; + break; + } + } + } + foreach ($arraySourcedir as $file) { + if ($file['name'] == $fileFounded) { + if (!is_writable($file['fullname'])) { + dolChmod($file['fullname'], '0664'); + } + $diff = $this->showDifferences(file_get_contents($destdir.'/containers/'."page" . $nextpage . ".tpl.php"), file_get_contents($file['fullname']), array($nextpage,$this->extractNumberFromFilename($file['name']))); + if ($diff != -1) { + $replace = $this->replaceLineUsingNum($destdir.'/containers/'."page" . $nextpage . ".tpl.php", $diff); + if ($replace !== false) { + setEventMessages("Copy file page".$nextpage.".tpl.php in template ".$this->name_template." with success", null, 'warnings'); + } + } + } + } + } + } + $this->saveState($etatPrecedent, $fichierEtat); + setEventMessages("file ".$nomFichierModifie." was created in template ".$website->name_template."", null, 'warnings'); + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website->ref); + exit(); + } + } + } + + // Find the corresponding file in the destination folder + if (in_array($nomFichierModifie, $namesSource)) { + foreach ($arraydestdir as $destFile) { + if ($destFile['name'] == $nomFichierModifie) { + $sourceContent = file_get_contents($fichierModifie['fullname']); + $destContent = file_get_contents($destFile['fullname']); + + if ($sourceContent !== $destContent) { + $differences[$nomFichierModifie] = $this->showDifferences($destContent, $sourceContent); + if (count($differences[$nomFichierModifie]) > 0) { + $result = $this->replaceLineUsingNum($destFile['fullname'], $differences[$nomFichierModifie]); + if ($result !== false) { + if ($result == -2) { + setEventMessages("No permissions to write into file ".$destdirrel.'/'.$nomFichierModifie." from the current website ".$website->name_template."", null, 'errors'); + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website->ref); + exit(); + } + setEventMessages("file ".$nomFichierModifie." was modified in template ".$website->name_template."", null, 'warnings'); + } else { + setEventMessages("file ".$nomFichierModifie." was not modified", null, 'errors'); + } + } + } + } + if (preg_match('/page(\d+)\.tpl\.php/', $nomFichierModifie)) { + $differences[$nomFichierModifie] = $this->compareFichierModifie($sourcedir, $destdir, $fichierModifie); + if (count($differences[$nomFichierModifie]) > 0) { + $result = $this->replaceLineUsingNum($differences[$nomFichierModifie]['file_destination']['fullname'], $differences[$nomFichierModifie]); + if ($result !== false) { + if ($result == -2) { + setEventMessages("No permissions to write into file ".$destdirrel.'/'.$differences[$nomFichierModifie]['file_destination']['name']." from the current website ".$website->name_template."", null, 'errors'); + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website->ref); + exit(); + } + $success++; + } + } + } + } + } + } + if ($success > 0) { + // Save the state file filelist.txt + $this->saveState($etatPrecedent, $fichierEtat); + setEventMessages("file ".$differences[$nomFichierModifie]['file_destination']['name']." was modified in template ".$website->name_template."", null, 'warnings'); + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website->ref); + exit(); + } + } else { + setEventMessages("No file has been modified", null, 'errors'); + } + + // save state file + $this->saveState($etatPrecedent, $fichierEtat); + + header("Location: ".$_SERVER["PHP_SELF"].'?website='.$website->ref); + exit(); + } + + /** + * extract num of page + * @param string $filename name of file + * @return int 1 if OK, -1 if KO + */ + protected function extractNumberFromFilename($filename) + { + $matches = []; + if (preg_match('/page(\d+)\.tpl\.php/', $filename, $matches)) { + return (int) $matches[1]; + } + return -1; + } + + /** + * update name_template in table after import template + * @param string $name_template name of template + * @return int 1 if OK, -1 if KO + */ + public function setTemplateName($name_template) + { + $sql = "UPDATE ".$this->db->prefix()."website SET"; + $sql .= " name_template = '".$this->db->escape($name_template)."'"; + $sql .= " WHERE rowid = ".(int) $this->id; + $result = $this->db->query($sql); + + if ($result) { + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * check previous state for file + * @param string $pathname path of file + * @return array|mixed + */ + public function checkPreviousState($pathname) + { + if (!file_exists($pathname)) { + if (touch($pathname)) { + dolChmod($pathname, '0664'); + } + return []; + } + return unserialize(file_get_contents($pathname)); + } + + + /** + * Save state for File + * @param mixed $etat state + * @param mixed $pathname path of file + * @return int|false + */ + public function saveState($etat, $pathname) + { + return file_put_contents($pathname, serialize($etat)); + } + + /** + * create file for save state of all files in folder + * @param string $sourcedir path of folder + * @return void + */ + public function initFilesStatus($sourcedir) + { + $fichierEtat = $sourcedir . '/filelist-lastwrite-doctemplates.txt'; + + $etatPrecedent = $this->checkPreviousState($fichierEtat); + + // for first save state when create file + if (empty($etatPrecedent)) { + $arraySourcedir = dol_dir_list($sourcedir, "files"); + $etatFichiers = []; + + foreach ($arraySourcedir as $file) { + // Ignore .old files and the status file itself + if (substr($file['name'], -4) === '.old' || $file['name'] === basename($fichierEtat)) { + continue; + } + + $hashActuel = hash_file('md5', $file['fullname']); + $etatFichiers[$file['name']] = $hashActuel; + } + $this->saveState($etatFichiers, $fichierEtat); + } + } + + /** + * Compare two files has not same name but same content + * @param string $dossierSource filepath of folder source + * @param string $dossierDestination filepath of folder dest + * @param mixed $fichierModifie files modified + * @return array empty if KO, array if OK + */ + public function compareFichierModifie($dossierSource, $dossierDestination, $fichierModifie) + { + + $fichiersSource = []; + $fichiersDestination = []; + + $fichierWithNoPage = []; + $fichierWithNoPageInDest = []; + + // filter files source + foreach (dol_dir_list($dossierSource, "files") as $file) { + if (preg_match('/^page\d+/', $file['name']) && !str_contains($file['name'], '.old')) { + $fichiersSource[] = $file; + } else { + $fichierWithNoPage[] = $file; + } + } + + // filter files destination + foreach (dol_dir_list($dossierDestination, "all", 1) as $file) { + if (preg_match('/^page\d+/', $file['name']) && !str_contains($file['name'], '.old')) { + $fichiersDestination[] = $file; + } else { + $fichierWithNoPageInDest[] = $file; + } + } + + // find index source and search it in folder destination + $numOfPageSource = 0; + foreach ($fichiersSource as $index => $file) { + if ($file['name'] == basename($fichierModifie['fullname'])) { + $numOfPageSource = $this->extractNumberFromFilename($file['name']); + break; + } + } + + //search numPage where was declared + $fileFounded = array(); + foreach ($fichierWithNoPage as $filesource) { + $fileContent = file_get_contents($filesource['fullname']); + if (strpos($fileContent, "require './page".$numOfPageSource.".tpl.php'") !== false) { + $fileFounded = $filesource; + break; + } + } + // find file with same name and extract num page in destination folder + $numPageFounded = ''; + foreach ($fichierWithNoPageInDest as $filedest) { + if ($filedest['name'] === $fileFounded['name']) { + $fileContent = file_get_contents($filedest['fullname']); + if (preg_match("/page\d+\.tpl\.php/", $fileContent, $matches)) { + $numPageFounded = $matches[0]; + break; + } + } + } + //search file with the number of page founded + $fileNeeded = array(); + foreach ($fichiersDestination as $index => $file) { + if ($file['name'] == $numPageFounded) { + $fileNeeded = $file; + break; + } + } + + if (isset($fileNeeded)) { + $sourceContent = file_get_contents($fichierModifie['fullname']); + if (file_exists($fileNeeded['fullname'])) { + $destContent = file_get_contents($fileNeeded['fullname']); + + $numOfPageDest = $this->extractNumberFromFilename($fileNeeded['name']); + $differences = $this->showDifferences($destContent, $sourceContent, array($numOfPageDest,$numOfPageSource)); + $differences['file_destination'] = $fileNeeded; + } + return $differences; + } + return array(); + } + + /** + * remove espace in string + * @param string $str string + * @return string + */ + private function normalizeString($str) + { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + return $str; + } + + /** + * show difference between to string + * @param string $str1 first string + * @param string $str2 seconde string + * @param array $exceptNumPge num of page files we don't want to change + * @return array|int -1 if KO, array if OK + */ + protected function showDifferences($str1, $str2, $exceptNumPge = array()) + { + $diff = array(); + $str1 = $this->normalizeString($str1); + $str2 = $this->normalizeString($str2); + + $lines1 = explode("\n", $str1); + $lines2 = explode("\n", $str2); + + $linesShouldnChange = array(); + $linesShouldnNotChange = array(); + $linefound = array(); + $countNumPage = count($exceptNumPge); + + for ($i = 0;$i< $countNumPage; $i++) { + $linefound[$i]['meta'] = '/content="' . preg_quote($exceptNumPge[$i], '/') . '" \/>/'; + $linefound[$i]['output'] = '/dolWebsiteOutput\(\$tmp, "html", ' . preg_quote($exceptNumPge[$i], '/') . '\);/'; + } + + $maxLines = max(count($lines1), count($lines2)); + for ($lineNum = 0; $lineNum < $maxLines; $lineNum++) { + $lineContent1 = $lines1[$lineNum] ?? ''; + $lineContent2 = $lines2[$lineNum] ?? ''; + if (preg_match($linefound[0]['output'], $lineContent1)) { + $linesShouldnChange[] = $lineContent1; + } + if (preg_match($linefound[0]['meta'], $lineContent1)) { + $linesShouldnChange[] = $lineContent1; + } + if (preg_match($linefound[1]['output'], $lineContent2)) { + $linesShouldnNotChange[] = $lineContent2; + } + if (preg_match($linefound[1]['meta'], $lineContent2)) { + $linesShouldnNotChange[] = $lineContent2; + } + if ($lineContent1 !== $lineContent2) { + if (isset($lines1[$lineNum]) && !isset($lines2[$lineNum])) { + // Ligne deleted de la source + $diff["Supprimée à la ligne " . ($lineNum + 1)] = $lineContent1; + } elseif (!isset($lines1[$lineNum]) && isset($lines2[$lineNum])) { + // Nouvelle ligne added dans la destination + $diff["Ajoutée à la ligne " . ($lineNum + 1)] = $lineContent2; + } else { + // Différence found it + $diff["Modifiée à la ligne " . ($lineNum + 1)] = $lineContent2; + } + } + } + + + if (empty($linesShouldnChange)) { + $linesShouldnChange[0] = ''; + $linesShouldnChange[1] = '$tmp = ob_get_contents(); ob_end_clean(); dolWebsiteOutput($tmp, "html", '.$exceptNumPge[0].');'; + } + + $pairesRemplacement = array(); + if (!empty($linesShouldnNotChange)) { + $i =0; + foreach ($linesShouldnNotChange as $numLigne => $ligneRemplacement) { + if (isset($linesShouldnChange[$numLigne])) { + $pairesRemplacement[$ligneRemplacement] = $linesShouldnChange[$numLigne]; + } else { + $pairesRemplacement[$ligneRemplacement] = $linesShouldnChange[$i]; + } + $i++; + } + $diff['lignes_dont_change'] = $pairesRemplacement; + } + // search path of image and replace it with the correcte path + $pattern = '/medias\/image\/'.$this->ref.'\/([^\'"\s]+)/'; + + foreach ($diff as $key => $value) { + // Assurez-vous que la valeur est une chaîne + if (is_string($value)) { + if (preg_match($pattern, $value)) { + $newValue = preg_replace($pattern, 'medias/image/'.$this->name_template.'/$1', $value); + $diff[$key] = $newValue; + } + } + } + return $diff; + } + + /** + * Replace line by line in file using num of line + * @param string $desfFile path of file dest + * @param array $differences array of differences between files + * @return false|int false if we can't replace + */ + protected function replaceLineUsingNum($desfFile, $differences) + { + $userId = fileowner($desfFile); + if ($userId !== false) { + // Obtain user information from the ID + if (function_exists('posix_getpwuid')&& function_exists('posix_getpwuid')) { + $uid = posix_geteuid(); + $userInfoM = posix_getpwuid($uid); + + $userInfo = posix_getpwuid($userId); + if ($userInfo['uid'] !== $userInfoM['uid']) { + return -2; + } + } + } + if (file_exists($desfFile)) { + dolChmod($desfFile, '0664'); + } + unset($differences['file_destination']); + $contentDest = file($desfFile, FILE_IGNORE_NEW_LINES); + foreach ($differences as $key => $ligneSource) { + if (preg_match('/(Ajoutée|Modifiée) à la ligne (\d+)/', $key, $matches)) { + $typeModification = $matches[1]; + $numLigne = (int) $matches[2] - 1; + + if ($typeModification === 'Ajoutée') { + array_splice($contentDest, $numLigne, 0, $ligneSource); + } elseif ($typeModification === 'Modifiée') { + $contentDest[$numLigne] = $ligneSource; + } + } elseif (preg_match('/Supprimée à la ligne (\d+)/', $key, $matches)) { + $numLigne = (int) $matches[1] - 1; + unset($contentDest[$numLigne]); + } + } + // Reindex the table keys + $contentDest = array_values($contentDest); + $stringreplacement = implode("\n", $contentDest); + file_put_contents($desfFile, $stringreplacement); + foreach ($differences['lignes_dont_change'] as $linechanged => $line) { + if (in_array($linechanged, $contentDest)) { + dolReplaceInFile($desfFile, array($linechanged => $line)); + } + } + } } diff --git a/htdocs/website/index.php b/htdocs/website/index.php index 0028f628013d9..872431c1bb9b0 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -2405,7 +2405,6 @@ if ($fileofzip) { $file_name = basename($fileofzip); - header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=".$file_name); header("Content-Length: ".filesize($fileofzip)); @@ -2418,6 +2417,12 @@ } } +// Overwrite site +if ($action == 'overwritesite' && $user->hasRight('website', 'export')) { + if (getDolGlobalString('WEBSITE_ALLOW_OVERWRITE_GIT_SOURCE')) { + $fileofzip = $object->overwriteTemplate(); + } +} // Regenerate site if ($action == 'regeneratesite' && $usercanedit) { // Check symlink to medias and restore it if ko. Recreate also dir of website if not found. @@ -2518,6 +2523,9 @@ } if (!$error && GETPOSTISSET('templateuserfile')) { + $templatewithoutzip = preg_replace('/\.zip$/i', '', GETPOST('templateuserfile')); + $object->setTemplateName($templatewithoutzip); + $result = $object->importWebSite($fileofzip); if ($result < 0) { @@ -3014,6 +3022,11 @@ // Export web site print ''; + if (getDolGlobalString('WEBSITE_ALLOW_OVERWRITE_GIT_SOURCE')) { + // Overwrite template in sources + print 'ref).'" class="button bordertransp">'.dol_escape_htmltag($langs->trans("ExportIntoGIT")).''; + } + // Clone web site print ''; diff --git a/test/phpunit/AllTests.php b/test/phpunit/AllTests.php index 5a5c2f48b0248..7dd60f8431e54 100644 --- a/test/phpunit/AllTests.php +++ b/test/phpunit/AllTests.php @@ -99,6 +99,8 @@ public static function suite() $suite->addTestSuite('FunctionsLibTest'); require_once dirname(__FILE__).'/Functions2LibTest.php'; $suite->addTestSuite('Functions2LibTest'); + require_once dirname(__FILE__).'/ProfidLibTest.php'; + $suite->addTestSuite('ProfidLibTest'); require_once dirname(__FILE__).'/XCalLibTest.php'; $suite->addTestSuite('XCalLibTest'); diff --git a/test/phpunit/ProfidLibTest.php b/test/phpunit/ProfidLibTest.php new file mode 100644 index 0000000000000..a0995e9d246aa --- /dev/null +++ b/test/phpunit/ProfidLibTest.php @@ -0,0 +1,283 @@ + + * + * 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 https://www.gnu.org/ + */ + +/** + * \file test/phpunit/ProfidLibTest.php + * \ingroup test + * \brief PHPUnit test + * \remarks To run this script as CLI: phpunit filename.php + */ + +global $conf,$user,$langs,$db,$mysoc; +//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/profid.lib.php'; + +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'); +} +if (! defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} +if (! defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); // If there is no menu to show +} +if (! defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php +} +if (! defined('NOREQUIREAJAX')) { + define('NOREQUIREAJAX', '1'); +} +if (! defined("NOLOGIN")) { + define("NOLOGIN", '1'); // If this page is public (can be called outside logged session) +} + + + +/** + * Class for PHPUnit tests + * + * @backupGlobals disabled + * @backupStaticAttributes enabled + * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased. + */ +class ProfidLibTest extends PHPUnit\Framework\TestCase +{ + protected $savconf; + protected $savuser; + protected $savlangs; + protected $savdb; + + /** + * Constructor + * We save global variables into local variables + * + * @param string $name Name + * @return CoreTest + */ + public function __construct($name = '') + { + parent::__construct($name); + + //$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"; + } + + /** + * setUpBeforeClass + * + * @return void + */ + public static function setUpBeforeClass(): void + { + 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"; + } + + /** + * tearDownAfterClass + * + * @return void + */ + public static function tearDownAfterClass(): void + { + global $conf,$user,$langs,$db; + //$db->rollback(); + + print __METHOD__."\n"; + } + + /** + * Init phpunit tests. Restore variables before each test. + * + * @return void + */ + protected function setUp(): void + { + global $conf,$user,$langs,$db; + $conf=$this->savconf; + $user=$this->savuser; + $langs=$this->savlangs; + //$db=$this->savdb; + + print __METHOD__."\n"; + } + + /** + * End phpunit tests + * + * @return void + */ + protected function tearDown(): void + { + print __METHOD__."\n"; + } + + + + /** + * testIsValidLuhn + * + * @return void + */ + public function testIsValidLuhn() + { + // Tests OK + $this->assertTrue(isValidLuhn(972487086)); // int + $this->assertTrue(isValidLuhn("972487086")); // string + // Tests KO + $this->assertFalse(isValidLuhn(123456789)); // int + $this->assertFalse(isValidLuhn("123456789")); // string + } + + + + /** + * testIsValidSiren + * + * @return void + */ + public function testIsValidSiren() + { + // Tests OK + $this->assertTrue(isValidSiren("732829320")); + $this->assertTrue(isValidSiren(" 732 829 320 ")); // formatted with spaces + // Tests NOK + $this->assertFalse(isValidSiren("123456ABC")); // not numeric + $this->assertFalse(isValidSiren("43336767")); // Luhn test OK but length != 9 + $this->assertFalse(isValidSiren("123456789")); // 9 digits but Luhn test KO + } + + + + /** + * testIsValidSiret + * + * @return void + */ + public function testIsValidSiret() + { + // Tests OK + $this->assertTrue(isValidSiret("73282932000074")); + $this->assertTrue(isValidSiret(" 732 829 320 00074 ")); // formatted with spaces + $this->assertTrue(isValidSiret("35600000049837")); // Specific cases of "La Poste" companies + // Tests NOK + $this->assertFalse(isValidSiret("123456ABC12345")); // not numeric + $this->assertFalse(isValidSiret("3624679471379")); // Luhn test OK but length != 14 + $this->assertFalse(isValidSiret("12345678912345")); // 14 digits but Luhn test KO + } + + + + /** + * testIsValidTinForPT + * + * @return void + */ + public function testIsValidTinForPT() + { + // Tests OK + $this->assertTrue(isValidTinForPT("123456789")); + $this->assertTrue(isValidTinForPT(" 123 456 789 ")); // formatted with spaces + // Tests NOK + $this->assertFalse(isValidTinForPT("123456ABC")); // not numeric + $this->assertFalse(isValidTinForPT("12345678")); // length != 9 + } + + + + /** + * testIsValidTinForDZ + * + * @return void + */ + public function testIsValidTinForDZ() + { + // Tests OK + $this->assertTrue(isValidTinForDZ("123456789123456")); + $this->assertTrue(isValidTinForDZ(" 12345 67891 23456 ")); // formatted with spaces + // Tests NOK + $this->assertFalse(isValidTinForDZ("123456789123ABC")); // not numeric + $this->assertFalse(isValidTinForDZ("123456789123")); // length != 15 + } + + + + /** + * testIsValidTinForBE + * + * @return void + */ + public function testIsValidTinForBE() + { + // Tests OK + $this->assertTrue(isValidTinForBE("0123.123.123")); + $this->assertTrue(isValidTinForBE("1234.123.123")); + // Tests NOK + $this->assertFalse(isValidTinForBE("2345.123.123")); // First digit shall be 0 or 1 + $this->assertFalse(isValidTinForBE("1234 123 123")); // formatted with spaces instead of dots + $this->assertFalse(isValidTinForBE("1234123123")); // without dots formatting + $this->assertFalse(isValidTinForBE("ABCD.123.123")); // not digits only + } + + // TODO + /** + * testIsValidTinForES + * + * @return void + */ + /* + public function testIsValidTinForES() + { + // Tests for NIF + $this->assertEquals(1, isValidTinForES("")); // valid NIF + $this->assertEquals(-1, isValidTinForES("")); // valid regex, but invalid control key + // Tests for CIF + $this->assertEquals(2, isValidTinForES("")); // valid CIF + $this->assertEquals(-2, isValidTinForES("")); // valid regex, but invalid control key + // Tests for NIE + $this->assertEquals(3, isValidTinForES("")); // valid NIE + $this->assertEquals(-3, isValidTinForES("")); // valid regex, but invalid control key + // Tests for unknown error + $this->assertEquals(-4, isValidTinForES("")); // invalid regex for both NIF, CIF and NIE + } + */ +}