Merge pull request #15322 from alifrumin/removePrintIcon
[civicrm-core.git] / CRM / Core / CommunityMessages.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * Manage the download, validation, and rendering of community messages
30 */
31 class CRM_Core_CommunityMessages {
32
33 const DEFAULT_MESSAGES_URL = 'https://alert.civicrm.org/alert?prot=1&ver={ver}&uf={uf}&sid={sid}&lang={lang}&co={co}';
34 const DEFAULT_PERMISSION = 'administer CiviCRM';
35
36 /**
37 * Default time to wait before retrying.
38 */
39 // 2 hours
40 const DEFAULT_RETRY = 7200;
41
42 /**
43 * @var CRM_Utils_HttpClient
44 */
45 protected $client;
46
47 /**
48 * @var CRM_Utils_Cache_Interface
49 */
50 protected $cache;
51
52 /**
53 * Url to retrieve community messages from.
54 *
55 * False means a retrieval will not be attempted.
56 *
57 * @var false|string
58 */
59 protected $messagesUrl;
60
61 /**
62 * Create default instance.
63 *
64 * @return CRM_Core_CommunityMessages
65 */
66 public static function create() {
67 return new CRM_Core_CommunityMessages(
68 Civi::cache('community_messages'),
69 CRM_Utils_HttpClient::singleton()
70 );
71 }
72
73 /**
74 * Class constructor.
75 *
76 * @param CRM_Utils_Cache_Interface $cache
77 * @param CRM_Utils_HttpClient $client
78 * @param string|false $messagesUrl
79 */
80 public function __construct($cache, $client, $messagesUrl = NULL) {
81 $this->cache = $cache;
82 $this->client = $client;
83 if ($messagesUrl === NULL) {
84 $this->messagesUrl = Civi::settings()->get('communityMessagesUrl');
85 }
86 else {
87 $this->messagesUrl = $messagesUrl;
88 }
89 if ($this->messagesUrl === '*default*') {
90 $this->messagesUrl = self::DEFAULT_MESSAGES_URL;
91 }
92 }
93
94 /**
95 * Get the messages document (either from the cache or by downloading).
96 *
97 * @return NULL|array
98 */
99 public function getDocument() {
100 $isChanged = FALSE;
101 $document = $this->cache->get('communityMessages');
102
103 if (empty($document) || !is_array($document)) {
104 $document = [
105 'messages' => [],
106 // ASAP
107 'expires' => 0,
108 'ttl' => self::DEFAULT_RETRY,
109 'retry' => self::DEFAULT_RETRY,
110 ];
111 $isChanged = TRUE;
112 }
113
114 if ($document['expires'] <= CRM_Utils_Time::getTimeRaw()) {
115 $newDocument = $this->fetchDocument();
116 if ($newDocument && $this->validateDocument($newDocument)) {
117 $document = $newDocument;
118 $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['ttl'];
119 }
120 else {
121 // keep the old messages for now, try again later
122 $document['expires'] = CRM_Utils_Time::getTimeRaw() + $document['retry'];
123 }
124 $isChanged = TRUE;
125 }
126
127 if ($isChanged) {
128 $this->cache->set('communityMessages', $document);
129 }
130
131 return $document;
132 }
133
134 /**
135 * Download document from URL and parse as JSON.
136 *
137 * @return NULL|array
138 * parsed JSON
139 */
140 public function fetchDocument() {
141 list($status, $json) = $this->client->get($this->getRenderedUrl());
142 if ($status != CRM_Utils_HttpClient::STATUS_OK || empty($json)) {
143 return NULL;
144 }
145 $doc = json_decode($json, TRUE);
146 if (empty($doc) || json_last_error() != JSON_ERROR_NONE) {
147 return NULL;
148 }
149 return $doc;
150 }
151
152 /**
153 * Get the final, usable URL string (after interpolating any variables)
154 *
155 * @return FALSE|string
156 */
157 public function getRenderedUrl() {
158 return CRM_Utils_System::evalUrl($this->messagesUrl);
159 }
160
161 /**
162 * @return bool
163 */
164 public function isEnabled() {
165 return $this->messagesUrl !== FALSE && $this->messagesUrl !== 'FALSE';
166 }
167
168 /**
169 * Pick a message to display.
170 *
171 * @return NULL|array
172 */
173 public function pick() {
174 $document = $this->getDocument();
175 $messages = [];
176 foreach ($document['messages'] as $message) {
177 if (!isset($message['perms'])) {
178 $message['perms'] = [self::DEFAULT_PERMISSION];
179 }
180 if (!CRM_Core_Permission::checkAnyPerm($message['perms'])) {
181 continue;
182 }
183
184 if (isset($message['components'])) {
185 $enabled = array_keys(CRM_Core_Component::getEnabledComponents());
186 if (count(array_intersect($enabled, $message['components'])) == 0) {
187 continue;
188 }
189 }
190
191 $messages[] = $message;
192 }
193 if (empty($messages)) {
194 return NULL;
195 }
196
197 $idx = rand(0, count($messages) - 1);
198 return $messages[$idx];
199 }
200
201 /**
202 * @param string $markup
203 * @return string
204 */
205 public static function evalMarkup($markup) {
206 $config = CRM_Core_Config::singleton();
207 $vals = [
208 'resourceUrl' => rtrim($config->resourceBase, '/'),
209 'ver' => CRM_Utils_System::version(),
210 'uf' => $config->userFramework,
211 'php' => phpversion(),
212 'sid' => CRM_Utils_System::getSiteID(),
213 'baseUrl' => $config->userFrameworkBaseURL,
214 'lang' => $config->lcMessages,
215 'co' => $config->defaultContactCountry,
216 ];
217 $vars = [];
218 foreach ($vals as $k => $v) {
219 $vars['%%' . $k . '%%'] = $v;
220 $vars['{{' . $k . '}}'] = urlencode($v);
221 }
222 return strtr($markup, $vars);
223 }
224
225 /**
226 * Ensure that a document is well-formed
227 *
228 * @param array $document
229 * @return bool
230 */
231 public function validateDocument($document) {
232 if (!isset($document['ttl']) || !is_int($document['ttl'])) {
233 return FALSE;
234 }
235 if (!isset($document['retry']) || !is_int($document['retry'])) {
236 return FALSE;
237 }
238 if (!isset($document['messages']) || !is_array($document['messages'])) {
239 return FALSE;
240 }
241 foreach ($document['messages'] as $message) {
242 // TODO validate $message['markup']
243 }
244
245 return TRUE;
246 }
247
248 }