dolibarr 25.0.0-alpha
modules_mailings.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2008 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
5 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * or see https://www.gnu.org/
21 */
22
28require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php';
29
30
34class MailingTargets // This can't be abstract as it is used for some method
35{
39 public $db;
40
44 public $error = '';
45
49 public $errors;
50
54 public $enabled;
55
59 public $name;
60
64 public $desc;
65
69 public $tooltip = '';
70
74 public $sql;
75
76
80 public $evenunsubscribe = 0;
81
82
88 public function __construct($db)
89 {
90 $this->db = $db;
91 }
92
98 public function getDesc()
99 {
100 global $langs, $form;
101
102 $langs->load("mails");
103 $transstring = "MailingModuleDesc".$this->name;
104 $s = '';
105
106 if ($langs->trans($this->name) != $this->name) {
107 $s = $langs->trans($this->name);
108 } elseif ($langs->trans($transstring) != $transstring) {
109 $s = $langs->trans($transstring);
110 } else {
111 $s = $this->desc;
112 }
113
114 if ($this->tooltip && is_object($form)) {
115 $s .= ' '.$form->textwithpicto('', $langs->trans($this->tooltip), 1, 'help');
116 }
117 return $s;
118 }
119
125 public function getNbOfRecords()
126 {
127 return 0;
128 }
129
136 public function getNbOfRecipients($sql)
137 {
138 $result = $this->db->query($sql);
139 if ($result) {
140 $total = 0;
141 while ($obj = $this->db->fetch_object($result)) {
142 $total += (int) $obj->nb;
143 }
144 return $total;
145 } else {
146 $this->error = $this->db->lasterror();
147 return -1;
148 }
149 }
150
156 public function formFilter()
157 {
158 return '';
159 }
160
161 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
168 public function update_nb($mailing_id)
169 {
170 // phpcs:enable
171 // Update the number of recipients in the mailing table
172 $sql = "SELECT COUNT(*) nb FROM ".$this->db->prefix()."mailing_cibles";
173 $sql .= " WHERE fk_mailing = ".((int) $mailing_id);
174 $result = $this->db->query($sql);
175 if ($result) {
176 $obj = $this->db->fetch_object($result);
177 $nb = (int) $obj->nb;
178
179 $sql = "UPDATE ".$this->db->prefix()."mailing";
180 $sql .= " SET nbemail = ".((int) $nb)." WHERE rowid = ".((int) $mailing_id);
181 if (!$this->db->query($sql)) {
182 dol_syslog($this->db->error());
183 $this->error = $this->db->error();
184 return -1;
185 }
186 } else {
187 return -1;
188 }
189 return $nb;
190 }
191
199 public function addTargetsToDatabase($mailing_id, $cibles)
200 {
201 global $conf;
202
203 $this->db->begin();
204
205
206 // Insert emailing targets from array into database
207 $j = 0;
208 $num = count($cibles);
209 foreach ($cibles as $targetarray) {
210 if (!empty($targetarray['email'])) { // avoid empty email address
211 $sql = "INSERT INTO ".$this->db->prefix()."mailing_cibles";
212 $sql .= " (fk_mailing,";
213 $sql .= " fk_contact,";
214 $sql .= " lastname, firstname, email, other, source_url, source_id,";
215 $sql .= " tag,";
216 $sql .= " source_type)";
217 $sql .= " VALUES (".((int) $mailing_id).",";
218 $sql .= (empty($targetarray['fk_contact']) ? '0' : (int) $targetarray['fk_contact']).",";
219 $sql .= "'".$this->db->escape($targetarray['lastname'])."',";
220 $sql .= "'".$this->db->escape($targetarray['firstname'])."',";
221 $sql .= "'".$this->db->escape($targetarray['email'])."',";
222 $sql .= "'".$this->db->escape($targetarray['other'])."',";
223 $sql .= "'".$this->db->escape($targetarray['source_url'])."',";
224 $sql .= (empty($targetarray['source_id']) ? 'null' : (int) $targetarray['source_id']).",";
225 $sql .= "'".$this->db->escape(dol_hash($conf->file->instance_unique_id.";".$targetarray['email'].";".$targetarray['lastname'].";".((int) $mailing_id).";".getDolGlobalString('MAILING_EMAIL_UNSUBSCRIBE_KEY'), 'md5'))."',";
226 $sql .= "'".$this->db->escape($targetarray['source_type'])."')";
227 dol_syslog(__METHOD__, LOG_DEBUG);
228 $result = $this->db->query($sql);
229 if ($result) {
230 $j++;
231 } else {
232 if ($this->db->errno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
233 // Si erreur autre que doublon
234 dol_syslog($this->db->error().' : '.$targetarray['email']);
235 $this->error = $this->db->error().' : '.$targetarray['email'];
236 $this->db->rollback();
237 return -1;
238 }
239 }
240 }
241 }
242
243 dol_syslog(__METHOD__.": mailing ".$j." targets added");
244
245 /*
246 //Update the status to show third-party emails that no longer wish to be contacted'
247 $sql = "UPDATE ".$this->db->prefix()."mailing_cibles";
248 $sql .= " SET statut=3";
249 $sql .= " WHERE fk_mailing = ".((int) $mailing_id)." AND email in (SELECT email FROM ".$this->db->prefix()."societe where fk_stcomm=-1)";
250 $sql .= " AND source_type='thirdparty'";
251 dol_syslog(__METHOD__.": mailing update status to display third-party emails that no longer wish to be contacted");
252 $result=$this->db->query($sql);
253
254 //Update the status to show contact emails that no longer wish to be contacted'
255 $sql = "UPDATE ".$this->db->prefix()."mailing_cibles";
256 $sql .= " SET statut=3";
257 $sql .= " WHERE fk_mailing = ".((int) $mailing_id)." AND source_type='contact' AND (email in (SELECT sc.email FROM ".$this->db->prefix()."socpeople AS sc ";
258 $sql .= " INNER JOIN ".$this->db->prefix()."societe s ON s.rowid=sc.fk_soc WHERE s.fk_stcomm=-1 OR no_email=1))";
259 dol_syslog(__METHOD__.": mailing update status to display contact emails that no longer wish to be contacted",LOG_DEBUG);
260 $result=$this->db->query($sql);
261 */
262
263 if (empty($this->evenunsubscribe)) {
264 $sql = "UPDATE ".$this->db->prefix()."mailing_cibles as mc";
265 $sql .= " SET statut = 3";
266 $sql .= " WHERE fk_mailing = ".((int) $mailing_id);
267 $sql .= " AND EXISTS (SELECT rowid FROM ".$this->db->prefix()."mailing_unsubscribe as mu WHERE mu.email = mc.email and mu.entity = ".((int) $conf->entity).")";
268
269 dol_syslog(__METHOD__.":mailing update status to display emails that do not want to be contacted anymore", LOG_DEBUG);
270 $result = $this->db->query($sql);
271 if (!$result) {
272 dol_print_error($this->db);
273 }
274 }
275
276 // Update nb of recipient into emailing record
277 $this->update_nb($mailing_id);
278
279 $this->db->commit();
280
281 return $j;
282 }
283
284 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
291 public function clear_target($mailing_id)
292 {
293 // phpcs:enable
294 $sql = "DELETE FROM ".$this->db->prefix()."mailing_cibles";
295 $sql .= " WHERE fk_mailing = ".((int) $mailing_id);
296
297 if (!$this->db->query($sql)) {
298 dol_syslog($this->db->error());
299 }
300
301 $this->update_nb($mailing_id);
302 }
303
304
312 public static function getEmailingSelectorsList($forcedir = null)
313 {
314 global $langs, $db;
315
316 $files = array();
317 $fullpath = array();
318 $relpath = array();
319 $iscoreorexternal = array();
320 $modules = array();
321 $orders = array();
322 $i = 0;
323
324 $diremailselector = array('/core/modules/mailings/'); // $conf->modules_parts['emailings'] is not required
325 if (is_array($forcedir)) {
326 $diremailselector = $forcedir;
327 }
328
329 foreach ($diremailselector as $reldir) {
330 $dir = dol_buildpath($reldir, 0);
331 $newdir = dol_osencode($dir);
332
333 // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
334 if (!is_dir($newdir)) {
335 continue;
336 }
337
338 $handle = opendir($newdir);
339 if (is_resource($handle)) {
340 while (($file = readdir($handle)) !== false) {
341 $reg = array();
342 if (is_readable($newdir.'/'.$file) && preg_match('/^(.+)\.modules.php/', $file, $reg)) {
343 if (preg_match('/\.back$/', $file) || preg_match('/^(.+)\.disabled\.php/', $file)) {
344 continue;
345 }
346
347 $part1 = $reg[1];
348
349 //$modName = ucfirst($reg[1]);
350 $modName = 'mailing_'.$reg[1]; // name of selector submodule
351 //print "file=$file modName=$modName"; exit;
352 if (in_array($modName, $modules)) {
353 $langs->load("errors");
354 print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateEmalingSelector", $modName, "").'</div>';
355 } else {
356 try {
357 //print $newdir.'/'.$file;
358 include_once $newdir.'/'.$file;
359 } catch (Exception $e) {
360 print $e->getMessage();
361 }
362 }
363
364 $files[$i] = $file;
365 $fullpath[$i] = $dir.'/'.$file;
366 $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
367 $iscoreorexternal[$i] = ($reldir == '/core/modules/mailings/' ? 'internal' : 'external');
368 $modules[$i] = $modName;
369 $orders[$i] = $part1; // Set sort criteria value
370
371 $i++;
372 }
373 }
374 closedir($handle);
375 }
376 }
377 //echo "<pre>";print_r($modules);echo "</pre>";
378
379 asort($orders);
380
381 $widget = array();
382 $j = 0;
383
384 // Loop on each emailing selector
385 foreach ($orders as $key => $value) {
386 $modName = $modules[$key];
387 if (empty($modName)) {
388 continue;
389 }
390
391 if (!class_exists($modName)) {
392 print 'Error: An emailing selector file was found but its class "'.$modName.'" was not found.'."<br>\n";
393 continue;
394 }
395
396 $objMod = new $modName($db);
397 if (is_object($objMod)) {
398 '@phan-var-force ModeleBoxes $objMod';
399 // Define disabledbyname and disabledbymodule
400 $disabledbyname = 0;
401 $disabledbymodule = 0; // TODO Set to 2 if module is not enabled
402 $module = '';
403
404 // Check if widget file is disabled by name
405 if (preg_match('/NORUN$/i', $files[$key])) {
406 $disabledbyname = 1;
407 }
408
409 // We set info of modules @phan-suppress-next-line PhanUndeclaredProperty
410 $widget[$j]['picto'] = (empty($objMod->picto) ? (empty($objMod->boximg) ? img_object('', 'generic') : $objMod->boximg) : img_object('', $objMod->picto));
411 $widget[$j]['file'] = $files[$key];
412 $widget[$j]['fullpath'] = $fullpath[$key];
413 $widget[$j]['relpath'] = $relpath[$key];
414 $widget[$j]['iscoreorexternal'] = $iscoreorexternal[$key];
415 $widget[$j]['version'] = empty($objMod->version) ? '' : $objMod->version;
416 $widget[$j]['status'] = img_picto($langs->trans("Active"), 'tick', 'class="pictofixedwidth"');
417 if ($disabledbyname > 0 || $disabledbymodule > 1) {
418 $widget[$j]['status'] = '';
419 }
420
421 $text = '<b>'.$langs->trans("Description").':</b><br>';
422 $text .= $objMod->boxlabel.'<br>';
423 $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
424 if ($disabledbymodule == 2) {
425 $text .= $langs->trans("WidgetDisabledAsModuleDisabled", $module).'<br>';
426 }
427
428 $widget[$j]['info'] = $text;
429 }
430 $j++;
431 }
432
433 return $widget;
434 }
435
436
445 public function getSqlArrayForStats()
446 {
447 // Needs to be implemented in child class
448 $msg = get_class($this)."::".__FUNCTION__." not implemented";
449 dol_syslog($msg, LOG_ERR);
450 return array();
451 }
452
453 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
460 public function add_to_target($mailing_id)
461 {
462 // phpcs:enable
463 // Needs to be implemented in child class
464 $msg = get_class($this)."::".__FUNCTION__." not implemented";
465 dol_syslog($msg, LOG_ERR);
466 return -1;
467 }
468}
Parent class of emailing target selectors modules.
__construct($db)
Constructor.
add_to_target($mailing_id)
Add destinations in the targets table.
static getEmailingSelectorsList($forcedir=null)
Return list of widget.
addTargetsToDatabase($mailing_id, $cibles)
Add a list of targets into the database.
getDesc()
Return description of email selector.
getNbOfRecipients($sql)
Return the number of recipients.
clear_target($mailing_id)
Deletes all recipients from the targets table.
update_nb($mailing_id)
Update the number of recipients.
formFilter()
Displays filter form that appears on the mailing recipient selection page.
getNbOfRecords()
Return number of records for email selector.
getSqlArrayForStats()
On the main mailing area, there is a box with statistics.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.