rray(); } $criteria = $this->_criteria; if ($include_parent) $criteria = array_merge($criteria, parent::getCriteria($include_parent)); return $criteria; } function getSupplementalCriteria() { return $this->getCriteria(false); } function useStandardColumns() { $this->getSettings(); if ($this->getCustomColumns() && isset($this->_settings['inherit-columns'])) return $this->_settings['inherit-columns']; // owner?? edit away. if ($this->_config && $this->_config->staff_id == $this->staff_id) return false; return parent::useStandardColumns(); } function inheritColumns() { if ($this->getSettings() && isset($this->_settings['inherit-columns'])) return $this->_settings['inherit-columns']; return parent::inheritColumns(); } function getStandardColumns() { return parent::getColumns(is_null($this->parent)); } function getColumns($use_template=false) { if (!$this->useStandardColumns() && ($columns=$this->getCustomColumns())) return $columns; return parent::getColumns($use_template); } function update($vars, &$errors=array()) { global $thisstaff; if (!$this->checkAccess($thisstaff)) return false; if ($this->checkOwnership($thisstaff)) { // Owner of the queue - can update everything if (!parent::update($vars, $errors)) return false; // Personal queues _always_ inherit from their parent $this->setFlag(self::FLAG_INHERIT_CRITERIA, $this->parent_id > 0); return true; } // Agent's config for public queue. if (!$this->_config) $this->_config = QueueConfig::create(array( 'queue_id' => $this->getId(), 'staff_id' => $thisstaff->getId())); // Validate & isolate supplemental criteria (if any) $vars['criteria'] = array(); if (isset($vars['fields'])) { $form = $this->getForm($vars, $thisstaff); if ($form->isValid()) { $criteria = self::isolateCriteria($form->getClean(), $this->getRoot()); $allowed = $this->getSupplementalMatches(); foreach ($criteria as $k => $c) if (!isset($allowed[$c[0]])) unset($criteria[$k]); $vars['criteria'] = $criteria ?: array(); } else { $errors['criteria'] = __('Validation errors exist on supplimental criteria'); } } if (!$errors && $this->_config->update($vars, $errors)) { // reset settings $this->_settings = $this->_criteria = null; // Reset chached queue options unset($_SESSION['sort'][$this->getId()]); } return (!$errors); } function getTotal($agent=null) { $query = $this->getQuery(); if ($agent) $query = $agent->applyVisibility($query); $query->filter(Q::any([ 'ticket_pid__isnull' => true, 'flags__hasbit' => Ticket::FLAG_LINKED ]))->limit(false)->offset(false)->order_by(false); try { return $query->count(); } catch (Exception $e) { return null; } } function getCount($agent, $cached=true) { $count = null; if ($cached && ($counts = self::counts($agent, $cached))) $count = $counts["q{$this->getId()}"]; if ($count == null) $count = $this->getTotal($agent); return $count; } // Get ticket counts for queues the agent has acces to. static function counts($agent, $cached=true, $criteria=array()) { if (!$agent instanceof Staff) return null; // Cache TLS in seconds $ttl = 5*60; // Cache key based on agent and salt of the installation $key = "counts.queues.{$agent->getId()}.".SECRET_SALT; if ($criteria && is_array($criteria)) // Consider additional criteria. $key .= '.'.md5(serialize($criteria)); // only consider cache if requesed if ($cached && ($counts=self::getCounts($key, $ttl))) return $counts; $queues = static::objects() ->filter(Q::any(array( 'flags__hasbit' => CustomQueue::FLAG_QUEUE, 'staff_id' => $agent->getId(), ))) ->filter(Q::not(array( 'flags__hasbit' => CustomQueue::FLAG_DISABLED, ))); if ($criteria && is_array($criteria)) $queues->filter($criteria); $counts = array(); $query = Ticket::objects(); // Apply tickets visibility for the agent $query = $agent->applyVisibility($query, true); // Aggregate constraints foreach ($queues as $queue) { $Q = $queue->getBasicQuery(); // only get counts for regular tickets (not children tickets) unless // queue is a saved search if ($queue->isAQueue() || $queue->isASubQueue()) { $reg = Q::any(array('thread__object_type' => 'T')); $Q->constraints[] = $reg; } if ($Q->constraints) { $empty = false; if (count($Q->constraints) > 1) { foreach ($Q->constraints as $value) { if (!$value->constraints) $empty = true; } } } // Add extra tables joins (if any) if ($Q->extra && isset($Q->extra['tables'])) { // skip counting keyword searches. Display them as '-' $counts['q'.$queue->getId()] = '-'; continue; $contraints = array(); if ($Q->constraints) $constraints = new Q($Q->constraints); foreach ($Q->extra['tables'] as $T) $query->addExtraJoin(array($T, $constraints, '')); } if ($Q->constraints && !$empty) { $constraints = $Q->constraints; // Add path_constraints to get the correct counts foreach ($Q->path_constraints as $pc) { if (!empty($pc[0]->constraints)) $constraints[] = $pc[0]; } $expr = SqlCase::N()->when(new SqlExpr(new Q($constraints)), new SqlField('ticket_id')); $query->aggregate(array( "q{$queue->id}" => SqlAggregate::COUNT($expr, true) )); } else //display skipped counts as '-' $counts['q'.$queue->getId()] = '-'; } try { $counts = array_merge($counts, $query->values()->one()); } catch (Exception $ex) { foreach ($queues as $q) $counts['q'.$q->getId()] = $q->getTotal(); } // Always cache the results self::storeCounts($key, $counts, $ttl); return $counts; } static function getCounts($key, $ttl) { if (!$key) { return array(); } elseif (function_exists('apcu_store')) { $found = false; $counts = apcu_fetch($key, $found); if ($found === true) return $counts; } elseif (isset($_SESSION['qcounts'][$key]) && (time() - $_SESSION['qcounts'][$key]['time']) < $ttl) { return $_SESSION['qcounts'][$key]['counts']; } else { // Auto clear missed session cache (if any) unset($_SESSION['qcounts'][$key]); } } static function storeCounts($key, $counts, $ttl) { if (function_exists('apcu_store')) { apcu_store($key, $counts, $ttl); } else { // Poor man's cache $_SESSION['qcounts'][$key]['counts'] = $counts; $_SESSION['qcounts'][$key]['time'] = time(); } } static function clearCounts() { if (function_exists('apcu_store')) { if (class_exists('APCUIterator')) { $regex = '/^counts.queues.\d+.' . preg_quote(SECRET_SALT, '/') . '$/'; foreach (new APCUIterator($regex, APC_ITER_KEY) as $key) { apcu_delete($key); } } // Also clear rough counts apcu_delete("rough.counts.".SECRET_SALT); } } static function lookup($criteria) { $queue = parent::lookup($criteria); // Annoted cusom settings (if any) if (($c=$queue->_config)) { $queue->_settings = $c->getSettings() ?: array(); $queue = AnnotatedModel::wrap($queue, array_intersect_key($queue->_settings, array_flip(array('sort_id', 'filter')))); $queue->_config = $c; } return $queue; } static function create($vars=false) { $search = parent::create($vars); $search->clearFlag(self::FLAG_QUEUE); return $search; } } class SavedSearch extends SavedQueue { function isSaved() { return (!$this->__new__); } function getCount($agent, $cached=true) { return 500; } } class AdhocSearch extends SavedSearch { function isSaved() { return false; } function isOwner(Staff $staff) { return $this->ht['staff_id'] == $staff->getId(); } function checkAccess(Staff $staff) { return true; } function getName() { return $this->title ?: $this->describeCriteria(); } static function load($key) { global $thisstaff; if (strpos($key, 'adhoc') === 0) list(, $key) = explode(',', $key, 2); if (!$key || !isset($_SESSION['advsearch']) || !($config=$_SESSION['advsearch'][$key])) return null; $queue = new AdhocSearch(array( 'id' => "adhoc,$key", 'root' => 'T', 'staff_id' => $thisstaff->getId(), 'title' => __('Advanced Search'), )); $queue->config = $config; return $queue; } } // AdvacedSearchForm class AdvancedSearchForm extends SimpleForm { static $id = 1337; function getNumFieldsSelected() { $selected = 0; foreach ($this->getFields() as $F) { if (substr($F->get('name'), -7) == '+search' && $F->getClean()) $selected += 1; // Consider keyword searches elseif ($F->get('name') == ':keywords' && $F->getClean()) $selected += 1; } return $selected; } } // Advanced search special fields class AdvancedSearchSelectionField extends ChoiceField { function hasIdValue() { return false; } function getSearchQ($method, $value, $name=false) { switch ($method) { case 'includes': case '!includes': if (!$value) return; $Q = new Q(); if (count($value) > 1) $Q->add(array("{$name}__in" => array_keys($value))); else $Q->add(array($name => key($value))); if ($method == '!includes') $Q->negate(); return $Q; break; // osTicket commonly uses `0` to represent an unset state, so // the set and unset checks need to check for both not null and // nonzero case 'nset': return new Q([$name => 0]); case 'set': return Q::not([$name => 0]); default: return parent::getSearchQ($method, $value, $name); } } } class HelpTopicChoiceField extends AdvancedSearchSelectionField { static $_topics; function hasIdValue() { return true; } function getChoices($verbose=false, $options=array()) { global $thisstaff; if (!isset($this->_topics)) { $this->_topics = $thisstaff ? $thisstaff->getTopicNames(false, Topic::DISPLAY_DISABLED) : Topic::getHelpTopics(false, Topic::DISPLAY_DISABLED);; } return $this->_topics; } } class SLAChoiceField extends AdvancedSearchSelectionField { static $_slas; function hasIdValue() { return true; } function getChoices($verbose=false, $options=array()) { if (!isset($this->_slas)) $this->_slas = SLA::getSLAs(array('nameOnly' => true)); return $this->_slas; } } require_once INCLUDE_DIR . 'class.dept.php'; class DepartmentChoiceField extends AdvancedSearchSelectionField { static $_depts; static $_alldepts; var $_choices; function getDepts($criteria=array()) { global $thisstaff; $staff = $criteria['staff']; $depts = array(); if ($staff) foreach ($staff->getDepartmentNames(true) as $id => $name) $depts[$id] = $name; else foreach (Dept::getDepartments() as $id => $name) $depts[$id] = $name; return $depts; } function getChoices($verbose=false, $options=array()) { global $thisstaff; $config = $this->getConfiguration(); $criteria = array( 'staff' => $config['staff'] ?: $thisstaff ); if (!isset($this->_choices)) $this->_choices = $this->getDepts($criteria); return $this->_choices; } function toString($value) { if (!isset($this->_alldepts)) $this->_alldepts = $this->getDepts(); $choices = $this->_alldepts; $selection = array(); if (!is_array($value)) $value = array($value => $value); foreach ($value as $k => $v) if (isset($choices[$k])) $selection[] = $choices[$k]; return $selection ? implode(',', $selection) : parent::toString($value); } function getQuickFilterChoices() { global $thisstaff; if (!isset($this->_choices)) { $depts = $thisstaff ? $thisstaff->getDepts() : array(); foreach ($this->getChoices() as $id => $name) { if (!$depts || in_array($id, $depts)) $this->_choices[$id] = $name; } } return $this->_choices; } function getSearchMethods() { return array( 'includes' => __('is'), '!includes' => __('is not'), ); } function addToQuery($query, $name=false) { return $query->values('dept_id', 'dept__name'); } function applyOrderBy($query, $reverse=false, $name=false) { $reverse = $reverse ? '-' : ''; return $query->order_by("{$reverse}dept__name"); } } class AssigneeChoiceField extends ChoiceField { protected $_items; function getChoices($verbose=false, $options=array()) { global $thisstaff; if (!isset($this->_items)) { $items = array( 'M' => __('Me'), 'T' => __('One of my teams'), ); $assignees = Staff::getStaffMembers(array('staff' => $thisstaff)); foreach ($assignees as $id=>$name) { // Don't include $thisstaff (since that's 'Me') if ($thisstaff && $thisstaff->getId() == $id) continue; $items['s' . $id] = $name; } foreach (Team::getTeams() as $id=>$name) { $items['t' . $id] = $name; } $this->_items = $items; } return $this->_items; } function getChoice($k) { $choices = $this->getChoices(); return $choices[$k] ?: null; } function getSearchMethods() { return array( 'assigned' => __('assigned'), '!assigned' => __('unassi=j­ÇtJH‰\$pH‰L$x¶PöÂuH‹P �ëè¹QH‹L$xH‹\$pH‰ÂH‹D$hI‹v0H‹¶ÈH‰t$P„1ÿE1ÀëHƒÄX]ÃHƒÇ@H9x†±H÷Ç?u D¶HÿÂëAÑèAºàsÒL� L�L‹ž˜IƒÃ�fDL9ž sKH‰|$D‰D$L‰L$HL‰T$@H‰T$0èÙH‹D$hH‹L$xH‹T$0H‹\$pH‹t$PH‹|$D‹D$L‹L$HL‹T$@L‹ž˜M‰ÜIƒÄL‰¦˜M‹ M‰ M‹ M‰Ké=ÿÿÿHƒÄX]ÃH‰¼$€èROH‰D$8H‰\$(H‹L$hH‹ H‰L$ èŒH� §o»(è…”H‹D$8H‹\$(èv”H�µ n» èe”H‹D$ 蛑H�•bn»èJ”H‹„$€f�è{‘èö�èŒH�„jo»$DèÛÓH�³o»)èÊÓ�ÌÌÌÌÌÌÌÌÌI;f†¨UH‰åHƒì0H‰D$@HÁëH�CHÁèH‰D$HÿÀH‰D$ »H� üÈ@èûâÿÿ„H‹L$ H�ù@wRH‹T$H9Ês@H‰D$(Æ¡H‰ÃH‹D$@èkH‹\$(H‹L$¶ €ù¡uHƒÄ0]ÃH�ÜÝn»è!ÓH‰ÐèYPº@è¯P�H‰D$H‰\$@è;/H‹D$H‹\$é,ÿÿÿÌÌÌÌÌÌÌÌÌÌÌÌUH‰åH‰Ù1Ò1öéL‰áH‰ÚL‰ÈL‰Ó@éþ¶8�I‰øƒçL�HAºàrfDH…ÿ„åHÁï1ÀéXH…ÿu 1À1ÿéÒ1ÀE1ÀéáH¯ÇHƒÿ9w�L�CÿH‰ÑI‰òéOH)×L�GIÁèƒçI‰ÚL)ÃI‰ØH…ÿt#D¶H�OøH÷ÙIÓëH‰ÑIÓãI�XL Þ�H�H)øH‰ÁHÁèéãƒáH…Év0¶¿HÓçHÿÏH!ßH‰ÈH‰ÑHÓçH�Hƒù@MÀL!ÇH þéÿÿÿH‰Óéüþÿÿ@ˆ3�HƒÂøHÁîHÿÃHƒúsëé÷þÿÿH‰ßH)ÏH‰ÑH÷ÚƒâH�ùHÑé7L�AL‰áE¶M‰ÓAƒâI‰ÌL‰ÁIÓâIÿÁ�Hƒù@MíM!êL ÐAºãrÍ@éêþÿÿ¶;I‰ÈH‰ÑHÓç�HÿÈHÿÃM�ZHƒù@MíL!ïH þAˆ2�HÁîL‰ÁM‰ÚH…ÀwÉ@éÿÿÿHÁæE¶L Þ�HƒÂIÿÈH9úrèvH)úI‰ÈH‰ÑHÓîHƒù@MÛL!Þë I‰ÈH‰×HƒÿuHƒþu¹9H¾ÿÿÿÿÿÿÿë(H‰Áë#H�?Hƒú9wH‰ùéµH‰ùë H)ÐH‰ÑH‰þM‰ØH9ÁwH‰ÊL‰ÁH‰÷HÓæL� Hƒù@MÀL!ÆI òëYH…Àv4H‰ÁºHÓâI�<Hƒø@MÛL!ÚHÿÊH!ÖL‰ÁHÓæHƒù@HÒH!ÖI òëL‰ÇL‰ÖI‰ÚH‰ûé]ýÿÿDˆ�IÁêIƒÃøHÿÃIƒûsëélÿÿÿHÒI‰ËH‰ùI‰õIÓåI!ÕL îH�< L‰ÙHƒÿ@vÞH…Ét7H‰Â¸9H‰×1ÒH÷ñH¯ÈºHÓâHƒù@MÛL!ÚHÿÊH!ÖH‰øH‰Ïé ÿÿÿèðJH�yL‰ÙE¶M‰ÂAƒàI‰ËH‰ùIÓàIÿÁ�Hƒù@MäM!ÄL àAºârÍL‰ÙH‰Çé÷üÿÿ@ˆ3�HƒÁøHÁîHÿÃH…Éwé]ÃE¶I‰ËH‰ÑIÓâL Ö�@ˆ3�HÁîHÿÀIÿÁHÿÃL‰ÙH9ørÖAƒàM…ÀvA¶9H‰ÈH‰ÑHÓçH þ�I�IÿÁëH‰ÈI‰ÄI‰ÚH‰Óé+üÿÿÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌI;f†¡UH‰åHƒì HÇD$H�žH‰D$H�D$H‰D$H�D$H‰$èa)EWÿdL‹4%øÿÿÿ1ÀëH‹L$„H��ÅH‰TÁ0HÿÀH=ˆ|âH‹C¾H…Àu H¹ÿÿÿÿÿÿÿëHƒøu1Éëè«ÜÿÿHcÈH‹T$H‰ H‹D$HƒÄ ]Ãèp*éKÿÿÿÌÌÌÌÌÌÌÌÌÌÌI;fvSUH‰åHƒìH‹JH‰L$��H� 3Æè»°ÿÿH�ü›ÇèïcH‹L$H‰‹ A4LJˆ°��H�ò2Æèí´ÿÿHƒÄ]Ãèb)f�ëžÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌI;f†"UH‰åHƒì0„¶Óf„H�úˆƒñH‹LÐ0·q`f„�f9q2…ÃH‰D$@H‰T$(H�5Å�ÅDH9ñ„Ò‹5‘3ǃÆ9qX…�ˆ\$H