= 2; $can_delete = lookupUserPermission('module_client') >= 3; $can_credentials = lookupUserPermission('module_credential') >= 1; $can_support = lookupUserPermission('module_support') >= 1; // The roles an MSP reaches for $contact_role_tabs = [ 'important' => ['label' => 'Important', 'icon' => 'fa-star', 'query' => "contact_important = 1"], 'billing' => ['label' => 'Billing', 'icon' => 'fa-file-invoice-dollar', 'query' => "contact_billing = 1"], 'technical' => ['label' => 'Technical', 'icon' => 'fa-tools', 'query' => "contact_technical = 1"], 'portal' => ['label' => 'Portal', 'icon' => 'fa-user-circle', 'query' => "contact_user_id > 0 AND COALESCE(user_auth_method, '') != ''"], ]; // Invites to the portal waiting to be accepted - their own tab, with its own list $invites_on = portalInvitesReady() && intval($config_client_portal_enable) === 1; $invites_view = $invites_on && ($_GET['role'] ?? '') === 'invites'; $role_filter = (isset($_GET['role']) && isset($contact_role_tabs[$_GET['role']])) ? $_GET['role'] : ''; $_GET['role'] = $role_filter; $role_query = $role_filter ? $contact_role_tabs[$role_filter]['query'] : "1 = 1"; // Tags Filter if (isset($_GET['tags']) && is_array($_GET['tags']) && !empty($_GET['tags'])) { $tag_filter = implode(",", array_map('intval', $_GET['tags'])); $tag_query = "AND contacts.contact_id IN (SELECT contact_tags.contact_id FROM contact_tags WHERE contact_tags.tag_id IN ($tag_filter))"; } else { $tag_filter = 0; $tag_query = ''; } $client = ''; if (!$client_url) { // Client Filter if (!empty($_GET['client'])) { $client = intval($_GET['client']); $client_query = "AND contact_client_id = $client"; } } if ($client_url && !empty($_GET['location'])) { // Location Filter $location_filter = intval($_GET['location']); $location_query = "AND contact_location_id = $location_filter"; } else { // Default - any $location_query = ''; $location_filter = 0; } // Only these sort - filter_header.php will hand through any lowercase word, and // an unknown column takes the page down with a mysqli exception $sortable_columns = ['contact_name', 'contact_title', 'contact_department', 'contact_email', 'location_name', 'client_name', 'contact_created_at']; if ($invites_view) { // Invites - newest first unless asked if (!in_array($sort, ['invite_email', 'invite_created_at', 'client_name'], true)) { $sort = 'invite_created_at'; $order = isset($_GET['order']) ? $order : 'DESC'; } } elseif (!in_array($sort, $sortable_columns, true)) { $sort = 'contact_name'; } // The invites tab's count - waiting, in date or run out - in this client or filter, for clients still here $invites_scope_sql = clientScopeSql('invite_client_id') . ($client_url ? " AND invite_client_id = $client_id" : ($client ? " AND invite_client_id = " . intval($client) : '')); $invites_count = $invites_on ? intval(mysqli_fetch_row(mysqli_query($mysqli, "SELECT COUNT(*) FROM portal_invites JOIN clients ON client_id = invite_client_id WHERE invite_kind = 'invite' AND invite_accepted_at IS NULL AND invite_cancelled_at IS NULL AND client_archived_at IS NULL $invites_scope_sql"))[0]) : 0; $show_invites_tab = $invites_on && ($client_url || $invites_count || $invites_view); // Tab counts - within the client, location, tag and archive scope, before the search narrows it $row = mysqli_fetch_assoc(mysqli_query($mysqli, " SELECT COUNT(*) AS all_count, SUM(contact_important = 1) AS important_count, SUM(contact_billing = 1) AS billing_count, SUM(contact_technical = 1) AS technical_count, SUM(contact_user_id > 0 AND COALESCE(user_auth_method, '') != '') AS portal_count FROM contacts LEFT JOIN clients ON client_id = contact_client_id LEFT JOIN users ON user_id = contact_user_id WHERE $archive_query $tag_query " . clientScopeSql('contact_client_id') . " $client_query $location_query ")); $tab_counts = ['' => intval($row['all_count'])]; foreach (array_keys($contact_role_tabs) as $tab_key) { $tab_counts[$tab_key] = intval($row[$tab_key . '_count']); } $invites = []; if ($invites_view) { $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS invite_id, invite_email, invite_name, invite_billing, invite_technical, invite_expires_at, invite_sent_at, invite_sent_count, invite_created_at, invite_created_by, invite_created_by_contact_id, invite_kind, invite_accepted_at, invite_cancelled_at, invite_client_id, client_name, agent.user_name AS invite_by_agent, by_contact.contact_name AS invite_by_contact FROM portal_invites JOIN clients ON client_id = invite_client_id LEFT JOIN users agent ON agent.user_id = invite_created_by AND invite_created_by > 0 LEFT JOIN contacts by_contact ON by_contact.contact_id = invite_created_by_contact_id AND invite_created_by_contact_id > 0 WHERE invite_kind = 'invite' AND invite_accepted_at IS NULL AND invite_cancelled_at IS NULL AND client_archived_at IS NULL AND (invite_email LIKE '%$q%' OR invite_name LIKE '%$q%' OR client_name LIKE '%$q%') $invites_scope_sql ORDER BY $sort $order, invite_id DESC LIMIT $record_from, $record_to"); while ($row = mysqli_fetch_assoc($sql)) { $invites[] = $row; } } else { $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS contacts.contact_id, contact_archived_at, contact_billing, contact_client_id, contact_department, contact_email, contact_extension, contact_important, contact_location_id, contact_mobile, contact_mobile_country_code, contact_name, contact_phone, contact_phone_country_code, contact_photo, contact_primary, contact_technical, contact_title, contact_user_id, client_id, client_name, location_archived_at, location_name, user_auth_method FROM contacts LEFT JOIN clients ON client_id = contact_client_id LEFT JOIN locations ON location_id = contact_location_id LEFT JOIN users ON user_id = contact_user_id WHERE $archive_query $tag_query AND ($role_query) AND (contact_name LIKE '%$q%' OR contact_title LIKE '%$q%' OR location_name LIKE '%$q%' OR contact_email LIKE '%$q%' OR contact_department LIKE '%$q%' OR contact_phone LIKE '%$phone_query%' OR contact_extension LIKE '%$q%' OR contact_mobile LIKE '%$phone_query%' OR client_name LIKE '%$q%' OR contacts.contact_id IN (SELECT contact_tags.contact_id FROM contact_tags JOIN tags ON tags.tag_id = contact_tags.tag_id WHERE tag_name LIKE '%$q%')) " . clientScopeSql('contact_client_id') . " $client_query $location_query ORDER BY contact_primary DESC, contact_important DESC, $sort $order LIMIT $record_from, $record_to" ); } $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $contacts = []; while (!$invites_view && ($row = mysqli_fetch_assoc($sql))) { $contacts[intval($row['contact_id'])] = $row; } // Whether people at this client can sign up themselves, shown on its Invites tab $signup = null; if ($invites_view && $client_url) { $signup = [ 'on' => intval(mysqli_fetch_row(mysqli_query($mysqli, "SELECT client_portal_signup FROM clients WHERE client_id = $client_id"))[0] ?? 0) === 1, 'domains' => portalSignupDomains($client_id), 'everyone' => portalSignupOn(), ]; } // Tags and what is linked to each contact, for the whole page in a handful of // queries rather than five per row $contacts_tags = tagsForRecords('contact', array_keys($contacts)); $linked = ['assets' => [], 'credentials' => [], 'licenses' => [], 'tickets' => [], 'open_tickets' => []]; if ($contacts) { $ids = implode(',', array_keys($contacts)); $sql_linked = mysqli_query($mysqli, "SELECT asset_contact_id AS id, COUNT(*) AS n FROM assets WHERE asset_contact_id IN ($ids) AND asset_archived_at IS NULL GROUP BY asset_contact_id"); while ($row = mysqli_fetch_assoc($sql_linked)) { $linked['assets'][intval($row['id'])] = intval($row['n']); } if ($can_credentials) { $sql_linked = mysqli_query($mysqli, "SELECT credential_contact_id AS id, COUNT(*) AS n FROM credentials WHERE credential_contact_id IN ($ids) AND credential_archived_at IS NULL GROUP BY credential_contact_id"); while ($row = mysqli_fetch_assoc($sql_linked)) { $linked['credentials'][intval($row['id'])] = intval($row['n']); } } $sql_linked = mysqli_query($mysqli, "SELECT software_contacts.contact_id AS id, COUNT(*) AS n FROM software_contacts JOIN software ON software.software_id = software_contacts.software_id WHERE software_contacts.contact_id IN ($ids) AND software_archived_at IS NULL GROUP BY software_contacts.contact_id"); while ($row = mysqli_fetch_assoc($sql_linked)) { $linked['licenses'][intval($row['id'])] = intval($row['n']); } if ($can_support) { $sql_linked = mysqli_query($mysqli, "SELECT ticket_contact_id AS id, COUNT(*) AS n, SUM(ticket_resolved_at IS NULL AND ticket_closed_at IS NULL) AS open_n FROM tickets WHERE ticket_contact_id IN ($ids) AND ticket_archived_at IS NULL GROUP BY ticket_contact_id"); while ($row = mysqli_fetch_assoc($sql_linked)) { $linked['tickets'][intval($row['id'])] = intval($row['n']); $linked['open_tickets'][intval($row['id'])] = intval($row['open_n']); } } } $search_noun = $role_filter ? strtolower($contact_role_tabs[$role_filter]['label']) . ' contacts' : 'contacts'; $search_placeholder = $invites_view ? 'Search invites by email, name or client' : "Search $search_noun, phones, emails..."; ?>
| Invited | Sees | Sent | Link | Client | Actions |
|---|---|---|---|---|---|
|
= contactAvatar($invite_client_id, 0, $invite_name !== '' ? $invite_name : $row['invite_email'], '') ?>
= escapeHtml($row['invite_email']) ?>
= escapeHtml($invite_name) ?>
Sent = escapeHtml(timeAgo($row['invite_created_at'])) ?>
|
= $invite_sees ? escapeHtml(implode(', ', $invite_sees)) : 'Their own tickets' ?> |
= escapeHtml(timeAgo($row['invite_created_at'])) ?>
= $invite_by !== '' ? 'by ' . escapeHtml($invite_by) . (intval($row['invite_created_by_contact_id']) ? ' in the portal' : '') : '' ?>= intval($row['invite_sent_count']) > 1 ? ' · sent ' . intval($row['invite_sent_count']) . ' times' : '' ?>
|
= escapeHtml($invite_state['label']) ?> | = escapeHtml($row['client_name']) ?> |
|