Merge pull request #15042 from civicrm/5.17
[civicrm-core.git] / CRM / Core / BAO / Dashboard.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 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2019
32 */
33
34 /**
35 * Class contains Contact dashboard related functions.
36 */
37 class CRM_Core_BAO_Dashboard extends CRM_Core_DAO_Dashboard {
38
39 /**
40 * Add Dashboard.
41 *
42 * @param array $params
43 * Values.
44 *
45 *
46 * @return object
47 */
48 public static function create($params) {
49 $hook = empty($params['id']) ? 'create' : 'edit';
50 CRM_Utils_Hook::pre($hook, 'Dashboard', CRM_Utils_Array::value('id', $params), $params);
51 $dao = self::addDashlet($params);
52 CRM_Utils_Hook::post($hook, 'Dashboard', $dao->id, $dao);
53 return $dao;
54 }
55
56 /**
57 * Get the list of dashlets enabled by admin.
58 *
59 * @param bool $all
60 * All or only active.
61 * @param bool $checkPermission
62 * All or only authorized for the current user.
63 *
64 * @return array
65 * array of dashlets
66 */
67 public static function getDashlets($all = TRUE, $checkPermission = TRUE) {
68 $dashlets = [];
69 $dao = new CRM_Core_DAO_Dashboard();
70
71 if (!$all) {
72 $dao->is_active = 1;
73 }
74
75 $dao->domain_id = CRM_Core_Config::domainID();
76
77 $dao->find();
78 while ($dao->fetch()) {
79 if ($checkPermission && !self::checkPermission($dao->permission, $dao->permission_operator)) {
80 continue;
81 }
82
83 $values = [];
84 CRM_Core_DAO::storeValues($dao, $values);
85 $dashlets[$dao->id] = $values;
86 }
87
88 return $dashlets;
89 }
90
91 /**
92 * Get the list of dashlets for the current user or the specified user.
93 *
94 * Additionlly, initializes the dashboard with defaults if this is the
95 * user's first visit to their dashboard.
96 *
97 * @param int $contactID
98 * Defaults to the current user.
99 *
100 * @return array
101 * array of dashlets
102 */
103 public static function getContactDashlets($contactID = NULL) {
104 $contactID = $contactID ? $contactID : CRM_Core_Session::getLoggedInContactID();
105 $dashlets = [];
106
107 // Get contact dashboard dashlets.
108 $results = civicrm_api3('DashboardContact', 'get', [
109 'contact_id' => $contactID,
110 'is_active' => 1,
111 'dashboard_id.is_active' => 1,
112 'options' => ['sort' => 'weight', 'limit' => 0],
113 'return' => [
114 'id',
115 'weight',
116 'column_no',
117 'dashboard_id',
118 'dashboard_id.name',
119 'dashboard_id.label',
120 'dashboard_id.url',
121 'dashboard_id.fullscreen_url',
122 'dashboard_id.cache_minutes',
123 'dashboard_id.permission',
124 'dashboard_id.permission_operator',
125 ],
126 ]);
127
128 foreach ($results['values'] as $item) {
129 if (self::checkPermission(CRM_Utils_Array::value('dashboard_id.permission', $item), CRM_Utils_Array::value('dashboard_id.permission_operator', $item))) {
130 $dashlets[$item['id']] = [
131 'dashboard_id' => $item['dashboard_id'],
132 'weight' => $item['weight'],
133 'column_no' => $item['column_no'],
134 'name' => $item['dashboard_id.name'],
135 'label' => $item['dashboard_id.label'],
136 'url' => $item['dashboard_id.url'],
137 'cache_minutes' => $item['dashboard_id.cache_minutes'],
138 'fullscreen_url' => CRM_Utils_Array::value('dashboard_id.fullscreen_url', $item),
139 ];
140 }
141 }
142
143 // If empty, then initialize default dashlets for this user.
144 if (!$results['count']) {
145 // They may just have disabled all their dashlets. Check if any records exist for this contact.
146 if (!civicrm_api3('DashboardContact', 'getcount', ['contact_id' => $contactID])) {
147 $dashlets = self::initializeDashlets();
148 }
149 }
150
151 return $dashlets;
152 }
153
154 /**
155 * @return array
156 */
157 public static function getContactDashletsForJS() {
158 $data = [[], []];
159 foreach (self::getContactDashlets() as $item) {
160 $data[$item['column_no']][] = [
161 'id' => (int) $item['dashboard_id'],
162 'name' => $item['name'],
163 'title' => $item['label'],
164 'url' => self::parseUrl($item['url']),
165 'cacheMinutes' => $item['cache_minutes'],
166 'fullscreenUrl' => self::parseUrl($item['fullscreen_url']),
167 ];
168 }
169 return $data;
170 }
171
172 /**
173 * Setup default dashlets for new users.
174 *
175 * When a user accesses their dashboard for the first time, set up
176 * the default dashlets.
177 *
178 * @return array
179 * Array of dashboard_id's
180 * @throws \CiviCRM_API3_Exception
181 */
182 public static function initializeDashlets() {
183 $dashlets = [];
184 $getDashlets = civicrm_api3("Dashboard", "get", [
185 'domain_id' => CRM_Core_Config::domainID(),
186 'option.limit' => 0,
187 ]);
188 $contactID = CRM_Core_Session::getLoggedInContactID();
189 $allDashlets = CRM_Utils_Array::index(['name'], $getDashlets['values']);
190 $defaultDashlets = [];
191 $defaults = ['blog' => 1, 'getting-started' => '0'];
192 foreach ($defaults as $name => $column) {
193 if (!empty($allDashlets[$name]) && !empty($allDashlets[$name]['id'])) {
194 $defaultDashlets[$name] = [
195 'dashboard_id' => $allDashlets[$name]['id'],
196 'is_active' => 1,
197 'column_no' => $column,
198 'contact_id' => $contactID,
199 ];
200 }
201 }
202 CRM_Utils_Hook::dashboard_defaults($allDashlets, $defaultDashlets);
203 if (is_array($defaultDashlets) && !empty($defaultDashlets)) {
204 foreach ($defaultDashlets as $id => $defaultDashlet) {
205 $dashboard_id = $defaultDashlet['dashboard_id'];
206 $dashlet = $getDashlets['values'][$dashboard_id];
207 if (!self::checkPermission(CRM_Utils_Array::value('permission', $dashlet), CRM_Utils_Array::value('permission_operator', $dashlet))) {
208 continue;
209 }
210 else {
211 $assignDashlets = civicrm_api3("dashboard_contact", "create", $defaultDashlet);
212 $values = $assignDashlets['values'][$assignDashlets['id']];
213 $dashlets[$assignDashlets['id']] = [
214 'dashboard_id' => $values['dashboard_id'],
215 'weight' => $values['weight'],
216 'column_no' => $values['column_no'],
217 'name' => $dashlet['name'],
218 'label' => $dashlet['label'],
219 'cache_minutes' => $dashlet['cache_minutes'],
220 'url' => $dashlet['url'],
221 'fullscreen_url' => CRM_Utils_Array::value('fullscreen_url', $dashlet),
222 ];
223 }
224 }
225 }
226 return $dashlets;
227 }
228
229 /**
230 * @param $url
231 * @return string
232 */
233 public static function parseUrl($url) {
234 // Check if it is already a fully-formed url
235 if ($url && substr($url, 0, 4) != 'http' && $url[0] != '/') {
236 $urlParam = explode('?', $url);
237 $url = CRM_Utils_System::url($urlParam[0], CRM_Utils_Array::value(1, $urlParam), FALSE, NULL, FALSE);
238 }
239 return $url;
240 }
241
242 /**
243 * Check dashlet permission for current user.
244 *
245 * @param string $permission
246 * Comma separated list.
247 * @param string $operator
248 *
249 * @return bool
250 * true if use has permission else false
251 */
252 public static function checkPermission($permission, $operator) {
253 if ($permission) {
254 $permissions = explode(',', $permission);
255 $config = CRM_Core_Config::singleton();
256
257 static $allComponents;
258 if (!$allComponents) {
259 $allComponents = CRM_Core_Component::getNames();
260 }
261
262 $hasPermission = FALSE;
263 foreach ($permissions as $key) {
264 $showDashlet = TRUE;
265
266 $componentName = NULL;
267 if (strpos($key, 'access') === 0) {
268 $componentName = trim(substr($key, 6));
269 if (!in_array($componentName, $allComponents)) {
270 $componentName = NULL;
271 }
272 }
273
274 // hack to handle case permissions
275 if (!$componentName
276 && in_array($key, ['access my cases and activities', 'access all cases and activities'])
277 ) {
278 $componentName = 'CiviCase';
279 }
280
281 //hack to determine if it's a component related permission
282 if ($componentName) {
283 if (!in_array($componentName, $config->enableComponents) ||
284 !CRM_Core_Permission::check($key)
285 ) {
286 $showDashlet = FALSE;
287 if ($operator == 'AND') {
288 return $showDashlet;
289 }
290 }
291 else {
292 $hasPermission = TRUE;
293 }
294 }
295 elseif (!CRM_Core_Permission::check($key)) {
296 $showDashlet = FALSE;
297 if ($operator == 'AND') {
298 return $showDashlet;
299 }
300 }
301 else {
302 $hasPermission = TRUE;
303 }
304 }
305
306 if (!$showDashlet && !$hasPermission) {
307 return FALSE;
308 }
309 else {
310 return TRUE;
311 }
312 }
313 else {
314 // if permission is not set consider everyone has permission to access it.
315 return TRUE;
316 }
317 }
318
319 /**
320 * Save changes made by user to the Dashlet.
321 *
322 * @param array $columns
323 *
324 * @param int $contactID
325 *
326 * @throws RuntimeException
327 */
328 public static function saveDashletChanges($columns, $contactID = NULL) {
329 if (!$contactID) {
330 $contactID = CRM_Core_Session::getLoggedInContactID();
331 }
332
333 if (empty($contactID)) {
334 throw new RuntimeException("Failed to determine contact ID");
335 }
336
337 $dashletIDs = [];
338 if (is_array($columns)) {
339 foreach ($columns as $colNo => $dashlets) {
340 if (!is_int($colNo)) {
341 continue;
342 }
343 $weight = 1;
344 foreach ($dashlets as $dashletID => $isMinimized) {
345 $dashletID = (int) $dashletID;
346 $query = "INSERT INTO civicrm_dashboard_contact
347 (weight, column_no, is_active, dashboard_id, contact_id)
348 VALUES({$weight}, {$colNo}, 1, {$dashletID}, {$contactID})
349 ON DUPLICATE KEY UPDATE weight = {$weight}, column_no = {$colNo}, is_active = 1";
350 // fire update query for each column
351 CRM_Core_DAO::executeQuery($query);
352
353 $dashletIDs[] = $dashletID;
354 $weight++;
355 }
356 }
357 }
358
359 // Disable inactive widgets
360 $dashletClause = $dashletIDs ? "dashboard_id NOT IN (" . implode(',', $dashletIDs) . ")" : '(1)';
361 $updateQuery = "UPDATE civicrm_dashboard_contact
362 SET is_active = 0
363 WHERE $dashletClause AND contact_id = {$contactID}";
364
365 CRM_Core_DAO::executeQuery($updateQuery);
366 }
367
368 /**
369 * Add dashlets.
370 *
371 * @param array $params
372 *
373 * @return object
374 * $dashlet returns dashlet object
375 */
376 public static function addDashlet(&$params) {
377
378 // special case to handle duplicate entries for report instances
379 $dashboardID = CRM_Utils_Array::value('id', $params);
380
381 if (!empty($params['instanceURL'])) {
382 $query = "SELECT id
383 FROM `civicrm_dashboard`
384 WHERE url LIKE '" . CRM_Utils_Array::value('instanceURL', $params) . "&%'";
385 $dashboardID = CRM_Core_DAO::singleValueQuery($query);
386 }
387
388 $dashlet = new CRM_Core_DAO_Dashboard();
389
390 if (!$dashboardID) {
391 // check url is same as exiting entries, if yes just update existing
392 if (!empty($params['name'])) {
393 $dashlet->name = CRM_Utils_Array::value('name', $params);
394 $dashlet->find(TRUE);
395 }
396 else {
397 $dashlet->url = CRM_Utils_Array::value('url', $params);
398 $dashlet->find(TRUE);
399 }
400 if (empty($params['domain_id'])) {
401 $dashlet->domain_id = CRM_Core_Config::domainID();
402 }
403 }
404 else {
405 $dashlet->id = $dashboardID;
406 }
407
408 if (is_array(CRM_Utils_Array::value('permission', $params))) {
409 $params['permission'] = implode(',', $params['permission']);
410 }
411 $dashlet->copyValues($params);
412 $dashlet->save();
413
414 // now we need to make dashlet entries for each contact
415 self::addContactDashlet($dashlet);
416
417 return $dashlet;
418 }
419
420 /**
421 * Update contact dashboard with new dashlet.
422 *
423 * @param object $dashlet
424 */
425 public static function addContactDashlet($dashlet) {
426 $admin = CRM_Core_Permission::check('administer CiviCRM');
427
428 // if dashlet is created by admin then you need to add it all contacts.
429 // else just add to contact who is creating this dashlet
430 $contactIDs = [];
431 if ($admin) {
432 $query = "SELECT distinct( contact_id )
433 FROM civicrm_dashboard_contact";
434 $dao = CRM_Core_DAO::executeQuery($query);
435 while ($dao->fetch()) {
436 $contactIDs[$dao->contact_id] = NULL;
437 }
438 }
439 else {
440 //Get the id of Logged in User
441 $contactID = CRM_Core_Session::getLoggedInContactID();
442 if (!empty($contactID)) {
443 $contactIDs[$contactID] = NULL;
444 }
445 }
446
447 // Remove contact ids that already have this dashlet to avoid DB
448 // constraint violation.
449 $query = "SELECT distinct( contact_id )
450 FROM civicrm_dashboard_contact WHERE dashboard_id = {$dashlet->id}";
451 $dao = CRM_Core_DAO::executeQuery($query);
452 while ($dao->fetch()) {
453 if (array_key_exists($dao->contact_id, $contactIDs)) {
454 unset($contactIDs[$dao->contact_id]);
455 }
456 }
457 if (!empty($contactIDs)) {
458 foreach ($contactIDs as $contactID => $value) {
459 $valuesArray[] = " ( {$dashlet->id}, {$contactID} )";
460 }
461 $valuesString = implode(',', $valuesArray);
462 $query = "
463 INSERT INTO civicrm_dashboard_contact ( dashboard_id, contact_id )
464 VALUES {$valuesString}";
465
466 CRM_Core_DAO::executeQuery($query);
467 }
468 }
469
470 /**
471 * @param array $params
472 * Each item is a spec for a dashlet on the contact's dashboard.
473 * @return bool
474 */
475 public static function addContactDashletToDashboard(&$params) {
476 $valuesString = NULL;
477 $columns = [];
478 foreach ($params as $dashboardIDs) {
479 $contactID = CRM_Utils_Array::value('contact_id', $dashboardIDs);
480 $dashboardID = CRM_Utils_Array::value('dashboard_id', $dashboardIDs);
481 $column = CRM_Utils_Array::value('column_no', $dashboardIDs, 0);
482 $columns[$column][$dashboardID] = 0;
483 }
484 self::saveDashletChanges($columns, $contactID);
485 return TRUE;
486 }
487
488 /**
489 * Delete Dashlet.
490 *
491 * @param int $dashletID
492 *
493 * @return bool
494 */
495 public static function deleteDashlet($dashletID) {
496 $dashlet = new CRM_Core_DAO_Dashboard();
497 $dashlet->id = $dashletID;
498 if (!$dashlet->find(TRUE)) {
499 return FALSE;
500 }
501 $dashlet->delete();
502 return TRUE;
503 }
504
505 }