diff --git a/htdocs/core/modules/modPartnership.class.php b/htdocs/core/modules/modPartnership.class.php index ae5886e589c..0d5d6dce7ca 100644 --- a/htdocs/core/modules/modPartnership.class.php +++ b/htdocs/core/modules/modPartnership.class.php @@ -177,7 +177,7 @@ class modPartnership extends DolibarrModules // Array to add new pages in new tabs $this->tabs = array(); - $tabtoadd = (!empty(getDolGlobalString('PARTNERSHIP_IS_MANAGED_FOR')) && getDolGlobalString('PARTNERSHIP_IS_MANAGED_FOR') == 'member') ? 'member' : 'thirdparty'; + $tabtoadd = ($conf->global->PARTNERSHIP_IS_MANAGED_FOR == 'member') ? 'member' : 'thirdparty'; if ($tabtoadd == 'member') { $this->tabs[] = array('data'=>'member:+partnership:Partnership:partnership@partnership:$user->rights->partnership->read:/adherents/partnership.php?socid=__ID__'); @@ -253,12 +253,12 @@ class modPartnership extends DolibarrModules // Cronjobs (List of cron jobs entries to add when module is enabled) // unit_frequency must be 60 for minute, 3600 for hour, 86400 for day, 604800 for week - $statusatinstall=0; $arraydate=dol_getdate(dol_now()); $datestart=dol_mktime(21, 15, 0, $arraydate['mon'], $arraydate['mday'], $arraydate['year']); $this->cronjobs = array( - 0 => array('priority'=>60, 'label'=>'CancelPartnershipForExpiredMembers', 'jobtype'=>'method', 'class'=>'/partnership/class/partnershiputils.class.php', 'objectname'=>'PartnershipUtils', 'method'=>'doCancelStatusOfPartnership', 'parameters'=>'', 'comment'=>'Cancel status of partnership when subscription is expired + x days.', 'frequency'=>1, 'unitfrequency'=>86400, 'status'=>$statusatinstall, 'test'=>'$conf->partnership->enabled', 'datestart'=>$datestart), + 0 => array('priority'=>60, 'label'=>'CancelPartnershipForExpiredMembers', 'jobtype'=>'method', 'class'=>'/partnership/class/partnershiputils.class.php', 'objectname'=>'PartnershipUtils', 'method'=>'doCancelStatusOfMemberPartnership', 'parameters'=>'', 'comment'=>'Cancel status of partnership when subscription is expired + x days.', 'frequency'=>1, 'unitfrequency'=>86400, 'status'=>1, 'test'=>'$conf->partnership->enabled', 'datestart'=>$datestart), + 1 => array('priority'=>61, 'label'=>'WarningOfPartnershipIfDolibarrBacklinkNotfound', 'jobtype'=>'method', 'class'=>'/partnership/class/partnershiputils.class.php', 'objectname'=>'PartnershipUtils', 'method'=>'doWarningOfPartnershipIfDolibarrBacklinkNotfound', 'parameters'=>'', 'comment'=>'Warning of partnership if Dolibarr backlink not found on partner website.', 'frequency'=>1, 'unitfrequency'=>86400, 'status'=>0, 'test'=>'$conf->partnership->enabled', 'datestart'=>$datestart), ); // Permissions provided by this module diff --git a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql index c1e75bbe353..d0375349880 100644 --- a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql +++ b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql @@ -398,8 +398,9 @@ CREATE TABLE llx_partnership( note_private text, note_public text, last_main_doc varchar(255), - count_last_url_check_error integer DEFAULT '0', - import_key varchar(14), + count_last_url_check_error integer DEFAULT '0', + last_check_backlink datetime NULL, + import_key varchar(14), model_pdf varchar(255) ) ENGINE=innodb; @@ -420,10 +421,10 @@ create table llx_partnership_extrafields ALTER TABLE llx_partnership_extrafields ADD INDEX idx_partnership_fk_object(fk_object); -INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipWillExpire)', NULL, 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipWillSoonExpireTopic)__', 0, '\n

Hello,

\n__(YourPartnershipWillSoonExpireContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); -INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipCanceled)', NULL, 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipCanceledTopic)__', 0, '\n

Hello,

\n__(YourPartnershipCanceledContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); -INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipRefused)', NULL, 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipRefusedTopic)__', 0, '\n

Hello,

