diff --git a/.forgejo/workflows/generate-release-zipfile.yml b/.forgejo/workflows/generate-release-zipfile.yml new file mode 100644 index 0000000..43b7ab2 --- /dev/null +++ b/.forgejo/workflows/generate-release-zipfile.yml @@ -0,0 +1,74 @@ +# Creates a module_sendrecurringinvoicebymail-X.Y.Z.zip file when pushing a tag +# +# This job is mainly useless (Forgejo already creates a usable .zip archive, +# minus the name) and serves more as a warmup for a decent CI/CD tryout. + +on: + push: + tags: + # For the time being, we only trigger on the tags clearly matching a + # '1.2.3' version pattern. + - '[0-9]+.[0-9]+.[0-9]+' + +env: + MYFILENAME: "module_sendrecurringinvoicebymail-${{ github.ref_name }}" + +jobs: + GenerateReleaseZipfile: + runs-on: docker + container: + image: code.bugness.org/chl/alpine-wget-git-zip:latest + steps: + - name: Download the automatic repository archive + run: | + # In case the repository is private, we build an authenticated URL + # with our action token. + MY_GITHUB_AUTHENTICATED_URL="$( echo "$GITHUB_SERVER_URL" | sed "s#^\(https\?://\)#\1$GITHUB_TOKEN\@#" )" + wget -O "$MYFILENAME.zip" "$MY_GITHUB_AUTHENTICATED_URL"/"$GITHUB_REPOSITORY"/archive/"$GITHUB_REF_NAME".zip + + - name: A bit of useless cleanup + run: | + #apk add zip + # On Forgejo, GITHUB_REPOSITORY="owner/repo" (and we just want the 'repo' part) + MY_REPOSITORY="$( echo "$GITHUB_REPOSITORY" | sed 's/.*\///' )" + zip -d "$MYFILENAME.zip" \ + "$MY_REPOSITORY/.editorconfig" \ + "$MY_REPOSITORY/.gitattributes" \ + "$MY_REPOSITORY/.gitignore" \ + "$MY_REPOSITORY/.tx*" + + - name: Upload artifact (using v4) + run: | + set -ex + + # The busybox version of wget does not offer --method=PUT as of 2024-08-26 + #apk add wget + + # We extract the Actions.Results:22:33 from ACTIONS_RUNTIME_TOKEN + # (base64 -d doesn't like when the '==' padding is missing, so 2>/dev/null and relying on the piping to forget about non-zero return code...) + read WORKFLOW_RUN_BACKEND_ID WORKFLOW_JOB_RUN_BACKEND_ID </dev/null | sed 's/.*Actions.Results:\([^:]\+\):\([^:" ]\+\).*/\1 \2/' ) + EOF + + # Get the upload URL + # note: we use the name without .zip, it seems to be added automatically. + RESPONSE="$( wget -O - \ + --header 'Content-Type:application/json' \ + --header "Authorization: Bearer $GITHUB_TOKEN" \ + --post-data "$( printf '{"version":4, "name":"%s", "workflow_run_backend_id":"%s", "workflow_job_run_backend_id":"%s"}' "$MYFILENAME" "$WORKFLOW_RUN_BACKEND_ID" "$WORKFLOW_JOB_RUN_BACKEND_ID" )" \ + "$GITHUB_SERVER_URL"/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact + )" + # We get a JSON with an signedUploadUrl similar to : + # https://entrepot.xlii.si/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=yWWEI8tIIECp8D7E5TVh4_6G2pZxWaVdQcSYaCsx5s0=&expires=2024-08-26+07%3A20%3A49.886890537+%2B0200+CEST&artifactName=mymodule-1.2.3.zip&taskID=63 + SIGNED_UPLOAD_URL="$( echo "$RESPONSE" | sed -n 's/.*"signedUploadUrl" *: *"\([^"]\+\)".*/\1/p' )" + + # Upload our file + # (note: adding '&comp=block' at the end of the URL) + wget --method PUT --body-file "$MYFILENAME.zip" "$SIGNED_UPLOAD_URL&comp=block" + + # Finalize the artifact + wget -O - \ + --header 'Content-Type:application/json' \ + --header "Authorization: Bearer $GITHUB_TOKEN" \ + --post-data "$( printf '{"hash":"sha256:%s", "name":"%s", "size":"%d", "workflow_run_backend_id":"%s", "workflow_job_run_backend_id":"%s"}' "$( sha256sum $MYFILENAME.zip | sed 's/[[:space:]]\+.*//' )" "$MYFILENAME" "$( stat -c %s $MYFILENAME.zip )" "$WORKFLOW_RUN_BACKEND_ID" "$WORKFLOW_JOB_RUN_BACKEND_ID" )" \ + "$GITHUB_SERVER_URL"/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact diff --git a/ChangeLog.md b/ChangeLog.md index b32a51e..cdf58df 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,38 @@ -# CHANGELOG SENDRECURRINGINVOICEBYMAIL FOR DOLIBARR ERP CRM +# CHANGELOG SENDRECURRINGINVOICEBYMAIL FOR [DOLIBARR ERP CRM](https://www.dolibarr.org) + +## 0.3.6 + +Fix: freeform email addresses being "html-filtered" `Postmaster ` → `Postmaster` + + +## 0.3.5 + +Fix: + +* HTML formating was silently removed when used on Dolibarr v.13+ (GH-11) +* `$conf->global->MAIN_MAIL_ERRORS_TO` might not always be set (cause not found) + + +## 0.3.4 + +Fix: the hook was also triggered by supplier invoices. +Thanks to jpardenoy for the report and the fix. + + +## 0.3.3 + +Fix: adds CSRF protection. + + +## 0.3.2 + +Note: This release includes a DB schema modification. Reactivate the module to trigger it. + +Enhancements: + +* Mails can now be sent in HTML (via global module configuration and via the customzation tab). +* Mails can now be sent even when the invoice is a draft. + ## 0.3.1 diff --git a/README.md b/README.md index 39a3144..2f736af 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ## Features -(en) This module send the invoice generated with recurring invoices by email to the client. +(en) This module sends by email the customer invoice generated with a recurring invoice template via scheduled jobs. -(fr) Ce module envoie par mail les factures générées automatiquement par les travaux planifiés et les factures modèles. +(fr) Ce module envoie par mail les factures clientes générées automatiquement par les travaux planifiés et les factures modèles. You can customize the mail globally or by recurring invoice. @@ -14,7 +14,7 @@ To edit the default global mail template, go to Home > Setup > Emails > Email te To edit the default sender address, go to Home > Setup > Emails, and edit the `Sender email for automatic emails` field. -This module is triggered by the cron (Scheduled jobs module) and will not send emails when manually generating an invoice. +This module hooks himself on the end of the `Recurring invoices` job from the Scheduled jobs (aka. `cron`) module. It will only be triggered via this Scheduled job and will not send mail when manually generating an invoice from a recurring invoice template. ## Requirements @@ -30,11 +30,18 @@ Other modules are available on `Setup` > `Modules/Applications` and finally the `Deploy/install external app/module` tab +and upload the module_sendrecurringinvoicebymail-x.y.z.zip file (you can get it from the +[original forge](https://code.bugness.org/Dolibarr/sendrecurringinvoicebymail/releases) +or [Github](https://github.com/bugness-chl/sendrecurringinvoicebymail/releases)). + +Next, on the `Modules/Applications` page, activate the newly available sendrecurringinvoicebymail module, +and probably the Scheduled jobs (alias cron or modCron) integrated module too. -Note: If this screen tell you there is no custom directory, check your setup is correct: +#### Troubleshooting + +Note: If the module screen tells you there is no custom directory, check that your setup is correct: - In your Dolibarr installation directory, edit the ```htdocs/conf/conf.php``` file and check that following lines are not commented: @@ -65,12 +72,10 @@ Note: If this screen tell you there is no custom directory, check your setup is ```sh cd ....../custom -git clone git@github.com:bugness-chl/sendrecurringinvoicebymail.git sendrecurringinvoicebymail +git clone https://code.bugness.org/Dolibarr/sendrecurringinvoicebymail.git sendrecurringinvoicebymail ``` -### Final steps - -From your browser: +Then, from your browser: - Log into Dolibarr as a super-administrator - Go to "Setup" -> "Modules" @@ -94,7 +99,7 @@ GPLv3 or (at your option) any later version. See file COPYING for more information. -#### Documentation +### Documentation All texts and readmes. diff --git a/admin/setup.php b/admin/setup.php new file mode 100644 index 0000000..42c53fe --- /dev/null +++ b/admin/setup.php @@ -0,0 +1,181 @@ + + * Copyright (C) 2021 Chl + * + * 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 sendrecurringinvoicebymail/admin/setup.php + * \ingroup sendrecurringinvoicebymail + * \brief SRIBM setup page. + */ + +// Load Dolibarr environment +$res=0; +// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined) +if (! $res && ! empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) $res=@include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; +// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME +$tmp=empty($_SERVER['SCRIPT_FILENAME'])?'':$_SERVER['SCRIPT_FILENAME'];$tmp2=realpath(__FILE__); $i=strlen($tmp)-1; $j=strlen($tmp2)-1; +while($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i]==$tmp2[$j]) { $i--; $j--; } +if (! $res && $i > 0 && file_exists(substr($tmp, 0, ($i+1))."/main.inc.php")) $res=@include substr($tmp, 0, ($i+1))."/main.inc.php"; +if (! $res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i+1)))."/main.inc.php")) $res=@include dirname(substr($tmp, 0, ($i+1)))."/main.inc.php"; +// Try main.inc.php using relative path +if (! $res && file_exists("../../main.inc.php")) $res=@include "../../main.inc.php"; +if (! $res && file_exists("../../../main.inc.php")) $res=@include "../../../main.inc.php"; +if (! $res) die("Include of main fails"); + +global $langs, $user; + +// Libraries +require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php"; +//require_once '../lib/mymodule.lib.php'; +//require_once "../class/myclass.class.php"; + +// Translations +$langs->loadLangs(array("admin", "sendrecurringinvoicebymail@sendrecurringinvoicebymail")); + +// Access control +if (! $user->admin) accessforbidden(); + +// Parameters +$action = GETPOST('action', 'alpha'); +$backtopage = GETPOST('backtopage', 'alpha'); + +$arrayofparameters=array( + //'SENDRECURRINGINVOICEBYMAIL_MYPARAM2'=>array('css'=>'minwidth500','enabled'=>1) + 'SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULT' => array( + 'css' => 'minwidth200', + 'selectvalues' => array( + -1 => 'MailBodyFormatAutoDetect', + 0 => 'MailBodyFormatPlainText', + 1 => 'MailBodyFormatHtml', + ), + ), +); + + + +/* + * Actions + */ + +if ((float) DOL_VERSION >= 6) +{ + include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; +} + + + +/* + * View + */ + +$page_name = "sendrecurringinvoicebymailSetup"; +llxHeader('', $langs->trans($page_name)); + +// Subheader +$linkback = ''.$langs->trans("BackToModuleList").''; + +print load_fiche_titre($langs->trans($page_name), $linkback, 'object_mymodule@mymodule'); + +// Configuration header +//$head = mymoduleAdminPrepareHead(); +$head = array(); +dol_fiche_head($head, 'settings', '', -1, "sendrecurringinvoicebymail@sendrecurringinvoicebymail"); + +// Setup page goes here +echo ''.$langs->trans("sendrecurringinvoicebymailSetupPage").'

