Update release-notes/5.52.1.md
[civicrm-core.git] / CRM / Core / CommunityMessages.php
CommitLineData
ecbe1139
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
ecbe1139 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
ecbe1139 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
ecbe1139
TO
11
12/**
13 * Manage the download, validation, and rendering of community messages
14 */
15class CRM_Core_CommunityMessages {
16
20eff8a7 17 const DEFAULT_MESSAGES_URL = 'https://alert.civicrm.org/alert?prot=1&ver={ver}&uf={uf}&sid={sid}&lang={lang}&co={co}';
dc92f2f8 18 const DEFAULT_PERMISSION = 'administer CiviCRM';
e8977170 19
ecbe1139 20 /**
0880a9d0 21 * Default time to wait before retrying.
ecbe1139 22 */
518fa0ee
SL
23 // 2 hours
24 const DEFAULT_RETRY = 7200;
ecbe1139
TO
25
26 /**
27 * @var CRM_Utils_HttpClient
28 */
29 protected $client;
30
31 /**
32 * @var CRM_Utils_Cache_Interface
33 */
34 protected $cache;
35
e8977170 36 /**
e97c66ff 37 * Url to retrieve community messages from.
38 *
39 * False means a retrieval will not be attempted.
40 *
41 * @var false|string
e8977170
TO
42 */
43 protected $messagesUrl;
44
847c93ac 45 /**
0880a9d0 46 * Create default instance.
847c93ac
TO
47 *
48 * @return CRM_Core_CommunityMessages
49 */
50 public static function create() {
51 return new CRM_Core_CommunityMessages(
a4704404 52 Civi::cache('community_messages'),
847c93ac
TO
53 CRM_Utils_HttpClient::singleton()
54 );
55 }
56
ecbe1139 57 /**
e97c66ff 58 * Class constructor.
59 *
ecbe1139
TO
60 * @param CRM_Utils_Cache_Interface $cache
61 * @param CRM_Utils_HttpClient $client
e97c66ff 62 * @param string|false $messagesUrl
ecbe1139 63 */
e8977170 64 public function __construct($cache, $client, $messagesUrl = NULL) {
ecbe1139
TO
65 $this->cache = $cache;
66 $this->client = $client;
e8977170 67 if ($messagesUrl === NULL) {
d356cdeb 68 $this->messagesUrl = Civi::settings()->get('communityMessagesUrl');
e8977170
TO
69 }
70 else {
71 $this->messagesUrl = $messagesUrl;
72 }
847c93ac
TO
73 if ($this->messagesUrl === '*default*') {
74 $this->messagesUrl = self::DEFAULT_MESSAGES_URL;
75 }
ecbe1139
TO
76 }
77
78 /**
e97c66ff 79 * Get the messages document (either from the cache or by downloading).
ecbe1139
TO
80 *
81 * @return NULL|array
82 */
83 public function getDocument() {
ecbe1139
TO
84 $isChanged = FALSE;
85 $document = $this->cache->get('communityMessages');
86
87 if (empty($document) || !is_array($document)) {
be2fb01f
CW
88 $document = [
89 'messages' => [],
518fa0ee
SL
90 // ASAP
91 'expires' => 0,
ecbe1139
TO
92 'ttl' => self::DEFAULT_RETRY,
93 'retry' => self::DEFAULT_RETRY,
be2fb01f 94 ];
ecbe1139
TO
95 $isChanged = TRUE;
96 }
97
aac05952
TO
98 $refTime = CRM_Utils_Time::getTimeRaw();
99 if ($document['expires'] <= $refTime) {
847c93ac 100 $newDocument = $this->fetchDocument();
8bfc3cb1 101 if ($newDocument && $this->validateDocument($newDocument)) {
ecbe1139 102 $document = $newDocument;
aac05952 103 $document['expires'] = $refTime + $document['ttl'];
e8977170
TO
104 }
105 else {
d1b65097 106 // keep the old messages for now, try again later
aac05952 107 $document['expires'] = $refTime + $document['retry'];
ecbe1139
TO
108 }
109 $isChanged = TRUE;
110 }
111
112 if ($isChanged) {
113 $this->cache->set('communityMessages', $document);
114 }
115
116 return $document;
117 }
118
119 /**
0880a9d0 120 * Download document from URL and parse as JSON.
ecbe1139 121 *
72b3a70c
CW
122 * @return NULL|array
123 * parsed JSON
ecbe1139 124 */
847c93ac
TO
125 public function fetchDocument() {
126 list($status, $json) = $this->client->get($this->getRenderedUrl());
ecbe1139
TO
127 if ($status != CRM_Utils_HttpClient::STATUS_OK || empty($json)) {
128 return NULL;
129 }
130 $doc = json_decode($json, TRUE);
131 if (empty($doc) || json_last_error() != JSON_ERROR_NONE) {
132 return NULL;
133 }
134 return $doc;
135 }
136
847c93ac
TO
137 /**
138 * Get the final, usable URL string (after interpolating any variables)
139 *
140 * @return FALSE|string
141 */
142 public function getRenderedUrl() {
143 return CRM_Utils_System::evalUrl($this->messagesUrl);
144 }
145
146 /**
147 * @return bool
148 */
149 public function isEnabled() {
150 return $this->messagesUrl !== FALSE && $this->messagesUrl !== 'FALSE';
151 }
152
ecbe1139 153 /**
0880a9d0 154 * Pick a message to display.
ecbe1139 155 *
ecbe1139
TO
156 * @return NULL|array
157 */
dc92f2f8
TO
158 public function pick() {
159 $document = $this->getDocument();
be2fb01f 160 $messages = [];
dc92f2f8
TO
161 foreach ($document['messages'] as $message) {
162 if (!isset($message['perms'])) {
be2fb01f 163 $message['perms'] = [self::DEFAULT_PERMISSION];
dc92f2f8
TO
164 }
165 if (!CRM_Core_Permission::checkAnyPerm($message['perms'])) {
166 continue;
167 }
168
169 if (isset($message['components'])) {
170 $enabled = array_keys(CRM_Core_Component::getEnabledComponents());
171 if (count(array_intersect($enabled, $message['components'])) == 0) {
172 continue;
173 }
174 }
175
176 $messages[] = $message;
177 }
178 if (empty($messages)) {
179 return NULL;
180 }
181
182 $idx = rand(0, count($messages) - 1);
183 return $messages[$idx];
ecbe1139
TO
184 }
185
186 /**
187 * @param string $markup
188 * @return string
189 */
190 public static function evalMarkup($markup) {
d1b65097 191 $config = CRM_Core_Config::singleton();
be2fb01f 192 $vals = [
d1b65097
TO
193 'resourceUrl' => rtrim($config->resourceBase, '/'),
194 'ver' => CRM_Utils_System::version(),
195 'uf' => $config->userFramework,
196 'php' => phpversion(),
202407d7 197 'sid' => CRM_Utils_System::getSiteID(),
d1b65097
TO
198 'baseUrl' => $config->userFrameworkBaseURL,
199 'lang' => $config->lcMessages,
08fb3005 200 'co' => $config->defaultContactCountry ?? '',
be2fb01f
CW
201 ];
202 $vars = [];
d1b65097 203 foreach ($vals as $k => $v) {
847c93ac
TO
204 $vars['%%' . $k . '%%'] = $v;
205 $vars['{{' . $k . '}}'] = urlencode($v);
d1b65097
TO
206 }
207 return strtr($markup, $vars);
ecbe1139
TO
208 }
209
8bfc3cb1
TO
210 /**
211 * Ensure that a document is well-formed
212 *
213 * @param array $document
214 * @return bool
215 */
216 public function validateDocument($document) {
389bcebf 217 if (!isset($document['ttl']) || !is_int($document['ttl'])) {
8bfc3cb1
TO
218 return FALSE;
219 }
389bcebf 220 if (!isset($document['retry']) || !is_int($document['retry'])) {
8bfc3cb1
TO
221 return FALSE;
222 }
223 if (!isset($document['messages']) || !is_array($document['messages'])) {
224 return FALSE;
225 }
226 foreach ($document['messages'] as $message) {
227 // TODO validate $message['markup']
228 }
229
230 return TRUE;
231 }
232
ecbe1139 233}