\n__(YourPartnershipRefusedContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); -INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipAccepted)', NULL, 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipAcceptedTopic)__', 0, '\n

Hello,

\n__(YourPartnershipAcceptedContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); +INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipWillExpire)', '', 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipWillSoonExpireTopic)__', 0, '\n

Hello,

\n__(YourPartnershipWillSoonExpireContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); +INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipCanceled)', '', 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipCanceledTopic)__', 0, '\n

Hello,

\n__(YourPartnershipCanceledContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); +INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipRefused)', '', 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipRefusedTopic)__', 0, '\n

Hello,

\n__(YourPartnershipRefusedContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); +INSERT INTO llx_c_email_templates (entity,module,type_template,label,lang,position,topic,joinfiles,content) VALUES (0, 'partnership', 'partnership_send', '(SendingEmailOnPartnershipAccepted)', '', 100, '[__[MAIN_INFO_SOCIETE_NOM]__] - __(YourPartnershipAcceptedTopic)__', 0, '\n

Hello,

\n__(YourPartnershipAcceptedContent)__

\n
\n\n
\n\n __(Sincerely)__
\n __[MAIN_INFO_SOCIETE_NOM]__
\n \n'); ALTER TABLE llx_adherent ADD COLUMN url varchar(255) NULL AFTER email; ALTER TABLE llx_facture_fourn ADD COLUMN date_closing datetime DEFAULT NULL after date_valid; diff --git a/htdocs/langs/en_US/partnership.lang b/htdocs/langs/en_US/partnership.lang index 4d5b518431f..2da278dd36c 100644 --- a/htdocs/langs/en_US/partnership.lang +++ b/htdocs/langs/en_US/partnership.lang @@ -32,7 +32,10 @@ ListOfPartnerships=List of partnership PartnershipSetup=Partnership setup PartnershipAbout=About Partnership PartnershipAboutPage=Partnership about page -PartnershipManagedFor=Partnership managed for +partnershipforthirdpartyormember=Partnership is for a 'thirdparty' or for a 'member' +PARTNERSHIP_IS_MANAGED_FOR=Partnership managed for +PARTNERSHIP_BACKLINKS_TO_CHECK=Backlinks to check +PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL=Nb of days before cancel status of partnership # # Object @@ -44,6 +47,7 @@ ReasonDecline=Decline reason ReasonDeclineOrCancel=Decline reason PartnershipAlreadyExist=Partnership already exist ShowPartnership=Show partnership +BacklinkNotFoundOnPartnerWebsite=Backlink not found on partner website ConfirmClosePartnershipAsk=Are you sure you want to cancel this partnership? # diff --git a/htdocs/partnership/admin/setup.php b/htdocs/partnership/admin/setup.php index 77edba1f21f..4cd5fe5f387 100644 --- a/htdocs/partnership/admin/setup.php +++ b/htdocs/partnership/admin/setup.php @@ -82,9 +82,7 @@ include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; if ($action == 'setting') { require_once DOL_DOCUMENT_ROOT."/core/modules/modPartnership.class.php"; - $value = GETPOST('managed_for', 'alpha'); - - $modulemenu = ($value == 'member') ? 'member' : 'thirdparty'; + $modulemenu = (GETPOST('PARTNERSHIP_IS_MANAGED_FOR', 'alpha') == 'member') ? 'member' : 'thirdparty'; $res = dolibarr_set_const($db, "PARTNERSHIP_IS_MANAGED_FOR", $modulemenu, 'chaine', 0, '', $conf->entity); $partnership = new modPartnership($db); @@ -94,6 +92,10 @@ if ($action == 'setting') { $error += $partnership->delete_menus(); $error += $partnership->insert_menus(); + + if(GETPOST("PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL",'int')) + dolibarr_set_const($db, "PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL", GETPOST("PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL",'int'), 'chaine', 0, '', $conf->entity); + dolibarr_set_const($db, "PARTNERSHIP_BACKLINKS_TO_CHECK", GETPOST("PARTNERSHIP_BACKLINKS_TO_CHECK"), 'chaine', 0, '', $conf->entity); } if ($action) { @@ -102,6 +104,8 @@ if ($action) { } else { setEventMessages($langs->trans("SetupNotError"), null, 'errors'); } + header("Location: ".$_SERVER['PHP_SELF']); + exit; } /* @@ -137,19 +141,48 @@ print ''; print ''; -// Default partnership price base type -print ''; -print ''; -print ''; + +print ''; +print ''; +print ''; +print ''; print ''; -print '
'.$langs->trans("PartnershipManagedFor").''; - print ''; -print '
'.$langs->trans("Setting").''.$langs->trans("Value").''.$langs->trans("Examples").'
'; +print ''.$langs->trans("PARTNERSHIP_IS_MANAGED_FOR").''; +print ''; +print ''; +print ''; +print ''.$langs->trans("partnershipforthirdpartyormember").''; +print ''; + + +if($conf->global->PARTNERSHIP_IS_MANAGED_FOR == 'member'){ + print ''.$langs->trans("PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL").''; + print ''; + $dnbdays = '7'; + $backlinks = (!empty($conf->global->PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL)) ? $conf->global->PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL : $dnbdays; + print ''; + print ''; + print ''.$dnbdays.''; + print ''; +} + + +print ''.$langs->trans("PARTNERSHIP_BACKLINKS_TO_CHECK").''; +print ''; +$dbacklinks = 'dolibarr.org|dolibarr.fr|dolibarr.es'; +$backlinks = (!empty($conf->global->PARTNERSHIP_BACKLINKS_TO_CHECK)) ? $conf->global->PARTNERSHIP_BACKLINKS_TO_CHECK : $dbacklinks; +print ''; +print ''; +print ''.$dbacklinks.''; +print ''; + + +print ''; print '
'; print ''; print '
'; diff --git a/htdocs/partnership/class/partnership.class.php b/htdocs/partnership/class/partnership.class.php index fd0233d3fef..6bf84ba38cc 100644 --- a/htdocs/partnership/class/partnership.class.php +++ b/htdocs/partnership/class/partnership.class.php @@ -118,6 +118,7 @@ class Partnership extends CommonObject public $date_partnership_start; public $date_partnership_end; public $count_last_url_check_error; + public $last_check_backlink; public $reason_decline_or_cancel; // END MODULEBUILDER PROPERTIES @@ -186,6 +187,7 @@ class Partnership extends CommonObject 'date_partnership_end' => array('type'=>'date', 'label'=>'DatePartnershipEnd', 'enabled'=>'1', 'position'=>53, 'notnull'=>1, 'visible'=>1,), 'status' => array('type'=>'smallint', 'label'=>'Status', 'enabled'=>'1', 'position'=>54, 'notnull'=>0, 'visible'=>2, 'index'=>1, 'arrayofkeyval'=>array('-1'=>'','0'=>$langs->trans('Draft'), '1'=>$langs->trans('Accepted'), '2'=>$langs->trans('Refused'), '9'=>$langs->trans('Canceled')),), 'count_last_url_check_error' => array('type'=>'integer', 'label'=>'CountLastUrlCheckError', 'enabled'=>'1', 'position'=>63, 'notnull'=>0, 'visible'=>-2, 'default'=>'0',), + 'last_check_backlink' => array('type'=>'datetime', 'label'=>'LastCheckBacklink', 'enabled'=>'1', 'position'=>65, 'notnull'=>0, 'visible'=>-2,), 'reason_decline_or_cancel' => array('type'=>'text', 'label'=>'ReasonDeclineOrCancel', 'enabled'=>'1', 'position'=>64, 'notnull'=>0, 'visible'=>-2,), ); @@ -361,7 +363,7 @@ class Partnership extends CommonObject $sql .= ', p.entity, p.date_partnership_start, p.date_partnership_end, p.date_creation'; $sql .= ', p.fk_user_creat, p.tms, p.fk_user_modif, p.fk_user_modif'; $sql .= ', p.note_private, p.note_public'; - $sql .= ', p.last_main_doc, p.count_last_url_check_error, p.reason_decline_or_cancel'; + $sql .= ', p.last_main_doc, p.count_last_url_check_error, p.last_check_backlink, p.reason_decline_or_cancel'; $sql .= ', p.import_key, p.model_pdf'; $sql .= ' FROM '.MAIN_DB_PREFIX.'partnership as p'; @@ -407,6 +409,7 @@ class Partnership extends CommonObject $this->note_public = $obj->note_public; $this->last_main_doc = $obj->last_main_doc; $this->count_last_url_check_error = $obj->count_last_url_check_error; + $this->last_check_backlink = $this->db->jdate($obj->last_check_backlink); $this->reason_decline_or_cancel = $obj->reason_decline_or_cancel; $this->import_key = $obj->import_key; $this->model_pdf = $obj->model_pdf; diff --git a/htdocs/partnership/class/partnershiputils.class.php b/htdocs/partnership/class/partnershiputils.class.php index 08c61b3454c..64fd0f62431 100644 --- a/htdocs/partnership/class/partnershiputils.class.php +++ b/htdocs/partnership/class/partnershiputils.class.php @@ -26,9 +26,13 @@ require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; + dol_include_once('partnership/lib/partnership.lib.php'); +dol_include_once('/partnership/class/partnership.class.php'); - +require_once(DOL_DOCUMENT_ROOT."/societe/class/societe.class.php"); +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; /** * Class with cron tasks of Partnership module */ @@ -49,34 +53,422 @@ class PartnershipUtils $this->db = $db; return 1; } - - + /** - * Action executed by scheduler to cancel status of partnership when subscription is expired + x days. (Max number of cancel per call = $conf->global->PARTNERSHIP_MAX_CANCEL_PER_CALL) + * Action executed by scheduler to cancel status of partnership when subscription is expired + x days. (Max number of action batch per call = $conf->global->PARTNERSHIP_MAX_EXPIRATION_CANCEL_PER_CALL) * * CAN BE A CRON TASK * * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) */ - public function doCancelStatusOfPartnership() + public function doCancelStatusOfMemberPartnership() { global $conf, $langs, $user; - $langs->load("agenda"); + $managedfor = $conf->global->PARTNERSHIP_IS_MANAGED_FOR; - $MAXPERCALL = (empty($conf->global->PARTNERSHIP_MAX_CANCEL_PER_CALL) ? 100 : $conf->global->PARTNERSHIP_MAX_CANCEL_PER_CALL); // Limit to 100 per call + if($managedfor != 'member'){ + return 0; // If option 'PARTNERSHIP_IS_MANAGED_FOR' = 'thirdparty', this cron job does nothing. + } - $error = 0; - $this->output = ''; - $this->error=''; + $partnership = new Partnership($this->db); + $MAXPERCALL = (empty($conf->global->PARTNERSHIP_MAX_EXPIRATION_CANCEL_PER_CALL) ? 25 : $conf->global->PARTNERSHIP_MAX_EXPIRATION_CANCEL_PER_CALL); // Limit to 25 per call + + $langs->loadLangs(array("partnership", "member")); + + $error = 0; + $erroremail = ''; + $this->output = ''; + $this->error = ''; + $partnershipsprocessed = array(); + + $gracedelay=$conf->global->PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL; + if ($gracedelay < 1) + { + $this->error='BadValueForDelayBeforeCancelCheckSetup'; + return -1; + } + + dol_syslog(get_class($this)."::doCancelStatusOfMemberPartnership cancel expired partnerships with grace delay of ".$gracedelay); + + $now = dol_now(); + $datetotest = dol_time_plus_duree($now, -1 * abs($gracedelay), 'd'); + + $this->db->begin(); + + $sql = "SELECT p.rowid, p.fk_member, p.status"; + $sql .= ", d.datefin, d.fk_adherent_type, dty.subscription"; + $sql .= " FROM ".MAIN_DB_PREFIX."partnership as p"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."adherent as d on (d.rowid = p.fk_member)"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."adherent_type as dty on (dty.rowid = d.fk_adherent_type)"; + $sql .= " WHERE fk_member > 0"; + $sql .= " AND (d.datefin < '".$this->db->idate($datetotest)."' AND dty.subscription = 1)"; + $sql .= " AND p.status = ".$partnership::STATUS_ACCEPTED; // Only accepted not yet canceled + $sql .= $this->db->order('d.rowid','ASC'); + // Limit is managed into loop later + + $resql = $this->db->query($sql); + if ($resql) + { + $numofexpiredmembers = $this->db->num_rows($resql); + + $somethingdoneonpartnership = 0; + $ifetchpartner = 0; + while ($ifetchpartner < $numofexpiredmembers) + { + $ifetchpartner++; + + $obj = $this->db->fetch_object($resql); + if ($obj) + { + if (! empty($partnershipsprocessed[$obj->rowid])) continue; + + if ($somethingdoneonpartnership >= $MAXPERCALL) + { + dol_syslog("We reach the limit of ".$MAXPERCALL." partnership processed, so we quit loop for this batch doCancelStatusOfMemberPartnership to avoid to reach email quota.", LOG_WARNING); + break; + } + + $object = new Partnership($this->db); + $object->fetch($obj->rowid); + + // Get expiration date + $expirationdate = $obj->datefin; + + if ($expirationdate && $expirationdate < $now) // If contract expired (we already had a test into main select, this is a security) + { + $somethingdoneonpartnership++; + + $result = $object->cancel($user, 0); + // $conf->global->noapachereload = null; + if ($result < 0) + { + $error++; + $this->error = $object->error; + if (is_array($object->errors) && count($object->errors)) { + if (is_array($this->errors)) $this->errors = array_merge($this->errors, $object->errors); + else $this->errors = $object->errors; + } + } + else + { + $partnershipsprocessed[$object->id]=$object->ref; + + // Send an email to inform member + $labeltemplate = '(SendingEmailOnPartnershipCanceled)'; + + dol_syslog("Now we will send an email to member id=".$object->fk_member." with label ".$labeltemplate); + + // Send deployment email + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + $formmail=new FormMail($this->db); + + // Define output language + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','aZ09')) $newlang = GETPOST('lang_id','aZ09'); + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->loadLangs(array('main','member','partnership')); + } + + $arraydefaultmessage=$formmail->getEMailTemplate($this->db, 'partnership_send', $user, $outputlangs, 0, 1, $labeltemplate); + + $substitutionarray=getCommonSubstitutionArray($outputlangs, 0, null, $object); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + + $subject = make_substitutions($arraydefaultmessage->topic, $substitutionarray, $outputlangs); + $msg = make_substitutions($arraydefaultmessage->content, $substitutionarray, $outputlangs); + $from = dol_string_nospecial($conf->global->MAIN_INFO_SOCIETE_NOM, ' ', array(",")).' <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + + $adherent = new Adherent($this->db); + $adherent->fetch($object->fk_member); + $to = $adherent->email; + + $cmail = new CMailFile($subject, $to, $from, $msg, array(), array(), array(), '', '', 0, 1); + $result = $cmail->sendfile(); + if (! $result || $cmail->error) + { + $erroremail .= ($erroremail ? ', ' : '').$cmail->error; + $this->errors[] = $cmail->error; + if (is_array($cmail->errors) && count($cmail->errors) > 0) $this->errors += $cmail->errors; + } + } + + } + } + } + } + else + { + $error++; + $this->error = $this->db->lasterror(); + } + + if (! $error) + { + $this->db->commit(); + $this->output = $numofexpiredmembers.' expired partnership members found'."\n"; + if ($erroremail) $this->output.='. Got errors when sending some email : '.$erroremail; + } + else + { + $this->db->rollback(); + $this->output = "Rollback after error\n"; + $this->output.= $numofexpiredmembers.' expired partnership members found'."\n"; + if ($erroremail) $this->output.='. Got errors when sending some email : '.$erroremail; + } + + return ($error ? 1: 0); + } - dol_syslog(__METHOD__." we cancel status of partnership ", LOG_DEBUG); + /** + * Action executed by scheduler to check if Dolibarr backlink not found on partner website. (Max number of action batch per call = $conf->global->PARTNERSHIP_MAX_WARNING_BACKLINK_PER_CALL) + * + * CAN BE A CRON TASK + * + * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) + */ + public function doWarningOfPartnershipIfDolibarrBacklinkNotfound() + { + global $conf, $langs, $user; - $now = dol_now(); + $managedfor = $conf->global->PARTNERSHIP_IS_MANAGED_FOR; - // En cours de traitement ... + $partnership = new Partnership($this->db); + $MAXPERCALL = (empty($conf->global->PARTNERSHIP_MAX_WARNING_BACKLINK_PER_CALL) ? 10 : $conf->global->PARTNERSHIP_MAX_WARNING_BACKLINK_PER_CALL); // Limit to 10 per call - return ($error ? 1: 0); + $langs->loadLangs(array("partnership", "member")); + + $error = 0; + $erroremail = ''; + $this->output = ''; + $this->error = ''; + $partnershipsprocessed = array(); + + $gracedelay=$conf->global->PARTNERSHIP_NBDAYS_AFTER_MEMBER_EXPIRATION_BEFORE_CANCEL; + if ($gracedelay < 1) + { + $this->error='BadValueForDelayBeforeCancelCheckSetup'; + return -1; + } + + $fk_partner = ($managedfor == 'member') ? 'fk_member' : 'fk_soc'; + + dol_syslog(get_class($this)."::doWarningOfPartnershipIfDolibarrBacklinkNotfound Warning of partnership"); + + $now = dol_now(); + $datetotest = dol_time_plus_duree($now, -1 * abs($gracedelay), 'd'); + + $this->db->begin(); + + $sql = "SELECT p.rowid, p.status, p.".$fk_partner; + $sql .= ", p.last_check_backlink"; + + $sql .= ', partner.url, partner.email'; + + $sql .= " FROM ".MAIN_DB_PREFIX."partnership as p"; + + if ($managedfor == 'member') { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."adherent as partner on (partner.rowid = p.fk_member)"; + } else { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as partner on (partner.rowid = p.fk_soc)"; + } + + $sql .= " WHERE 1 = 1"; + $sql .= " AND p.".$fk_partner." > 0"; + $sql .= " AND p.status = ".$partnership::STATUS_ACCEPTED; // Only accepted not yet canceled + $sql .= " AND (p.last_check_backlink IS NULL OR p.last_check_backlink <= '".$this->db->idate($now - 7 * 24 * 3600)."')"; // Every week, check that website contains a link to dolibarr. + $sql .= $this->db->order('p.rowid','ASC'); + // Limit is managed into loop later + + $resql = $this->db->query($sql); + if ($resql) + { + $numofexpiredmembers = $this->db->num_rows($resql); + $somethingdoneonpartnership = 0; + $ifetchpartner = 0; + while ($ifetchpartner < $numofexpiredmembers) + { + $ifetchpartner++; + + $obj = $this->db->fetch_object($resql); + if ($obj) + { + if (! empty($partnershipsprocessed[$obj->rowid])) continue; + + if ($somethingdoneonpartnership >= $MAXPERCALL) + { + dol_syslog("We reach the limit of ".$MAXPERCALL." partnership processed, so we quit loop for this batch doWarningOfPartnershipIfDolibarrBacklinkNotfound to avoid to reach email quota.", LOG_WARNING); + break; + } + + $backlinkfound = 0; + + $object = new Partnership($this->db); + $object->fetch($obj->rowid); + + if($managedfor == 'member'){ + $fk_partner = $object->fk_member; + }else{ + $fk_partner = $object->fk_soc; + } + + $website = $obj->url; + + if(empty($website)){ + $websitenotfound .= ($websitenotfound ? ', ' : '').'Website not found for id="'.$fk_partner.'"'."\n"; + } else { + $backlinkfound = $this->checkDolibarrBacklink($website); + } + + if(!$backlinkfound){ + $tmpcount = $object->count_last_url_check_error + 1; + + if($tmpcount == 3){ // Send Warning Email + + if (!empty($obj->email)){ + $emailnotfound .= ($emailnotfound ? ', ' : '').'Email not found for id="'.$fk_partner.'"'."\n"; + + } else { + + $labeltemplate = '(SendingEmailOnPartnershipWillExpire)'; + + dol_syslog("Now we will send an email to partner id=".$fk_partner." with label ".$labeltemplate); + + // Send deployment email + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + $formmail=new FormMail($this->db); + + // Define output language + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','aZ09')) $newlang = GETPOST('lang_id','aZ09'); + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->loadLangs(array('main','member','partnership')); + } + + $arraydefaultmessage=$formmail->getEMailTemplate($this->db, 'partnership_send', $user, $outputlangs, 0, 1, $labeltemplate); + + $substitutionarray=getCommonSubstitutionArray($outputlangs, 0, null, $object); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + + $subject = make_substitutions($arraydefaultmessage->topic, $substitutionarray, $outputlangs); + $msg = make_substitutions($arraydefaultmessage->content, $substitutionarray, $outputlangs); + $from = dol_string_nospecial($conf->global->MAIN_INFO_SOCIETE_NOM, ' ', array(",")).' <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + + $to = $obj->email; + + $cmail = new CMailFile($subject, $to, $from, $msg, array(), array(), array(), '', '', 0, 1); + $result = $cmail->sendfile(); + if (! $result || $cmail->error) + { + $erroremail .= ($erroremail ? ', ' : '').$cmail->error; + $this->errors[] = $cmail->error; + if (is_array($cmail->errors) && count($cmail->errors) > 0) $this->errors += $cmail->errors; + } + } + + + } elseif($tmpcount > 4) { // Cancel Partnership + $object->status = $object::STATUS_CANCELED; + $object->reason_decline_or_cancel = $langs->trans('BacklinkNotFoundOnPartnerWebsite'); + } + + $object->count_last_url_check_error = $tmpcount; + + } else { + $object->count_last_url_check_error = 0; + $object->reason_decline_or_cancel = ''; + } + + $partnershipsprocessed[$object->id]=$object->ref; + + $object->last_check_backlink = $this->db->idate($now); + + $object->update($user); + } + } + } + else + { + $error++; + $this->error = $this->db->lasterror(); + } + + if (! $error) + { + $this->db->commit(); + $this->output = $numofexpiredmembers.' partnership checked'."\n"; + if ($erroremail) $this->output.='. Got errors when sending some email : '.$erroremail."\n"; + if ($emailnotfound) $this->output.='. Email not found for some partner : '.$emailnotfound."\n"; + if ($websitenotfound) $this->output.='. Website not found for some partner : '.$websitenotfound."\n"; + } + else + { + $this->db->rollback(); + $this->output = "Rollback after error\n"; + $this->output.= $numofexpiredmembers.' partnership checked'."\n"; + if ($erroremail) $this->output.='. Got errors when sending some email : '.$erroremail."\n"; + if ($emailnotfound) $this->output.='. Email not found for some partner : '.$emailnotfound."\n"; + if ($websitenotfound) $this->output.='. Website not found for some partner : '.$websitenotfound."\n"; + } + + return ($error ? 1: 0); + } + + /** + * Action to check if Dolibarr backlink not found on partner website + * + * CAN BE A CRON TASK + * + * @return int 0 if KO, 1 if OK + */ + public function checkDolibarrBacklink($website = null) + { + global $conf, $langs, $user; + + $found = 0; + $error = 0; + $webcontent = ''; + + // $website = 'https://nextgestion.com/'; // For Test + $tmpgeturl = getURLContent($website); + if ($tmpgeturl['curl_error_no']) { + $error++; + dol_syslog('Error getting '.$website.': '.$tmpgeturl['curl_error_msg']); + } elseif ($tmpgeturl['http_code'] != '200') { + $error++; + dol_syslog('Error getting '.$website.': '.$tmpgeturl['curl_error_msg']); + } else { + $urlContent = $tmpgeturl['content']; + $dom = new DOMDocument(); + @$dom->loadHTML($urlContent); + + $xpath = new DOMXPath($dom); + $hrefs = $xpath->evaluate("//a"); + + for($i = 0; $i < $hrefs->length; $i++){ + $href = $hrefs->item($i); + $url = $href->getAttribute('href'); + $url = filter_var($url, FILTER_SANITIZE_URL); + if(!filter_var($url, FILTER_VALIDATE_URL) === false){ + $webcontent .= $url; + } + } + } + + if ($webcontent && !empty($conf->global->PARTNERSHIP_BACKLINKS_TO_CHECK) && preg_match('/'.$conf->global->PARTNERSHIP_BACKLINKS_TO_CHECK.'/', $webcontent)) + { + $found = 1; + } + + return $found; } } diff --git a/htdocs/partnership/partnership_list.php b/htdocs/partnership/partnership_list.php index 5ded7ae0047..1ae90d0d4cf 100644 --- a/htdocs/partnership/partnership_list.php +++ b/htdocs/partnership/partnership_list.php @@ -496,9 +496,9 @@ $param .= $hookmanager->resPrint; // List of mass actions available $arrayofmassactions = array( 'cancel'=>img_picto('', 'close_title', 'class="pictofixedwidth"').$langs->trans("Cancel"), - //'generate_doc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("ReGeneratePDF"), - //'builddoc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("PDFMerge"), - 'presend'=>img_picto('', 'email', 'class="pictofixedwidth"').$langs->trans("SendByMail"), + //'generate_doc'=>img_picto('', 'pdf').$langs->trans("ReGeneratePDF"), + //'builddoc'=>img_picto('', 'pdf').$langs->trans("PDFMerge"), + 'presend'=>img_picto('', 'email', 'class="pictofixedwidth"').$langs->trans("SendMail"), ); if ($permissiontodelete) { $arrayofmassactions['predelete'] = img_picto('', 'delete', 'class="pictofixedwidth"').$langs->trans("Delete");