Merge pull request #16052 from eileenmcnaughton/desc
[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
98 if ($document['expires'] <= CRM_Utils_Time::getTimeRaw()) {
847c93ac 99 $newDocument = $this->fetchDocument();
8bfc3cb1 100 if ($newDocument && $this->validateDocument($newDocument)) {
ecbe1139
TO
101 $document = $newDocument;
102 $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['ttl'];
e8977170
TO
103 }
104 else {
d1b65097 105 // keep the old messages for now, try again later
ecbe1139
TO
106 $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['retry'];
107 }
108 $isChanged = TRUE;
109 }
110
111 if ($isChanged) {
112 $this->cache->set('communityMessages', $document);
113 }
114
115 return $document;
116 }
117
118 /**
0880a9d0 119 * Download document from URL and parse as JSON.
ecbe1139 120 *
72b3a70c
CW
121 * @return NULL|array
122 * parsed JSON
ecbe1139 123 */
847c93ac
TO
124 public function fetchDocument() {
125 list($status, $json) = $this->client->get($this->getRenderedUrl());
ecbe1139
TO
126 if ($status != CRM_Utils_HttpClient::STATUS_OK || empty($json)) {
127 return NULL;
128 }
129 $doc = json_decode($json, TRUE);
130 if (empty($doc) || json_last_error() != JSON_ERROR_NONE) {
131 return NULL;
132 }
133 return $doc;
134 }
135
847c93ac
TO
136 /**
137 * Get the final, usable URL string (after interpolating any variables)
138 *
139 * @return FALSE|string
140 */
141 public function getRenderedUrl() {
142 return CRM_Utils_System::evalUrl($this->messagesUrl);
143 }
144
145 /**
146 * @return bool
147 */
148 public function isEnabled() {
149 return $this->messagesUrl !== FALSE && $this->messagesUrl !== 'FALSE';
150 }
151
ecbe1139 152 /**
0880a9d0 153 * Pick a message to display.
ecbe1139 154 *
ecbe1139
TO
155 * @return NULL|array
156 */
dc92f2f8
TO
157 public function pick() {
158 $document = $this->getDocument();
be2fb01f 159 $messages = [];
dc92f2f8
TO
160 foreach ($document['messages'] as $message) {
161 if (!isset($message['perms'])) {
be2fb01f 162 $message['perms'] = [self::DEFAULT_PERMISSION];
dc92f2f8
TO
163 }
164 if (!CRM_Core_Permission::checkAnyPerm($message['perms'])) {
165 continue;
166 }
167
168 if (isset($message['components'])) {
169 $enabled = array_keys(CRM_Core_Component::getEnabledComponents());
170 if (count(array_intersect($enabled, $message['components'])) == 0) {
171 continue;
172 }
173 }
174
175 $messages[] = $message;
176 }
177 if (empty($messages)) {
178 return NULL;
179 }
180
181 $idx = rand(0, count($messages) - 1);
182 return $messages[$idx];
ecbe1139
TO
183 }
184
185 /**
186 * @param string $markup
187 * @return string
188 */
189 public static function evalMarkup($markup) {
d1b65097 190 $config = CRM_Core_Config::singleton();
be2fb01f 191 $vals = [
d1b65097
TO
192 'resourceUrl' => rtrim($config->resourceBase, '/'),
193 'ver' => CRM_Utils_System::version(),
194 'uf' => $config->userFramework,
195 'php' => phpversion(),
202407d7 196 'sid' => CRM_Utils_System::getSiteID(),
d1b65097
TO
197 'baseUrl' => $config->userFrameworkBaseURL,
198 'lang' => $config->lcMessages,
199 'co' => $config->defaultContactCountry,
be2fb01f
CW
200 ];
201 $vars = [];
d1b65097 202 foreach ($vals as $k => $v) {
847c93ac
TO
203 $vars['%%' . $k . '%%'] = $v;
204 $vars['{{' . $k . '}}'] = urlencode($v);
d1b65097
TO
205 }
206 return strtr($markup, $vars);
ecbe1139
TO
207 }
208
8bfc3cb1
TO
209 /**
210 * Ensure that a document is well-formed
211 *
212 * @param array $document
213 * @return bool
214 */
215 public function validateDocument($document) {
389bcebf 216 if (!isset($document['ttl']) || !is_int($document['ttl'])) {
8bfc3cb1
TO
217 return FALSE;
218 }
389bcebf 219 if (!isset($document['retry']) || !is_int($document['retry'])) {
8bfc3cb1
TO
220 return FALSE;
221 }
222 if (!isset($document['messages']) || !is_array($document['messages'])) {
223 return FALSE;
224 }
225 foreach ($document['messages'] as $message) {
226 // TODO validate $message['markup']
227 }
228
229 return TRUE;
230 }
231
ecbe1139 232}