diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index c563cf14bbc..7bb634cde2a 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -2837,35 +2837,50 @@ function isValidPhone($phone) * @param string $stringencoding Encoding of string * @return int Length of string */ -function dol_strlen($string,$stringencoding='UTF-8') +function dol_strlen($string, $stringencoding='UTF-8') { if (function_exists('mb_strlen')) return mb_strlen($string,$stringencoding); else return strlen($string); } /** - * Make a substring. Works even in mbstring module is not enabled. + * Make a substring. Works even if mbstring module is not enabled for better compatibility. * * @param string $string String to scan * @param string $start Start position - * @param int $length Length + * @param int $length Length (in nb of characters or nb of bytes depending on trunconbytes param) * @param string $stringencoding Page code used for input string encoding + * @param int $trunconbytes 1=Length is max of bytes instead of max of characters * @return string substring */ -function dol_substr($string,$start,$length,$stringencoding='') +function dol_substr($string, $start, $length, $stringencoding='', $trunconbytes=0) { global $langs; if (empty($stringencoding)) $stringencoding=$langs->charset_output; $ret=''; - if (function_exists('mb_substr')) + if (empty($trunconbytes)) { - $ret=mb_substr($string,$start,$length,$stringencoding); + if (function_exists('mb_substr')) + { + $ret=mb_substr($string, $start, $length, $stringencoding); + } + else + { + $ret=substr($string, $start, $length); + } } else { - $ret=substr($string,$start,$length); + if (function_exists('mb_strcut')) + { + $ret=mb_strcut($string, $start, $length, $stringencoding); + } + else + { + $ret=substr($string, $start, $length); + } } return $ret; } @@ -3063,7 +3078,7 @@ function dol_print_graph($htmlid,$width,$height,$data,$showlegend=0,$type='pie', * @param string $trunc Where to trunc: right, left, middle (size must be a 2 power), wrap * @param string $stringencoding Tell what is source string encoding * @param int $nodot Truncation do not add ... after truncation. So it's an exact truncation. - * @param int $display Trunc is use to display and can be changed for small screen. TODO Remove this param (must be dealt with CSS) + * @param int $display Trunc is used to display data and can be changed for small screen. TODO Remove this param (must be dealt with CSS) * @return string Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise. */ function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodot=0, $display=0) @@ -6782,26 +6797,27 @@ function dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id if ($key == '') return ''; // Check in cache - if (isset($cache_codes[$tablename][$key])) // Can be defined to 0 or '' + if (isset($cache_codes[$tablename][$key][$fieldid])) // Can be defined to 0 or '' { - return $cache_codes[$tablename][$key]; // Found in cache + return $cache_codes[$tablename][$key][$fieldid]; // Found in cache } + dol_syslog('dol_getIdFromCode (value not found into cache)', LOG_DEBUG); + $sql = "SELECT ".$fieldid." as valuetoget"; $sql.= " FROM ".MAIN_DB_PREFIX.$tablename; $sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'"; if (! empty($entityfilter)) $sql.= " AND entity IN (" . getEntity($tablename) . ")"; - dol_syslog('dol_getIdFromCode', LOG_DEBUG); $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); - if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget; - else $cache_codes[$tablename][$key]=''; + if ($obj) $cache_codes[$tablename][$key][$fieldid]=$obj->valuetoget; + else $cache_codes[$tablename][$key][$fieldid]=''; $db->free($resql); - return $cache_codes[$tablename][$key]; + return $cache_codes[$tablename][$key][$fieldid]; } else { @@ -6889,8 +6905,6 @@ function picto_from_langcode($codelang, $moreatt = '') if (empty($codelang)) return ''; - if (empty($codelang)) return ''; - if ($codelang == 'auto') { return ''; diff --git a/htdocs/core/modules/project/mod_project_simple.php b/htdocs/core/modules/project/mod_project_simple.php index 46afc144da3..e5f5c268f06 100644 --- a/htdocs/core/modules/project/mod_project_simple.php +++ b/htdocs/core/modules/project/mod_project_simple.php @@ -122,7 +122,7 @@ class mod_project_simple extends ModeleNumRefProjects * @param Project $project Object project * @return string Value if OK, 0 if KO */ - function getNextValue($objsoc,$project) + function getNextValue($objsoc, $project) { global $db,$conf; @@ -167,9 +167,9 @@ class mod_project_simple extends ModeleNumRefProjects * @param Project $project Object project * @return string Next not used reference */ - function project_get_num($objsoc=0,$project='') + function project_get_num($objsoc=0, $project='') { // phpcs:enable - return $this->getNextValue($objsoc,$project); + return $this->getNextValue($objsoc, $project); } } diff --git a/htdocs/core/modules/project/mod_project_universal.php b/htdocs/core/modules/project/mod_project_universal.php index d1dd95b22bc..e052dbcc7bd 100644 --- a/htdocs/core/modules/project/mod_project_universal.php +++ b/htdocs/core/modules/project/mod_project_universal.php @@ -123,7 +123,7 @@ class mod_project_universal extends ModeleNumRefProjects * @param Project $project Object project * @return string Value if OK, 0 if KO */ - function getNextValue($objsoc,$project) + function getNextValue($objsoc, $project) { global $db,$conf; @@ -139,7 +139,7 @@ class mod_project_universal extends ModeleNumRefProjects } $date=empty($project->date_c)?dol_now():$project->date_c; - $numFinal=get_next_value($db,$mask,'projet','ref','',$objsoc->code_client,$date); + $numFinal=get_next_value($db, $mask, 'projet', 'ref', '', (is_object($objsoc) ? $objsoc->code_client : ''), $date); return $numFinal; } @@ -153,9 +153,9 @@ class mod_project_universal extends ModeleNumRefProjects * @param Project $project Object project * @return string Next not used reference */ - function project_get_num($objsoc=0,$project='') + function project_get_num($objsoc=0, $project='') { // phpcs:enable - return $this->getNextValue($objsoc,$project); + return $this->getNextValue($objsoc, $project); } } diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index a9e2431be71..880ccda25cf 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -710,12 +710,12 @@ class EmailCollector extends CommonObject dol_syslog("EmailCollector::doCollectOneCollector start", LOG_DEBUG); + $langs->loadLangs(array("project", "companies", "errors")); + $error = 0; $this->output = ''; $this->error=''; - dol_syslog(__METHOD__, LOG_DEBUG); - $now = dol_now(); if (empty($this->host)) @@ -781,7 +781,9 @@ class EmailCollector extends CommonObject dol_syslog("search string = ".$search); //var_dump($search); - $nbemailprocessed=0; $nbactiondone=0; + $nbemailprocessed=0; + $nbemailok=0; + $nbactiondone=0; $projectstatic=new Project($this->db); $thirdpartystatic=new Societe($this->db); $contactstatic=new Contact($this->db); @@ -797,7 +799,12 @@ class EmailCollector extends CommonObject { if ($nbemailprocessed > 100) break; // Do not process more than 100 email per launch + $nbactiondoneforemail = 0; + $errorforemail = 0; $errorforactions = 0; + $thirdpartyfoundby = ''; + $contactfoundby = ''; + $projectfoundby = ''; $this->db->begin(); @@ -830,8 +837,9 @@ class EmailCollector extends CommonObject $sendtocc=$overview[0]->cc; $sendtobcc=$overview[0]->bcc; $date=$overview[0]->udate; - $msgid=$overview[0]->message_id; + $msgid=str_replace(array('<','>'), '', $overview[0]->message_id); $subject=$overview[0]->subject; + //var_dump($msgid);exit; $reg=array(); if (preg_match('/^(.*)<(.*)>$/', $fromstring, $reg)) @@ -848,9 +856,12 @@ class EmailCollector extends CommonObject $contactid = 0; $thirdpartyid = 0; $projectid = 0; // Analyze TrackId + $trackid = ''; $reg=array(); if (! empty($headers['X-Dolibarr-TrackId']) && preg_match('/:\s*([a-z]+)([0-9]+)$/', $headers['X-Dolibarr-TrackId'], $reg)) { + $trackid = $reg[0].$reg[1]; + $objectid = 0; $objectemail = null; if ($reg[0] == 'inv') @@ -888,6 +899,7 @@ class EmailCollector extends CommonObject if ($fk_element_type == 'facture') $fk_element_type = 'invoice'; $thirdpartyid = $objectemail->fk_soc; + $contactid = $objectemail->fk_socpeople; $projectid = isset($objectemail->fk_project)?$objectemail->fk_project:$objectemail->fk_projet; } } @@ -898,6 +910,7 @@ class EmailCollector extends CommonObject { $result = $projectstatic->fetch($projectid); if ($result <= 0) $projectstatic->id = 0; + else $projectfoundby = 'Trackid ('.$trackid.')'; } // Contact $contactstatic->id=0; @@ -905,10 +918,12 @@ class EmailCollector extends CommonObject { $result = $contactstatic->fetch($contactid); if ($result <= 0) $contactstatic->id = 0; + else $contactfoundby = 'Trackid ('.$trackid.')'; } else // Try to find contact using email { - $contactstatic->fetch(0, null, '', $from); + $result = $contactstatic->fetch(0, null, '', $from); + if ($result > 0) $contactfoundby = 'email ('.$from.')'; } // Thirdparty $thirdpartystatic->id=0; @@ -916,15 +931,17 @@ class EmailCollector extends CommonObject { $result = $thirdpartystatic->fetch($thirdpartyid); if ($result <= 0) $thirdpartystatic->id = 0; + else $thirdpartyfoundby = 'Trackid ('.$trackid.')'; } else // Try to find thirdparty using email { - $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from); + $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from); + if ($result > 0) $thirdpartyfoundby = 'email ('.$from.')'; } require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; - // Do operationss + // Do operations foreach($this->actions as $operation) { if ($errorforactions) break; @@ -942,7 +959,7 @@ class EmailCollector extends CommonObject $actioncomm->code = 'AC_'.$actioncode; $actioncomm->label = $langs->trans("EmailReceived").' - '.$langs->trans("From").' '.$from; $actioncomm->note = $messagetext; - $actioncomm->fk_project = 0; + $actioncomm->fk_project = $projectstatic->id; $actioncomm->datep = $date; $actioncomm->datef = $date; $actioncomm->percentage = -1; // Not applicable @@ -981,42 +998,143 @@ class EmailCollector extends CommonObject $this->errors = $actioncomm->errors; } } - elseif ($operation['type'] == 'aaa') + elseif ($operation['type'] == 'project') { + // @TODO Check project not alreayd created using ref_ext=msg_id + + $note_private = $langs->trans("ProjectCreatedByEmailCollector", $msgid); + $projecttocreate = new Project($this->db); + if ($thirdpartystatic->id > 0) + { + $projecttocreate->fk_soc = $thirdpartystatic->id; + if ($thirdpartyfoundby) $note_private .= ' - Third party found from '.$thirdpartyfoundby; + } + if ($contactstatic->id > 0) + { + $projecttocreate->contact_id = $contactstatic->id; + if ($contactfoundby) $note_private .= ' - Contact/address found from '.$contactfoundby; + } + + $id_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'rowid'); + $percent_opp_status = dol_getIdFromCode($this->db, 'PROSP', 'c_lead_status', 'code', 'percent'); + + $projecttocreate->title = $subject; + $projecttocreate->date_start = $now; + $projecttocreate->opp_status = $id_opp_status; + $projecttocreate->opp_percent = $percent_opp_status; + $projecttocreate->description = ($note_private?$note_private."\n":'').$messagetext; + $projecttocreate->note_private = $note_private; + + // Overwrite values with values extracted from source email + $arrayvaluetouse = array(); + foreach($arrayvaluetouse as $propertytooverwrite => $valueforproperty) + { + // Example: $propertytooverwrite = 'project.opportunity_status', $valueforproperty = '123' or 'REGEX:BODY:...(.*)...' + } + + // Get next project Ref + $defaultref=''; + $modele = empty($conf->global->PROJECT_ADDON)?'mod_project_simple':$conf->global->PROJECT_ADDON; + + // Search template files + $file=''; $classname=''; $filefound=0; + $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); + foreach($dirmodels as $reldir) + { + $file=dol_buildpath($reldir."core/modules/project/".$modele.'.php',0); + if (file_exists($file)) + { + $filefound=1; + $classname = $modele; + break; + } + } + + if ($filefound) + { + $result=dol_include_once($reldir."core/modules/project/".$modele.'.php'); + $modProject = new $classname; + + $defaultref = $modProject->getNextValue(($thirdpartystatic->id > 0 ? $thirdpartystatic : null), $projecttocreate); + } + + if (is_numeric($defaultref) && $defaultref <= 0) + { + $errorforactions++; + $this->error = 'Failed to create project: Can\'t get a free project Ref'; + } + else + { + $projecttocreate->ref = $defaultref; + + // Create project + $result = $projecttocreate->create($user); + if ($result <= 0) + { + $errorforactions++; + $this->error = 'Failed to create project: '.$langs->trans($projecttocreate->error); + $this->errors = $projecttocreate->errors; + } + } } if (! $errorforactions) { - $nbactiondone++; + $nbactiondoneforemail++; } } - // Move email - if (! $errorforactions && $targetdir) + // Error for email or not ? + if (! $errorforactions) { - dol_syslog("EmailCollector::doCollectOneCollector move message ".$imapemail." to ".$connectstringtarget, LOG_DEBUG); - $res = imap_mail_move($connection, $imapemail, $targetdir, 0); - if ($res == false) { - $error++; - $this->error = imap_last_error(); - dol_syslog(imap_last_error()); + if ($targetdir) + { + dol_syslog("EmailCollector::doCollectOneCollector move message ".$imapemail." to ".$connectstringtarget, LOG_DEBUG); + $res = imap_mail_move($connection, $imapemail, $targetdir, 0); + if ($res == false) { + $errorforemail++; + $this->error = imap_last_error(); + $this->errors[] = $this->error; + dol_syslog(imap_last_error()); + } } + else + { + dol_syslog("EmailCollector::doCollectOneCollector message ".$imapemail." to ".$connectstringtarget." was set to read", LOG_DEBUG); + + } + } + else + { + $errorforemail++; + } + + if (! $errorforemail) + { + $nbactiondone += $nbactiondoneforemail; + $nbemailok++; + + $this->db->commit(); + } + else + { + $error++; + + $this->db->rollback(); } $nbemailprocessed++; unset($objectemail); - - $this->db->commit(); } - $this->output=$langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbactiondone); + $output=$langs->trans('XEmailsDoneYActionsDone', $nbemailprocessed, $nbemailok, $nbactiondone); } else { - $this->output=$langs->trans('NoNewEmailToProcess'); + $output=$langs->trans('NoNewEmailToProcess'); } imap_expunge($connection); // To validate any move @@ -1024,7 +1142,8 @@ class EmailCollector extends CommonObject imap_close($connection); $this->datelastresult = $now; - $this->lastresult = $this->output; + $this->lastresult = $output; + if (! empty($this->errors)) $this->lastresult.= " - ".join(" - ", $this->errors); $this->codelastresult = ($error ? 'KO' : 'OK'); $this->update($user); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 2741b48ac83..08508d4e6e8 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1833,7 +1833,7 @@ EmailCollectorConfirmCollectTitle=Email collect confirmation EmailCollectorConfirmCollect=Do you want to run the collect for this collector now ? NoNewEmailToProcess=No new email (matching filters) to process NothingProcessed=Nothing done -XEmailsDoneYActionsDone=%s emails analyzed, %s record/actions done by collector +XEmailsDoneYActionsDone=%s emails analyzed, %s emails successfuly processed (for %s record/actions done) by collector RecordEvent=Record event CreateLeadAndThirdParty=Create lead (and thirdparty if necessary) CodeLastResult=Result code of last collect diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 91442bbd05d..e8cd9b78d2f 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -244,6 +244,9 @@ YourPasswordHasBeenReset=Your password has been reset successfully ApplicantIpAddress=IP address of applicant SMSSentTo=SMS sent to %s MissingIds=Missing ids +ThirdPartyCreatedByEmailCollector=Third party created by email collector from email ID %s +ContactCreatedByEmailCollector=Contact/address created by email collector from email ID %s +ProjectCreatedByEmailCollector=Project created by email collector from email ID %s ##### Export ##### ExportsArea=Exports area diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 76c0e430276..6cb54cfb572 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -179,6 +179,10 @@ class Project extends CommonObject $now=dol_now(); + // Clean parameters + $this->note_private = dol_substr($this->note_private, 0, 65535); + $this->note_public = dol_substr($this->note_public, 0, 65535); + // Check parameters if (!trim($this->ref)) { @@ -193,6 +197,7 @@ class Project extends CommonObject return -1; } + // Create project $this->db->begin(); $sql = "INSERT INTO " . MAIN_DB_PREFIX . "projet ("; @@ -211,6 +216,8 @@ class Project extends CommonObject $sql.= ", opp_amount"; $sql.= ", budget_amount"; $sql.= ", bill_time"; + $sql.= ", note_private"; + $sql.= ", note_public"; $sql.= ", entity"; $sql.= ") VALUES ("; $sql.= "'" . $this->db->escape($this->ref) . "'"; @@ -228,6 +235,8 @@ class Project extends CommonObject $sql.= ", " . (strcmp($this->opp_amount,'') ? price2num($this->opp_amount) : 'null'); $sql.= ", " . (strcmp($this->budget_amount,'') ? price2num($this->budget_amount) : 'null'); $sql.= ", " . ($this->bill_time ? 1 : 0); + $sql.= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : 'null'); + $sql.= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : 'null'); $sql.= ", ".$conf->entity; $sql.= ")";