Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(REF) Move CIVICRM_MAIL_LOG logic from patch-files to wrapper-class #16497

Merged
merged 4 commits into from
Feb 11, 2020

Conversation

totten
Copy link
Member

@totten totten commented Feb 7, 2020

Overview

This aims to improve maintainablity and deployment workflows by reduing the
scope of the needed patch-files.

It is an alternative approach for #16481.

Before

civicrm-core included patch files for pear/mail. These files modified
the behavior of Mail_sendmail, Mail_smtp, and Mail_mail to ensure that
the send() function would abide by the CIVICRM_MAIL_LOG constant.

After

civicrm-core includes a wrapper class LoggingMailer. It takes an
instance of Mail_sendmail, Mail_smtp, or Mail_mail and mixes-in the
CIVICRM_MAIL_LOG logic. The resulting object is still an instance of Mail.

Technical Details

There is a hook_civicrm_alterMailer, for which consumers could potentially
see a change in behavior (because the concrete-class of $mailer may
change and support different properties). Mitigating factors:

  • There is only one public method in the Mail contract: send().
  • The only example of alterMailer in the documentation suggests
    that you wholly replace the object with your own implementation.
    This was the original purpose. If you use it this way, then it doesn't
    matter what's happening inside the old object.
    https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterMailer/
  • In public universe, there is only one extension (Cividesk's Sparkpost)
    which uses this hook. And it does swap in that expected way.
  • Just in case... the LoggingMailer passes-through all calls to read/write properties.
    You can see this in action in a specific configuration, e.g.
    ## Before patch
    $ cv ev '$m=Civi::service("pear_mail"); return [get_class($m), $m->host];'
    [
        "Mail_smtp",
        "127.0.0.1"
    ]
    
    ## After patch
    $ cv ev '$m=Civi::service("pear_mail"); return [get_class($m), $m->host];'
    [
        "CRM_Utils_Mail_LoggingMailer",
        "127.0.0.1"
    ]
    

Comments

Ping @seamuslee001

Overview
--------

This aims to improve maintainablity and deployment workflows by reduing the
scope of the needed files.

It is an alternative approach for civicrm#16481.

Before
------

`civicrm-core` included patch files for `pear/mail`.  These files modified
the behavior of `Mail_sendmail`, `Mail_smtp`, and `Mail_mail` to ensure that
the `send()` function would abide by the `CIVICRM_MAIL_LOG` constant.

After
-----

`civicrm-core` includes a wrapper class `LoggingMailer`.  It takes an
instance of `Mail_sendmail`, `Mail_smtp`, or `Mail_mail` and mixes-in the
`CIVICRM_MAIL_LOG` logic.

Technical Details
-----------------

There is a `hook_civicrm_alterMailer`, for which consumers could potentially
see a change in behavior (because the concrete-class of `$mailer` may
change and support different properties). Mitigating factors:

* There is only one public method in the `Mail` contract: `send()`.
* The only example of `alterMailer` in the documentation suggests
  that you wholly replace the object with your own implementation.
  This was the original purpose. If you use it this way, then it doesn't
  matter what's happening inside the old object.
  https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterMailer/
* In public `universe`, there is only one extension (Cividesk's Sparkpost)
  which uses this hook. And it does swap in that expected way.
* Just in case... the `LoggingMailer` passes-through all calls to read/write properties.
  You can see this in action in a specific configuration, e.g.
  ```
  ## Before patch
  $ cv ev '$m=Civi::service("pear_mail"); return [get_class($m), $m->host];'
  [
      "Mail_smtp",
      "127.0.0.1"
  ]

  ## After patch
  $ cv ev '$m=Civi::service("pear_mail"); return [get_class($m), $m->host];'
  [
      "CRM_Utils_Mail_LoggingMailer",
      "127.0.0.1"
  ]
  ```
@civibot
Copy link

civibot bot commented Feb 7, 2020

(Standard links)

@civibot civibot bot added the master label Feb 7, 2020
@seamuslee001
Copy link
Contributor

@totten this test failure api_v3_JobProcessMailingTest.testConcurrency looks like it might relate, I like the idea of this

@totten
Copy link
Member Author

totten commented Feb 8, 2020

jenkins, test this please

Yeah, at a first glance, it does seem plausible. I tried running the test locally (both in isolation and in the full suite), and it didn't reproduce. It could also be coincidence in a test that's a little time-sensitive.

Consider _createMailer() circa line 122 - the tests for simulated mail-blast are supposed to write to a log-table instead of sending messages, which means they're supposed to go through the CRM_Mailing_BAO_Spool path (without using Mail_smtp or Mail_sendmail or Mail_mail).

Fingers crossed 🤞 for another run to give better result.

@seamuslee001
Copy link
Contributor

Test fail is unrelated

@seamuslee001
Copy link
Contributor

@totten I did an r-run of this by clearing out my local vendor folder and reinstalling packages and clearing cache then submitting the SMTP settings to send a test email. I had to make one additional patch to make it work. However once done it seemed to work correctly

diff --git a/CRM/Admin/Form/Setting/Smtp.php b/CRM/Admin/Form/Setting/Smtp.php
index 87d30761cf..a3590b3ca2 100644
--- a/CRM/Admin/Form/Setting/Smtp.php
+++ b/CRM/Admin/Form/Setting/Smtp.php
@@ -165,7 +165,7 @@ class CRM_Admin_Form_Setting_Smtp extends CRM_Admin_Form_Setting {
           'Subject' => $subject,
         ];

-        $mailer = Mail::factory($mailerName, $params);
+        $mailer = CRM_Utils_Mail::_createMailer($mailerName, $params);

         $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
         $result = $mailer->send($toEmail, $headers, $message);

@seamuslee001
Copy link
Contributor

When civicrm-core used patches to change the behavior of `Mail_mail` (etal),
this was fine.  The preceding commit removes the patches and uses a wrapper
instead - so it's more important to get the same mailer as the
regular CRM_Utils_Mail logic.
@totten
Copy link
Member Author

totten commented Feb 11, 2020

Good points @seamuslee001 ! Some follow-ups:

  • Code updates:
    • For the first point (CRM/Admin/Form/Setting/Smtp.php), I added the fix for Administer => System => Outbound Mail UI.
    • For the second point (quirk-handling for CC/BCC via CRM_Utils_Mail::send()), I made some changes to the wrapper class. The newer FilteredPearMailer allows a similar conditional ($mailer->getDriver() === 'mail') so that the quirk-handling doesn't have to change much. (It also might allow future cleanup by extracting the quirk-handler to a separate function, but I didn't want the scope-of-work/review to expand even further, so I kept the patch minimal and left a TODO.)
  • Testing:
    • I ran with mailcatcher and manually checked each permutation of:
      • (1a) Send email via "View Contact" screen and (1b) Send email via Administer => System => Outbound Mail UI
      • (2a) With SMTP settings, (2b) With CIVICRM_MAIL_LOG pointed to a file, and (2c) With CIVICRM_MAIL_LOG and CIVICRM_MAIL_LOG_AND_SEND.
    • Added a small unit-test for the FilteredPearMail utility

@seamuslee001
Copy link
Contributor

Changes look good to me adding merge on pass

@yashodha
Copy link
Contributor

test this please

…d Logger

1. Captures more context (i.e. the original driver and params)
2. Changes various property names to avoid potential for conflict with delegated properties
3. Adds `addFilter($id, $func)` so that one can move more filters in here
4. Consolidates the various references ot CIVICRM_MAIL_LOG* into one file.
@totten totten force-pushed the master-pear-mail-delg branch from 1a53571 to e4c7508 Compare February 11, 2020 04:32
@totten
Copy link
Member Author

totten commented Feb 11, 2020

There were a large number of failures like this...

CRM_Event_Form_ParticipantTest::testParticipantOfflineReceipt with data set #1 (',')
Invalid argument supplied for foreach()

/home/jenkins/bknix-dfl/build/core-16497-6bzhv/web/sites/all/modules/civicrm/CRM/Utils/Mail/FilteredPearMailer.php:61
/home/jenkins/bknix-dfl/build/core-16497-6bzhv/web/sites/all/modules/civicrm/CRM/Utils/Mail.php:299

... because the filters list is empty (NULL) in headless environment.

It should be empty ([]). Patched/squashed/pushed.

@seamuslee001 seamuslee001 merged commit ead59d8 into civicrm:master Feb 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants