3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 use Civi\Api4\DashboardContact
;
21 * Class contains Contact dashboard related functions.
23 class CRM_Core_BAO_Dashboard
extends CRM_Core_DAO_Dashboard
{
26 * Create or update Dashboard.
28 * @param array $params
30 * @return CRM_Core_DAO_Dashboard
32 public static function create($params) {
33 $hook = empty($params['id']) ?
'create' : 'edit';
34 CRM_Utils_Hook
::pre($hook, 'Dashboard', CRM_Utils_Array
::value('id', $params), $params);
35 $dao = self
::addDashlet($params);
36 CRM_Utils_Hook
::post($hook, 'Dashboard', $dao->id
, $dao);
41 * Get all available contact dashlets
45 * @throws \API_Exception
47 public static function getContactDashlets(): array {
48 $cid = CRM_Core_Session
::getLoggedInContactID();
49 if ($cid && !isset(Civi
::$statics[__CLASS__
][__FUNCTION__
][$cid])) {
50 Civi
::$statics[__CLASS__
][__FUNCTION__
][$cid] = [];
51 // If empty, then initialize default dashlets for this user.
52 if (0 === DashboardContact
::get(FALSE)->selectRowCount()->addWhere('contact_id', '=', $cid)->execute()->count()) {
53 self
::initializeDashlets();
55 $contactDashboards = (array) DashboardContact
::get(FALSE)
56 ->addSelect('column_no', 'is_active', 'dashboard_id', 'weight', 'contact_id')
57 ->addWhere('contact_id', '=', $cid)
58 ->addOrderBy('weight')
59 ->execute()->indexBy('dashboard_id');
62 'select' => ['*', 'dashboard_contact.*'],
64 ['domain_id', '=', 'current_domain'],
68 // Get Dashboard + any joined DashboardContact records.
69 $results = (array) civicrm_api4('Dashboard', 'get', $params);
70 foreach ($results as $item) {
71 $item['dashboard_contact.id'] = $contactDashboards[$item['id']]['id'] ??
NULL;
72 $item['dashboard_contact.contact_id'] = $contactDashboards[$item['id']]['contact_id'] ??
NULL;
73 $item['dashboard_contact.weight'] = $contactDashboards[$item['id']]['weight'] ??
NULL;
74 $item['dashboard_contact.column_no'] = $contactDashboards[$item['id']]['column_no'] ??
NULL;
75 $item['dashboard_contact.is_active'] = $contactDashboards[$item['id']]['is_active'] ??
NULL;
76 if ($item['is_active'] && self
::checkPermission($item['permission'], $item['permission_operator'])) {
77 Civi
::$statics[__CLASS__
][__FUNCTION__
][$cid][] = $item;
80 usort(Civi
::$statics[__CLASS__
][__FUNCTION__
][$cid], static function ($a, $b) {
81 // Sort by dashboard contact weight, preferring not null to null.
82 // I had hoped to do this in mysql either by
83 // 1) making the dashboard contact part of the query NOT permissioned while
84 // the parent query IS or
85 // 2) using FIELD like
86 // $params['orderBy'] = ['FIELD(id,' . implode(',', array_keys($contactDashboards)) . ')' => 'ASC'];
87 // 3) or making the dashboard contact acl more inclusive such that 'view own contact'
88 // is not required to view own contact's acl
89 // but I couldn't see a way to make any of the above work. Perhaps improve in master?
90 if (!isset($b['dashboard_contact.weight']) && !isset($a[$b['dashboard_contact.weight']])) {
93 if (!isset($b['dashboard_contact.weight'])) {
96 if (!isset($a['dashboard_contact.weight'])) {
99 return $a['dashboard_contact.weight'] <=> $b['dashboard_contact.weight'];
102 return Civi
::$statics[__CLASS__
][__FUNCTION__
][$cid] ??
[];
106 * Set default dashlets for new users.
108 * Called when a user accesses their dashboard for the first time.
110 public static function initializeDashlets() {
111 $allDashlets = (array) civicrm_api4('Dashboard', 'get', [
112 'where' => [['domain_id', '=', 'current_domain']],
114 $defaultDashlets = [];
115 $defaults = ['blog' => 1, 'getting-started' => '0'];
116 foreach ($defaults as $name => $column) {
117 if (!empty($allDashlets[$name]) && !empty($allDashlets[$name]['id'])) {
118 $defaultDashlets[$name] = [
119 'dashboard_id' => $allDashlets[$name]['id'],
121 'column_no' => $column,
125 CRM_Utils_Hook
::dashboard_defaults($allDashlets, $defaultDashlets);
126 if (is_array($defaultDashlets) && !empty($defaultDashlets)) {
127 DashboardContact
::save(FALSE)
128 ->setRecords($defaultDashlets)
129 ->setDefaults(['contact_id' => CRM_Core_Session
::getLoggedInContactID()])
135 * Check dashlet permission for current user.
137 * @param array|null $permissions
138 * @param string|null $operator
141 * true if user has permission to view dashlet
143 private static function checkPermission(?
array $permissions, ?
string $operator): bool {
145 static $allComponents;
146 if (!$allComponents) {
147 $allComponents = CRM_Core_Component
::getNames();
150 $hasPermission = FALSE;
151 foreach ($permissions as $key) {
154 $componentName = CRM_Core_Permission
::getComponentName($key);
156 // If the permission depends on a component, ensure it is enabled
157 if ($componentName) {
158 if (!CRM_Core_Component
::isEnabled($componentName) ||
!CRM_Core_Permission
::check($key)) {
159 $showDashlet = FALSE;
160 if ($operator == 'AND') {
165 $hasPermission = TRUE;
168 elseif (!CRM_Core_Permission
::check($key)) {
169 $showDashlet = FALSE;
170 if ($operator == 'AND') {
175 $hasPermission = TRUE;
179 return $showDashlet ||
$hasPermission;
181 // If permission is not set consider everyone has access.
188 * @param array $params
191 * $dashlet returns dashlet object
193 public static function addDashlet(&$params) {
195 // special case to handle duplicate entries for report instances
196 $dashboardID = $params['id'] ??
NULL;
198 if (!empty($params['instanceURL'])) {
200 FROM `civicrm_dashboard`
201 WHERE url LIKE '" . CRM_Utils_Array
::value('instanceURL', $params) . "&%'";
202 $dashboardID = CRM_Core_DAO
::singleValueQuery($query);
205 $dashlet = new CRM_Core_DAO_Dashboard();
208 // Assign domain before search to allow identical dashlets in different domains.
209 $dashlet->domain_id
= $params['domain_id'] ?? CRM_Core_Config
::domainID();
211 // Try and find an existing dashlet - it will be updated if found.
212 if (!empty($params['name'])) {
213 $dashlet->name
= $params['name'] ??
NULL;
214 $dashlet->find(TRUE);
218 $dashlet->id
= $dashboardID;
221 $dashlet->copyValues($params);
224 // now we need to make dashlet entries for each contact
225 self
::addContactDashlet($dashlet);
231 * Update contact dashboard with new dashlet.
233 * @param object $dashlet
235 public static function addContactDashlet($dashlet) {
236 $admin = CRM_Core_Permission
::check('administer CiviCRM');
238 // if dashlet is created by admin then you need to add it all contacts.
239 // else just add to contact who is creating this dashlet
242 $query = "SELECT distinct( contact_id )
243 FROM civicrm_dashboard_contact";
244 $dao = CRM_Core_DAO
::executeQuery($query);
245 while ($dao->fetch()) {
246 $contactIDs[$dao->contact_id
] = NULL;
250 //Get the id of Logged in User
251 $contactID = CRM_Core_Session
::getLoggedInContactID();
252 if (!empty($contactID)) {
253 $contactIDs[$contactID] = NULL;
257 // Remove contact ids that already have this dashlet to avoid DB
258 // constraint violation.
259 $query = "SELECT distinct( contact_id )
260 FROM civicrm_dashboard_contact WHERE dashboard_id = {$dashlet->id}";
261 $dao = CRM_Core_DAO
::executeQuery($query);
262 while ($dao->fetch()) {
263 if (array_key_exists($dao->contact_id
, $contactIDs)) {
264 unset($contactIDs[$dao->contact_id
]);
267 if (!empty($contactIDs)) {
268 foreach ($contactIDs as $contactID => $value) {
269 $valuesArray[] = " ( {$dashlet->id}, {$contactID} )";
271 $valuesString = implode(',', $valuesArray);
273 INSERT INTO civicrm_dashboard_contact ( dashboard_id, contact_id )
274 VALUES {$valuesString}";
276 CRM_Core_DAO
::executeQuery($query);