From f6ccc3f6e35d501d295a85b4cd8e990d15f7661e Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Wed, 12 Aug 2015 08:17:37 -0500 Subject: [PATCH] orgs: Add ticket sharing feature This feature allows (by option) an organization to allow its members to see tickets from other members. It can be used beside or instead of the automated collaboration feature. Now, collaborators retain access to tickets on which they participate; however, sharing can be used to enable access to other tickets owned by their organization. --- include/class.nav.php | 2 +- include/class.organization.php | 23 ++++++++++++++++++ include/class.ticket.php | 12 ++++++++++ include/class.user.php | 6 +++++ include/client/tickets.inc.php | 25 +++++++++++++------- include/staff/templates/org-profile.tmpl.php | 18 +++++++++++++- tickets.php | 2 +- 7 files changed, 77 insertions(+), 11 deletions(-) diff --git a/include/class.nav.php b/include/class.nav.php index aafb4dc25..e69014817 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -344,7 +344,7 @@ class UserNav { $navs['new']=array('desc'=>__('Open a New Ticket'),'href'=>'open.php','title'=>''); if($user && $user->isValid()) { if(!$user->isGuest()) { - $navs['tickets']=array('desc'=>sprintf(__('Tickets (%d)'),$user->getNumTickets()), + $navs['tickets']=array('desc'=>sprintf(__('Tickets (%d)'),$user->getNumTickets($user->canSeeOrgTickets())), 'href'=>'tickets.php', 'title'=>__('Show all tickets')); } else { diff --git a/include/class.organization.php b/include/class.organization.php index 75024ec6b..aae39975f 100644 --- a/include/class.organization.php +++ b/include/class.organization.php @@ -35,6 +35,9 @@ class OrganizationModel extends VerySimpleModel { const COLLAB_PRIMARY_CONTACT = 0x0002; const ASSIGN_AGENT_MANAGER = 0x0004; + const SHARE_PRIMARY_CONTACT = 0x0008; + const SHARE_EVERYBODY = 0x0010; + const PERM_CREATE = 'org.create'; const PERM_EDIT = 'org.edit'; const PERM_DELETE = 'org.delete'; @@ -100,6 +103,14 @@ class OrganizationModel extends VerySimpleModel { return $this->check(self::ASSIGN_AGENT_MANAGER); } + function shareWithPrimaryContacts() { + return $this->check(self::SHARE_PRIMARY_CONTACT); + } + + function shareWithEverybody() { + return $this->check(self::SHARE_EVERYBODY); + } + function getUpdateDate() { return $this->updated; } @@ -206,6 +217,8 @@ implements TemplateVariable { 'collab-all-flag' => Organization::COLLAB_ALL_MEMBERS, 'collab-pc-flag' => Organization::COLLAB_PRIMARY_CONTACT, 'assign-am-flag' => Organization::ASSIGN_AGENT_MANAGER, + 'sharing-primary' => Organization::SHARE_PRIMARY_CONTACT, + 'sharing-all' => Organization::SHARE_EVERYBODY, ) as $ck=>$flag) { if ($this->check($flag)) $base[$ck] = true; @@ -393,6 +406,16 @@ implements TemplateVariable { $this->clearStatus($flag); } + foreach (array( + 'sharing-primary' => Organization::SHARE_PRIMARY_CONTACT, + 'sharing-all' => Organization::SHARE_EVERYBODY, + ) as $ck=>$flag) { + if ($vars['sharing'] == $ck) + $this->setStatus($flag); + else + $this->clearStatus($flag); + } + // Set staff and primary contacts $this->set('domain', $vars['domain']); $this->set('manager', $vars['manager'] ?: ''); diff --git a/include/class.ticket.php b/include/class.ticket.php index 55e3f9fbb..f849340c5 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -358,6 +358,18 @@ implements RestrictedAccess, Threadable { if ($user->getId() == $this->getUserId()) return true; + // Organization + if ($user->canSeeOrgTickets() + && ($U = $this->getUser()) + && ($U->getOrgId() == $user->getOrgId()) + ) { + // The owner of this ticket is in the same organization as the + // user in question, and the organization is configured to allow + // the user in question to see other tickets in the + // organization. + return true; + } + // Collaborator? // 1) If the user was authorized via this ticket. if ($user->getTicketId() == $this->getId() diff --git a/include/class.user.php b/include/class.user.php index 4fcb2c3ce..8d5b68368 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -432,6 +432,12 @@ implements TemplateVariable { return (string) $account->getStatus(); } + function canSeeOrgTickets() { + return $this->org && ( + $this->org->shareWithEverybody() + || ($this->isPrimaryContact() && $this->org->shareWithPrimaryContacts())); + } + function register($vars, &$errors) { // user already registered? diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php index 0b10183a9..32ad5abf8 100644 --- a/include/client/tickets.inc.php +++ b/include/client/tickets.inc.php @@ -16,17 +16,20 @@ if (isset($_REQUEST['status'])) { $settings['status'] = $_REQUEST['status']; } +$org_tickets = $thisclient->canSeeOrgTickets(); if ($settings['keywords']) { // Don't show stat counts for searches $openTickets = $closedTickets = -1; } elseif ($settings['topic_id']) { - $openTickets = $thisclient->getNumTopicTicketsInState($settings['topic_id'], 'open'); - $closedTickets = $thisclient->getNumTopicTicketsInState($settings['topic_id'], 'closed'); + $openTickets = $thisclient->getNumTopicTicketsInState($settings['topic_id'], + 'open', $org_tickets); + $closedTickets = $thisclient->getNumTopicTicketsInState($settings['topic_id'], + 'closed', $org_tickets); } else { - $openTickets = $thisclient->getNumOpenTickets(); - $closedTickets = $thisclient->getNumClosedTickets(); + $openTickets = $thisclient->getNumOpenTickets($org_tickets); + $closedTickets = $thisclient->getNumClosedTickets($org_tickets); } $tickets = TicketModel::objects(); @@ -63,10 +66,15 @@ $x=$sort.'_sort'; $$x=' class="'.strtolower($_REQUEST['order'] ?: 'desc').'" '; // Add visibility constraints -$tickets->filter(Q::any(array( +$visibility = Q::any(array( 'user_id' => $thisclient->getId(), 'thread__collaborators__user_id' => $thisclient->getId(), -))); +)); + +if ($thisclient->canSeeOrgTickets()) + $visibility->add(array('user__org_id' => $thisclient->getOrgId())); + +$tickets->filter($visibility); // Perform basic search if ($settings['keywords']) { @@ -122,8 +130,9 @@ $tickets->values( <?php echo __('Help Topic'); ?>: <select name="topic_id" class="nowarn" onchange="javascript: this.form.submit(); "> <option value="">— <?php echo __('All Help Topics');?> —</option> -<?php foreach (Topic::getHelpTopics(true) as $id=>$name) { - $count = $thisclient->getNumTopicTickets($id); +<?php +foreach (Topic::getHelpTopics(true) as $id=>$name) { + $count = $thisclient->getNumTopicTickets($id, $org_tickets); if ($count == 0) continue; ?> diff --git a/include/staff/templates/org-profile.tmpl.php b/include/staff/templates/org-profile.tmpl.php index 90420dab8..d22ba8bb4 100644 --- a/include/staff/templates/org-profile.tmpl.php +++ b/include/staff/templates/org-profile.tmpl.php @@ -98,6 +98,22 @@ if ($ticket && $ticket->getOwnerId() == $user->getId()) </select> <br/><span class="error"><?php echo $errors['contacts']; ?></span> </td> + </tr> + <tr> + <td width="180"> + <?php echo __('Ticket Sharing'); ?>: + </td> + <td> + <select name="sharing"> + <option value=""><?php echo __('Disable'); ?></option> + <option value="sharing-primary" <?php echo $info['sharing-primary'] ? 'selected="selected"' : ''; + ?>><?php echo __('Primary contacts see all tickets'); ?></option> + <option value="sharing-all" <?php echo $info['sharing-all'] ? 'selected="selected"' : ''; + ?>><?php echo __('All members see all tickets'); ?></option> + </select> + <i class="help-tip icon-question-sign" href="#org-sharing"></i> + </td> + </tr> <tr> <th colspan="2"> <?php echo __('Automated Collaboration'); ?>: @@ -123,7 +139,7 @@ if ($ticket && $ticket->getOwnerId() == $user->getId()) </tr> <tr> <th colspan="2"> - <?php echo __('Main Domain'); ?> + <?php echo __('Email Domain'); ?> </th> </tr> <tr> diff --git a/tickets.php b/tickets.php index 3467bc9ec..2c80ec91a 100644 --- a/tickets.php +++ b/tickets.php @@ -128,7 +128,7 @@ if($ticket && $ticket->checkUserAccess($thisclient)) { } else $inc='view.inc.php'; -} elseif($thisclient->getNumTickets()) { +} elseif($thisclient->getNumTickets($thisclient->canSeeOrgTickets())) { $inc='tickets.inc.php'; } else { $nav->setActiveNav('new'); -- GitLab