Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
38a96d7ac2 | |||
60b4a1fd99 | |||
|
59f543e196 |
6 changed files with 27 additions and 133 deletions
|
@ -1,74 +0,0 @@
|
||||||
# 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 <<EOF
|
|
||||||
$( echo "$ACTIONS_RUNTIME_TOKEN" | sed 's/.*\.\(.*\)\..*/\1/' | base64 -d 2>/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
|
|
23
ChangeLog.md
23
ChangeLog.md
|
@ -1,28 +1,5 @@
|
||||||
# CHANGELOG SENDRECURRINGINVOICEBYMAIL FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
# CHANGELOG SENDRECURRINGINVOICEBYMAIL FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
||||||
|
|
||||||
## 0.3.6
|
|
||||||
|
|
||||||
Fix: freeform email addresses being "html-filtered" `Postmaster <postmaster@bugness.org>` → `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
|
## 0.3.2
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
(en) This module sends by email the customer invoice generated with a recurring invoice template via scheduled jobs.
|
(en) This module sends by email the invoice generated with recurring invoices via scheduled jobs.
|
||||||
|
|
||||||
(fr) Ce module envoie par mail les factures clientes générées automatiquement par les travaux planifiés et les factures modèles.
|
(fr) Ce module envoie par mail les factures 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.
|
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.
|
To edit the default sender address, go to Home > Setup > Emails, and edit the `Sender email for automatic emails` field.
|
||||||
|
|
||||||
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.
|
This module is triggered by the cron (Scheduled jobs module) and will not send emails when manually generating an invoice.
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
|
@ -84,14 +84,6 @@ class Actionssendrecurringinvoicebymail
|
||||||
$error = 0; // Error counter
|
$error = 0; // Error counter
|
||||||
|
|
||||||
$facturerec = $parameters['facturerec'];
|
$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);
|
$mailObject = new SRIBMCustomMailInfo($this->db);
|
||||||
if ($mailObject->fetch(null, $facturerec->id, true) != 1) {
|
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 ??)"));
|
dol_syslog("Error loading SRIBMCustomMailInfo for facture rec " . (isset($facturerec->id) ? $facturerec->id : "(facturerec->id not set ??)"));
|
||||||
|
@ -120,8 +112,8 @@ class Actionssendrecurringinvoicebymail
|
||||||
'to' => implode(', ', $mailObject->compileEmails('to', true)),
|
'to' => implode(', ', $mailObject->compileEmails('to', true)),
|
||||||
'cc' => implode(', ', $mailObject->compileEmails('cc', true)),
|
'cc' => implode(', ', $mailObject->compileEmails('cc', true)),
|
||||||
'bcc' => implode(', ', $mailObject->compileEmails('bcc', true)),
|
'bcc' => implode(', ', $mailObject->compileEmails('bcc', true)),
|
||||||
'errorsTo' => (isset($conf->global->MAIN_MAIL_ERRORS_TO) ? $conf->global->MAIN_MAIL_ERRORS_TO : ''),
|
'errorsTo' => $conf->global->MAIN_MAIL_ERRORS_TO,
|
||||||
'replyTo' => (isset($conf->global->MAIN_MAIL_ERRORS_TO) ? $conf->global->MAIN_MAIL_ERRORS_TO : ''),
|
'replyTo' => $conf->global->MAIN_MAIL_ERRORS_TO,
|
||||||
'subject' => $mailObject->subject,
|
'subject' => $mailObject->subject,
|
||||||
'message' => $mailObject->body,
|
'message' => $mailObject->body,
|
||||||
'ishtml' => $mailObject->body_ishtml,
|
'ishtml' => $mailObject->body_ishtml,
|
||||||
|
|
|
@ -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','...'
|
// 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
|
// It is used to group modules by family in module setup page
|
||||||
$this->family = "financial";
|
$this->family = "crm";
|
||||||
// Module position in the family on 2 digits ('01', '10', '20', ...)
|
// Module position in the family on 2 digits ('01', '10', '20', ...)
|
||||||
$this->module_position = '90';
|
$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)
|
// 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';
|
$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'
|
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z'
|
||||||
$this->version = '0.3.6';
|
$this->version = '0.3.3';
|
||||||
|
|
||||||
//Url to the file with your last numberversion of this module
|
//Url to the file with your last numberversion of this module
|
||||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||||
|
|
|
@ -103,7 +103,7 @@ do {
|
||||||
if (GETPOST('save')) {
|
if (GETPOST('save')) {
|
||||||
do {
|
do {
|
||||||
// Validate input data
|
// Validate input data
|
||||||
if (! array_key_exists(GETPOST('fromtype'), $listFrom)) {
|
if (! array_key_exists(GETPOST('fromtype', 'alpha'), $listFrom)) {
|
||||||
setEventMessages('Unexpected from value', null, 'errors');
|
setEventMessages('Unexpected from value', null, 'errors');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -116,13 +116,13 @@ do {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Validate some non-breaking stuff after feeding
|
// Validate some non-breaking stuff after feeding
|
||||||
if (empty(GETPOST('sendto_free', 'none')) && empty(GETPOST('sendto_socpeople', 'array'))) {
|
if (empty(GETPOST('sendto_free', 'alpha')) && empty(GETPOST('sendto_socpeople', 'array'))) {
|
||||||
// Kinda weird behaviour from CMailFile but better alert the user beforehand
|
// Kinda weird behaviour from CMailFile but better alert the user beforehand
|
||||||
// FIXME: check if there is a workaround ?
|
// 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');
|
setEventMessages("In some configuration, CMailFile doesn't allow empty 'to' recipient. You should set at least one.", null, 'warnings');
|
||||||
//break;
|
//break;
|
||||||
}
|
}
|
||||||
if (! strlen(GETPOST('subject', 'none'))) {
|
if (! strlen(GETPOST('subject', 'alpha'))) {
|
||||||
// Kinda weird behaviour from CMailFile but better alert the user beforehand
|
// Kinda weird behaviour from CMailFile but better alert the user beforehand
|
||||||
// FIXME: check if there is a workaround ?
|
// FIXME: check if there is a workaround ?
|
||||||
setEventMessages("In some configuration, CMailFile doesn't allow empty subject. You should set one.", null, 'warnings');
|
setEventMessages("In some configuration, CMailFile doesn't allow empty subject. You should set one.", null, 'warnings');
|
||||||
|
@ -140,14 +140,14 @@ do {
|
||||||
$mailObject->fromtype = GETPOST('fromtype', 'alpha');
|
$mailObject->fromtype = GETPOST('fromtype', 'alpha');
|
||||||
$mailObject->frommail = $listFrom[$mailObject->fromtype];
|
$mailObject->frommail = $listFrom[$mailObject->fromtype];
|
||||||
|
|
||||||
$mailObject->sendto_free = GETPOST('sendto_free', 'none');
|
$mailObject->sendto_free = GETPOST('sendto_free', 'alpha');
|
||||||
$mailObject->sendto_thirdparty = in_array('thirdparty', GETPOST('sendto_socpeople', 'array'));
|
$mailObject->sendto_thirdparty = in_array('thirdparty', GETPOST('sendto_socpeople', 'array'));
|
||||||
|
|
||||||
$mailObject->sendcc_free = GETPOST('sendcc_free', 'none');
|
$mailObject->sendcc_free = GETPOST('sendcc_free', 'alpha');
|
||||||
$mailObject->sendcc_thirdparty = in_array('thirdparty', GETPOST('sendcc_socpeople', 'array'));
|
$mailObject->sendcc_thirdparty = in_array('thirdparty', GETPOST('sendcc_socpeople', 'array'));
|
||||||
|
|
||||||
$mailObject->subject = GETPOST('subject', 'none');
|
$mailObject->subject = GETPOST('subject', 'alpha');
|
||||||
$mailObject->body = GETPOST('body', 'none');
|
$mailObject->body = GETPOST('body', 'restricthtml');
|
||||||
$mailObject->body_ishtml = (int)GETPOST('body_ishtml', 'int');
|
$mailObject->body_ishtml = (int)GETPOST('body_ishtml', 'int');
|
||||||
|
|
||||||
// Save into database
|
// Save into database
|
||||||
|
@ -234,12 +234,6 @@ do {
|
||||||
|
|
||||||
$output .= '<div class="titre inline-block">' . $langs->trans("Options") . "</div>\n";
|
$output .= '<div class="titre inline-block">' . $langs->trans("Options") . "</div>\n";
|
||||||
$output .= '<form id="sribmform" name="sribmform" method="POST" action="#sribmform">';
|
$output .= '<form id="sribmform" name="sribmform" method="POST" action="#sribmform">';
|
||||||
if (function_exists('newToken')) {
|
|
||||||
$output .= '<input type="hidden" name="token" value="'.newToken().'">'; // CSRF protection
|
|
||||||
} else {
|
|
||||||
// Used before Dolibar 13
|
|
||||||
$output .= '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">'; // CSRF protection
|
|
||||||
}
|
|
||||||
$output .= '<table class="liste" summary="mail options"><tbody>';
|
$output .= '<table class="liste" summary="mail options"><tbody>';
|
||||||
$output .= '<tr class="oddeven">';
|
$output .= '<tr class="oddeven">';
|
||||||
$output .= ' <td><label for="active">' . $langs->trans('OptionEnable') . "</label></td>\n";
|
$output .= ' <td><label for="active">' . $langs->trans('OptionEnable') . "</label></td>\n";
|
||||||
|
@ -306,16 +300,21 @@ do {
|
||||||
$output .= '<tr><td class="minwidth200" valign="top">';
|
$output .= '<tr><td class="minwidth200" valign="top">';
|
||||||
$output .= $form->textwithpicto($langs->trans("MailText"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody');
|
$output .= $form->textwithpicto($langs->trans("MailText"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody');
|
||||||
$output .= "</td>\n<td>";
|
$output .= "</td>\n<td>";
|
||||||
/*
|
|
||||||
// doleditor does some weird stuff, adding <br> and newlines, I'll get more into it when I have time.
|
|
||||||
// fallback to simple <textarea> for the time being.
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
|
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
|
||||||
$doleditor = new DolEditor('body_plaintext', (GETPOST('body_plaintext', 'alpha') ? GETPOST('body_plaintext', 'alpha') : $mailObject->body_plaintext), '', 280);
|
$doleditor = new DolEditor(
|
||||||
|
'body',
|
||||||
|
(GETPOST('body', 'alpha') ? GETPOST('body', 'alpha') : $mailObject->body),
|
||||||
|
'',
|
||||||
|
280,
|
||||||
|
'dolibarr_mailings', // toolbar name
|
||||||
|
'In', // toolbar location
|
||||||
|
false, // toolbar start expanded
|
||||||
|
true, // use local browser
|
||||||
|
!empty($conf->global->FCKEDITOR_ENABLE_MAIL) // follow global conf about using ckeditor for mails.
|
||||||
|
);
|
||||||
$output .= $doleditor->Create(1);
|
$output .= $doleditor->Create(1);
|
||||||
*/
|
|
||||||
$output .= '<textarea id="body" name="body" rows="14" cols="80" class="flat">';
|
|
||||||
$output .= htmlentities(GETPOST('body', 'alpha') ? GETPOST('body', 'alpha') : $mailObject->body);
|
|
||||||
$output .= "</textarea>\n";
|
|
||||||
$output .= "</td></tr>\n";
|
$output .= "</td></tr>\n";
|
||||||
|
|
||||||
// body_ishtml
|
// body_ishtml
|
||||||
|
|
Loading…
Reference in a new issue