d12a03a5d07a76b81113c5d936195b8a43f241ef
3 class CRM_OAuth_MailSetup
{
6 * Return a list of setup-options based on OAuth2 services.
8 * @see CRM_Utils_Hook::mailSetupActions()
10 public static function buildSetupLinks() {
11 $clients = Civi\Api4\OAuthClient
::get(0)->addWhere('is_active', '=', 1)->execute();
12 $providers = Civi\Api4\OAuthProvider
::get(0)->execute()->indexBy('name');
15 foreach ($clients as $client) {
16 $provider = $providers[$client['provider']] ??
NULL;
17 if ($provider === NULL) {
20 // v api OptionValue.get option_group_id=mail_protocol
21 if (!empty($provider['mailSettingsTemplate'])) {
22 $setupActions['oauth_' . $client['id']] = [
23 'title' => sprintf('%s (ID #%s)', $provider['title'] ??
$provider['name'] ??
ts('OAuth2'), $client['id']),
24 'callback' => ['CRM_OAuth_MailSetup', 'setup'],
25 'oauth_client_id' => $client['id'],
34 * When a user chooses to add one of our mail options, we kick off
35 * the authorization-code workflow.
37 * @param array $setupAction
38 * The chosen descriptor from mailSetupActions.
41 * - url: string, the final URL to go to.
42 * @see CRM_Utils_Hook::mailSetupActions()
44 public static function setup($setupAction) {
45 $authCode = Civi\Api4\OAuthClient
::authorizationCode(0)
46 ->addWhere('id', '=', $setupAction['oauth_client_id'])
47 ->setStorage('OAuthSysToken')
48 ->setTag('MailSettings:setup')
53 'url' => $authCode['url'],
58 * When the user returns with a token, we add a new record to
59 * civicrm_mail_settings with defaults and redirect to the edit screen.
63 * @param string $nextUrl
65 public static function onReturn($token, &$nextUrl) {
66 if ($token['tag'] !== 'MailSettings:setup') {
70 $client = \Civi\Api4\OAuthClient
::get(0)->addWhere('id', '=', $token['client_id'])->execute()->single();
71 $provider = \Civi\Api4\OAuthProvider
::get(0)->addWhere('name', '=', $client['provider'])->execute()->single();
73 $vars = ['token' => $token, 'client' => $client, 'provider' => $provider];
74 $mailSettings = civicrm_api4('MailSettings', 'create', [
75 'values' => self
::evalArrayTemplate($provider['mailSettingsTemplate'], $vars),
78 \Civi\Api4\OAuthSysToken
::update(0)
79 ->addWhere('id', '=', $token['id'])
80 ->setValues(['tag' => 'MailSettings:' . $mailSettings['id']])
83 CRM_Core_Session
::setStatus(
84 ts('Here are the account defaults we detected for %1. Please check them carefully.', [
85 1 => $mailSettings['name'],
87 ts('Account created!'),
91 $nextUrl = CRM_Utils_System
::url('civicrm/admin/mailSettings', [
93 'id' => $mailSettings['id'],
95 ], TRUE, NULL, FALSE);
99 * @param array $template
100 * List of key-value expressions.
101 * Ex: ['name' => '{{person.first}} {{person.last}}']
102 * Expressions begin with the dotted-name of a variable.
103 * Optionally, the value may be piped through other functions
105 * Array tree of data to interpolate.
107 * The template array, with '{{...}}' expressions evaluated.
109 public static function evalArrayTemplate($template, $vars) {
111 'getMailDomain' => function($v) {
112 $parts = explode('@', $v);
113 return $parts[1] ??
NULL;
115 'getMailUser' => function($v) {
116 $parts = explode('@', $v);
117 return $parts[0] ??
NULL;
121 $lookupVars = function($m) use ($vars, $filters) {
122 $parts = explode('|', $m[1]);
123 $value = (string) CRM_Utils_Array
::pathGet($vars, explode('.', array_shift($parts)));
124 foreach ($parts as $part) {
125 if (isset($filters[$part])) {
126 $value = $filters[$part]($value);
136 foreach ($template as $key => $value) {
137 $values[$key] = is_string($value)
138 ?
preg_replace_callback(';{{([a-zA-Z0-9_\.\|]+)}};', $lookupVars, $value)
145 * If we have a stored token for this for this, then use it.
147 * @see CRM_Utils_Hook::alterMailStore()
149 public static function alterMailStore(&$mailSettings) {
150 $token = civicrm_api4('OAuthSysToken', 'refresh', [
151 'checkPermissions' => FALSE,
152 'where' => [['tag', '=', 'MailSettings:' . $mailSettings['id']]],
153 'orderBy' => ['id' => 'DESC'],
156 if ($token === NULL) {
159 // Not certain if 'refresh' will complain about staleness. Doesn't hurt to double-check.
160 if (empty($token['access_token']) ||
$token['expires'] < CRM_Utils_Time
::getTimeRaw()) {
161 throw new \
OAuthException("Found invalid token for mail store #" . $mailSettings['id']);
164 $mailSettings['auth'] = 'XOAuth2';
165 $mailSettings['password'] = $token['access_token'];