'; + + +if ($action == 'edit') +{ + print '
'; + print ''; + print ''; + + print ''; + print ''; + + foreach($arrayofparameters as $key => $val) + { + print '\n"; + print '\n"; + } + print '
'.$langs->trans("Parameter").''.$langs->trans("Value").'
'; + $tooltiphelp = (($langs->trans($key.'Tooltip') != $key.'Tooltip') ? $langs->trans($key.'Tooltip') : ''); + print $form->textwithpicto($langs->trans($key), $tooltiphelp); + print "'; + if (isset($val['selectvalues'])) { + // Select input + print '\n"; + } else { + // Simple input + print ''; + } + print "
'; + + print '
'; + print ''; + print '
'; + + print '
'; + print '
'; +} +else +{ + if (! empty($arrayofparameters)) + { + print ''; + print ''; + + foreach($arrayofparameters as $key => $val) + { + print ''; + } + + print '
'.$langs->trans("Parameter").''.$langs->trans("Value").'
'; + $tooltiphelp = (($langs->trans($key.'Tooltip') != $key.'Tooltip') ? $langs->trans($key.'Tooltip') : ''); + print $form->textwithpicto($langs->trans($key), $tooltiphelp); + print '' . $conf->global->$key . '
'; + + print '
'; + print ''.$langs->trans("Modify").''; + print '
'; + } + else + { + print '
'.$langs->trans("NothingToSetup"); + } + + // Display a notice if the 'cron' module is not enabled + if (! in_array('cron', $conf->modules, true)) { + print '

' . $langs->trans('NoticeCronIsDisabled') . "

\n"; + } +} + + +// Page end +dol_fiche_end(); + +llxFooter(); +$db->close(); diff --git a/class/actions_sendrecurringinvoicebymail.class.php b/class/actions_sendrecurringinvoicebymail.class.php index 2f0c15c..ceb5417 100644 --- a/class/actions_sendrecurringinvoicebymail.class.php +++ b/class/actions_sendrecurringinvoicebymail.class.php @@ -84,15 +84,23 @@ class Actionssendrecurringinvoicebymail $error = 0; // Error counter $facturerec = $parameters['facturerec']; + // Since Dolibarr 16, this hook is also used for the FactureFournisseurRec class. + if (! $facturerec instanceof FactureRec) { + return 0; + } + + // Load our own object, linked to this facture + // (if it doesn't exist in database, fetch(,,true) will fill the object + // from the global mail template) $mailObject = new SRIBMCustomMailInfo($this->db); if ($mailObject->fetch(null, $facturerec->id, true) != 1) { dol_syslog("Error loading SRIBMCustomMailInfo for facture rec " . (isset($facturerec->id) ? $facturerec->id : "(facturerec->id not set ??)")); return -1; } - // We only send the mail when the invoice is not a draft - // and the sending is enabled for the template. - if ($object->brouillon || !$mailObject->active) { + // Abort sending when inactive + // (draft invoice or explictly disabled) + if (!$mailObject->active) { return 0; } @@ -112,10 +120,11 @@ class Actionssendrecurringinvoicebymail 'to' => implode(', ', $mailObject->compileEmails('to', true)), 'cc' => implode(', ', $mailObject->compileEmails('cc', true)), 'bcc' => implode(', ', $mailObject->compileEmails('bcc', true)), - 'errorsTo' => $conf->global->MAIN_MAIL_ERRORS_TO, - 'replyTo' => $conf->global->MAIN_MAIL_ERRORS_TO, + 'errorsTo' => (isset($conf->global->MAIN_MAIL_ERRORS_TO) ? $conf->global->MAIN_MAIL_ERRORS_TO : ''), + 'replyTo' => (isset($conf->global->MAIN_MAIL_ERRORS_TO) ? $conf->global->MAIN_MAIL_ERRORS_TO : ''), 'subject' => $mailObject->subject, - 'message' => $mailObject->body_plaintext, + 'message' => $mailObject->body, + 'ishtml' => $mailObject->body_ishtml, ); // Check that we have a recipient, to avoid some frequent error... @@ -154,7 +163,7 @@ class Actionssendrecurringinvoicebymail $mail_data['cc'], // CC $mail_data['bcc'], // BCC 0, //deliveryreceipt - 0, //msgishtml + $mail_data['ishtml'], //msgishtml $mail_data['errorsTo'], '', // css '', // trackid diff --git a/class/sribmcustommailinfo.class.php b/class/sribmcustommailinfo.class.php index 6559134..a611e5c 100644 --- a/class/sribmcustommailinfo.class.php +++ b/class/sribmcustommailinfo.class.php @@ -109,12 +109,12 @@ class SRIBMCustomMailInfo extends CommonObject /** * @var string */ - public $body_plaintext; + public $body; /** - * @var string (not used at the moment) + * @var int 0: plain text, 1: html, -1: auto (see CMailFile and dol_ishtml()) */ - public $body_html; + public $body_ishtml = 0; // End of database fields @@ -228,7 +228,7 @@ class SRIBMCustomMailInfo extends CommonObject $sql .= " SET "; $sql .= " fk_facture_rec = " . (int)$this->fk_facture_rec; $sql .= ", active = " . (int)$this->active; - $sql .= ", addmaindocfile = '" . (int)$this->addmaindocfile . "'"; + $sql .= ", addmaindocfile = " . (int)$this->addmaindocfile; $sql .= ", fromtype = '" . $this->db->escape($this->fromtype) . "'"; $sql .= ", frommail = '" . $this->db->escape($this->frommail) . "'"; $sql .= ", sendto_thirdparty = " . (int)$this->db->escape($this->sendto_thirdparty); @@ -238,8 +238,8 @@ class SRIBMCustomMailInfo extends CommonObject $sql .= ", sendbcc_thirdparty = " . (int)$this->db->escape($this->sendbcc_thirdparty); $sql .= ", sendbcc_free = '" . $this->db->escape($this->sendbcc_free) . "'"; $sql .= ", subject = '" . $this->db->escape($this->subject) . "'"; - $sql .= ", body_plaintext = '" . $this->db->escape($this->body_plaintext) . "'"; - $sql .= ", body_html = '" . $this->db->escape($this->body_html) . "'"; + $sql .= ", body = '" . $this->db->escape($this->body) . "'"; + $sql .= ", body_ishtml = " . (int)$this->body_ishtml; $sql .= " WHERE rowid = " . (int)$this->id; $result = $this->db->query($sql); @@ -281,7 +281,7 @@ class SRIBMCustomMailInfo extends CommonObject { global $conf; - $sql = "SELECT rowid, fk_facture_rec, active, addmaindocfile, fromtype, frommail, sendto_thirdparty, sendto_free, sendcc_thirdparty, sendcc_free, sendbcc_thirdparty, sendbcc_free, subject, body_plaintext, body_html"; + $sql = "SELECT rowid, fk_facture_rec, active, addmaindocfile, fromtype, frommail, sendto_thirdparty, sendto_free, sendcc_thirdparty, sendcc_free, sendbcc_thirdparty, sendbcc_free, subject, body, body_ishtml"; $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element; $sql .= " WHERE " . (isset($ref) ? 'fk_facture_rec = ' . (int)$ref : "rowid = " . (int)$rowid); @@ -309,8 +309,8 @@ class SRIBMCustomMailInfo extends CommonObject $this->sendbcc_thirdparty = $obj->sendbcc_thirdparty; $this->sendbcc_free = $obj->sendbcc_free; $this->subject = $obj->subject; - $this->body_plaintext = $obj->body_plaintext; - $this->body_html = $obj->body_html; + $this->body = $obj->body; + $this->body_ishtml = $obj->body_ishtml; $ref = $obj->fk_facture_rec; } elseif (!$fill_defaults_from_template) { $this->error = "SRIBMCustomMailInfo not found (id: " . var_export($rowid, true) . ", ref: " . var_export($ref, true); @@ -366,8 +366,15 @@ class SRIBMCustomMailInfo extends CommonObject } $this->subject = $template->topic; - $this->body_plaintext = $template->content; + $this->body = $template->content; $this->addmaindocfile = $template->joinfiles; + + // By default, we don't send emails when the generated invoice is + // still a draft. + $this->active = $this->fac_rec_object->auto_validate; + + // Retrieve the default body format from config. + $this->body_ishtml = $conf->global->SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULT; } return 1; diff --git a/core/modules/modsendrecurringinvoicebymail.class.php b/core/modules/modsendrecurringinvoicebymail.class.php index 0ce3334..4a2cc1f 100644 --- a/core/modules/modsendrecurringinvoicebymail.class.php +++ b/core/modules/modsendrecurringinvoicebymail.class.php @@ -52,7 +52,7 @@ class modsendrecurringinvoicebymail extends DolibarrModules // Family can be 'base' (core modules),'crm','financial','hr','projects','products','ecm','technic' (transverse modules),'interface' (link with external tools),'other','...' // It is used to group modules by family in module setup page - $this->family = "crm"; + $this->family = "financial"; // Module position in the family on 2 digits ('01', '10', '20', ...) $this->module_position = '90'; // Gives the possibility for the module, to provide his own family info and position of this family (Overwrite $this->family and $this->module_position. Avoid this) @@ -69,7 +69,7 @@ class modsendrecurringinvoicebymail extends DolibarrModules $this->editor_url = 'https://code.bugness.org/Dolibarr/sendrecurringinvoicebymail'; // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z' - $this->version = '0.3.1'; + $this->version = '0.3.6'; //Url to the file with your last numberversion of this module //$this->url_last_version = 'http://www.example.com/versionmodule.txt'; @@ -103,7 +103,7 @@ class modsendrecurringinvoicebymail extends DolibarrModules $this->dirs = array("/sendrecurringinvoicebymail/temp"); // Config pages. Put here list of php page, stored into sendrecurringinvoicebymail/admin directory, to use to setup module. - //$this->config_page_url = array("setup.php@sendrecurringinvoicebymail"); + $this->config_page_url = array("setup.php@sendrecurringinvoicebymail"); // Dependencies $this->hidden = false; // A condition to hide module @@ -124,6 +124,15 @@ class modsendrecurringinvoicebymail extends DolibarrModules // 1=>array('SENDRECURRINGINVOICEBYMAIL_MYNEWCONST2','chaine','myvalue','This is another constant to add',0, 'current', 1) // ); $this->const = array( + 0 => array( + 'SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULT', // key + 'chaine', // always 'chaine' ? + '0', // value + 'default format for mail body : -1 for auto-detect, 0 for plain text, 1 for HTML.', // desc + 1, // visible + 'current', // current or allentities + 0, // deleteonunactive + ), //1=>array('SENDRECURRINGINVOICEBYMAIL_MYCONSTANT', 'chaine', 'avalue', 'This is a constant to add', 1, 'allentities', 1) ); @@ -357,7 +366,7 @@ class modsendrecurringinvoicebymail extends DolibarrModules $this->db->query("INSERT INTO " . MAIN_DB_PREFIX . "sribm_custom_mail_info (fk_facture_rec, fromtype, frommail) VALUES (" . (int)$row->rid . ", 'robot', '" . $this->db->escape($conf->global->MAIN_MAIL_EMAIL_FROM) . "')"); $sid = $this->db->last_insert_id(MAIN_DB_PREFIX . 'sribm_custom_mail_info'); } - foreach (array('subject' => 'subject', 'body' => 'body_plaintext', 'sendto' => 'sendto_free') as $key => $item) { + foreach (array('subject' => 'subject', 'body' => 'body', 'sendto' => 'sendto_free') as $key => $item) { if (! empty($mail_data[$key])) { // We loop on each field. // Not optimized, I know. diff --git a/fiche-rec-tab1.php b/fiche-rec-tab1.php index b787ffd..69fcb98 100644 --- a/fiche-rec-tab1.php +++ b/fiche-rec-tab1.php @@ -56,18 +56,14 @@ do { */ // Load necessary data - $object = new FactureRec($db); $mailObject = new SRIBMCustomMailInfo($db); $form = new Form($db); - if ($object->fetch($id) <= 0 or !$object->id) - { + if ($mailObject->fetch(null, $id, true) <= 0) { + // Note : this should only happen when facture-rec doesn't exist or some database error. + // If sribmcustommailinfo doesn't exist in database, we should still get a instance of the template. setEventMessages($langs->trans("ErrorRecordNotFound"), null, 'errors'); break; } - if ($mailObject->fetch(null, $object->id, true) <= 0) { - setEventMessages("Weird stuff, shouldn't happen : " . $mailObject->error); - break; - } // List of senders (user, company, robot, ...) $listFrom = array(); @@ -92,7 +88,7 @@ do { // Substitution array/string $helpforsubstitution = $langs->trans('AvailableVariables').' :
'."\n"; - $tmparray = getCommonSubstitutionArray($langs, 0, null, $object); + $tmparray = getCommonSubstitutionArray($langs, 0, null, $mailObject->fac_rec_object); complete_substitutions_array($tmparray, $langs); foreach($tmparray as $key => $val) { $helpforsubstitution .= $key . ' -> ' . $langs->trans(dol_string_nohtmltag($val)) . '
'; @@ -107,7 +103,7 @@ do { if (GETPOST('save')) { do { // Validate input data - if (! array_key_exists(GETPOST('fromtype', 'alpha'), $listFrom)) { + if (! array_key_exists(GETPOST('fromtype'), $listFrom)) { setEventMessages('Unexpected from value', null, 'errors'); break; } @@ -120,18 +116,22 @@ do { break; } // Validate some non-breaking stuff after feeding - if (empty(GETPOST('sendto_free', 'alpha')) && empty(GETPOST('sendto_socpeople', 'array'))) { + if (empty(GETPOST('sendto_free', 'none')) && empty(GETPOST('sendto_socpeople', 'array'))) { // Kinda weird behaviour from CMailFile but better alert the user beforehand // FIXME: check if there is a workaround ? setEventMessages("In some configuration, CMailFile doesn't allow empty 'to' recipient. You should set at least one.", null, 'warnings'); //break; } - if (! strlen(GETPOST('subject', 'alpha'))) { + if (! strlen(GETPOST('subject', 'none'))) { // Kinda weird behaviour from CMailFile but better alert the user beforehand // FIXME: check if there is a workaround ? setEventMessages("In some configuration, CMailFile doesn't allow empty subject. You should set one.", null, 'warnings'); //break; } + if (! in_array(GETPOST('body_ishtml', 'int'), array('-1', '0', '1'), true)) { + setEventMessages("Unexpected body_ishtml value", null, 'errors'); + break; + } // Feed the input data to the model $mailObject->active = GETPOST('active', 'int') ? 1 : 0; @@ -140,14 +140,15 @@ do { $mailObject->fromtype = GETPOST('fromtype', 'alpha'); $mailObject->frommail = $listFrom[$mailObject->fromtype]; - $mailObject->sendto_free = GETPOST('sendto_free', 'alpha'); + $mailObject->sendto_free = GETPOST('sendto_free', 'none'); $mailObject->sendto_thirdparty = in_array('thirdparty', GETPOST('sendto_socpeople', 'array')); - $mailObject->sendcc_free = GETPOST('sendcc_free', 'alpha'); + $mailObject->sendcc_free = GETPOST('sendcc_free', 'none'); $mailObject->sendcc_thirdparty = in_array('thirdparty', GETPOST('sendcc_socpeople', 'array')); - $mailObject->subject = GETPOST('subject', 'alpha'); - $mailObject->body_plaintext = GETPOST('body_plaintext', 'alpha'); + $mailObject->subject = GETPOST('subject', 'none'); + $mailObject->body = GETPOST('body', 'none'); + $mailObject->body_ishtml = (int)GETPOST('body_ishtml', 'int'); // Save into database if ($mailObject->id) { @@ -156,7 +157,7 @@ do { break; } } else { - $mailObject->fk_facture_rec = $object->id; + $mailObject->fk_facture_rec = $mailObject->fac_rec_object->id; if ($mailObject->create($user) < 0) { setEventMessages($langs->trans("ErrorSQL") . ' : ' . $mailObject->error, null, 'errors'); break; @@ -225,7 +226,7 @@ do { */ // Same tabs than the main page - $head=invoice_rec_prepare_head($object); + $head=invoice_rec_prepare_head($mailObject->fac_rec_object); $output .= dol_get_fiche_head($head, 'sendrecurringinvoicebymail', $langs->trans("RepeatableInvoice"), -1, 'bill'); // Add a div $output .= '
' . $mailObject->fac_rec_object->ref . "
\n"; $output .= '
' . $langs->trans('ThirdParty') . ' : ' . $mailObject->fac_rec_object->thirdparty->getNomUrl(1) . "
\n"; @@ -233,6 +234,12 @@ do { $output .= '
' . $langs->trans("Options") . "
\n"; $output .= '
'; + if (function_exists('newToken')) { + $output .= ''; // CSRF protection + } else { + // Used before Dolibar 13 + $output .= ''; // CSRF protection + } $output .= ''; $output .= ''; $output .= ' \n"; @@ -295,7 +302,7 @@ do { $output .= '\n"; - // body_plaintext + // body $output .= '\n\n"; + // body_ishtml + $output .= '\n"; + $tmp_ishtml = (int)(GETPOSTISSET('body_ishtml') ? GETPOST('body_ishtml', 'int') : $mailObject->body_ishtml); + // selectarray() does funny things with -1 key, so we build it manually. + //$output .= $form->selectarray('body_ishtml', $listBodyIsHtml, $mailObject->body_ishtml); + $output .= '\n"; + $output .= "
' . $langs->trans("JoinMainDoc") . "
'; $output .= $form->textwithpicto($langs->trans("MailText"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody'); $output .= ""; @@ -306,11 +313,24 @@ do { $doleditor = new DolEditor('body_plaintext', (GETPOST('body_plaintext', 'alpha') ? GETPOST('body_plaintext', 'alpha') : $mailObject->body_plaintext), '', 280); $output .= $doleditor->Create(1); */ - $output .= '\n"; $output .= "
' . $langs->trans('MailBodyFormat') . "\n
\n"; $output .= '
'; diff --git a/langs/en_US/sendrecurringinvoicebymail.lang b/langs/en_US/sendrecurringinvoicebymail.lang index 92ea605..dc9d28f 100644 --- a/langs/en_US/sendrecurringinvoicebymail.lang +++ b/langs/en_US/sendrecurringinvoicebymail.lang @@ -28,10 +28,9 @@ ModulesendrecurringinvoicebymailDesc = Send FactureRec generated invoices by mai sendrecurringinvoicebymailSetup = sendrecurringinvoicebymail setup Settings = Settings sendrecurringinvoicebymailSetupPage = sendrecurringinvoicebymail setup page -SENDRECURRINGINVOICEBYMAIL_MYPARAM1 = My param 1 -SENDRECURRINGINVOICEBYMAIL_MYPARAM1Tooltip = My param 1 tooltip -SENDRECURRINGINVOICEBYMAIL_MYPARAM2=My param 2 -SENDRECURRINGINVOICEBYMAIL_MYPARAM2Tooltip=My param 2 tooltip +NoticeCronIsDisabled = Notice: the cron / Scheduled Jobs module seems disabled. Unless you know what you are doing, invoices won't be generated and emails won't be sent. +SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULT = Default mail body's format +SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULTTooltip = -1 for auto-detect, 0 for plaintext, 1 for HTML # @@ -41,10 +40,16 @@ About = About sendrecurringinvoicebymailAbout = About sendrecurringinvoicebymail sendrecurringinvoicebymailAboutPage = sendrecurringinvoicebymail about page +# # Tab title on the fiche-rec page +# CustomizationTitle = Customization CustomizationIntro = Below, you can override the global email template for this particular recurring invoice. To remove all customization, click on the %s button. CustomizationLinkToGlobalTemplate = As a note, the global email template can be configured here, and the default sender address can be configured there. +MailBodyFormat = Message format +MailBodyFormatAutoDetect = Auto-detect +MailBodyFormatPlainText = Plain text +MailBodyFormatHtml = HTML OptionEnable = Send mail when generating via cron (cf. module "Scheduled Jobs") Reset = Reset ResetDone = Reset done : customization deleted diff --git a/langs/fr_FR/sendrecurringinvoicebymail.lang b/langs/fr_FR/sendrecurringinvoicebymail.lang index 47a29da..cdd1af6 100644 --- a/langs/fr_FR/sendrecurringinvoicebymail.lang +++ b/langs/fr_FR/sendrecurringinvoicebymail.lang @@ -28,6 +28,10 @@ ModulesendrecurringinvoicebymailDesc = Envoi par mail des factures générées p sendrecurringinvoicebymailSetup = Configuration du module sendrecurringinvoicebymail Settings = Réglages sendrecurringinvoicebymailSetupPage = Page de configuration du module sendrecurringinvoicebymail +NoticeCronIsDisabled = Notice : le module cron / Travaux Planifiés semble désactivé. Sauf cas particulier, les factures ne seront pas générées et les mails ne seront pas envoyés. +SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULT = Format par défaut du corps du mail +SENDRECURRINGINVOICEBYMAIL_BODY_ISHTML_DEFAULTTooltip = -1: auto-détection, 0: texte pur, 1: HTML + # # Page À propos @@ -42,6 +46,10 @@ sendrecurringinvoicebymailAboutPage = Page à propos de sendrecurringinvoicebyma CustomizationTitle = Personnalisation CustomizationIntro = Ci-dessous, vous pouvez personnaliser les emails pour cette facture-modèle. Pour annuler toute personnalisation, cliquez sur le bouton '%s'. CustomizationLinkToGlobalTemplate = Pour mémoire, le template global est configurable par ici, et l'adresse de l'émetteur par défaut est configurable par là. +MailBodyFormat = Format du message +MailBodyFormatAutoDetect = Auto-detect +MailBodyFormatPlainText = Texte pur +MailBodyFormatHtml = HTML OptionEnable = Envoyer par email lors d'une génération automatique via le module "Travaux planifiés" Reset = Réinitialiser ResetDone = Réinitialisation effectuée : personnalisation supprimée. diff --git a/sql/update_0.3.1-0.3.2.sql b/sql/update_0.3.1-0.3.2.sql new file mode 100644 index 0000000..388337c --- /dev/null +++ b/sql/update_0.3.1-0.3.2.sql @@ -0,0 +1,21 @@ +-- Copyright (C) 2021 Chl +-- +-- 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 http://www.gnu.org/licenses/. + + +-- We designed the schema too quickly : CMailFile doesn't let us manage +-- independently the text and html parts. All we can do is set the mode. +ALTER TABLE llx_sribm_custom_mail_info CHANGE COLUMN body_plaintext body mediumtext; +ALTER TABLE llx_sribm_custom_mail_info DROP COLUMN body_html; +ALTER TABLE llx_sribm_custom_mail_info ADD COLUMN body_ishtml smallint DEFAULT 0 NOT NULL;