diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index b582110b0100c933334ed03592e3d2c8f6afddeb..a5fa4c6479f6f8c33ccb50abce33aca947f34dd6 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -452,11 +452,18 @@ class DynamicFormField extends VerySimpleModel { } function getField($cache=true) { + global $thisstaff; + + // Create the `required` flag for the FormField instance + $ht = $this->ht; + $ht['required'] = ($thisstaff) ? $this->isRequiredForStaff() + : $this->isRequiredForUsers(); + if (!$cache) - return new FormField($this->ht); + return new FormField($ht); if (!isset($this->_field)) - $this->_field = new FormField($this->ht); + $this->_field = new FormField($ht); return $this->_field; } @@ -538,11 +545,11 @@ class DynamicFormField extends VerySimpleModel { $hints[] = __('For EndUsers Only'); } if ($impl->hasData()) { - if (~$F & (self::FLAG_CLIENT_REQUIRED | self::FLAG_AGENT_REQUIRED)) { - $hints[] = __('Optional'); + if ($F & (self::FLAG_CLIENT_REQUIRED | self::FLAG_AGENT_REQUIRED)) { + $hints[] = __('Required'); } else { - $hints[] = __('Required'); + $hints[] = __('Optional'); } if (!($F & (self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT))) { $hints[] = __('Immutable'); @@ -613,7 +620,7 @@ class DynamicFormField extends VerySimpleModel { return false; $info = $modes[$mode]; - $this->set('flags', $info['flags']); + $this->set('flags', $info['flags'] | self::FLAG_ENABLED); } function isRequiredForStaff() { @@ -686,6 +693,7 @@ class DynamicFormField extends VerySimpleModel { static function create($ht=false) { $inst = parent::create($ht); $inst->set('created', new SqlFunction('NOW')); + $inst->flags = self::FLAG_ENABLED; if (isset($ht['configuration'])) $inst->configuration = JsonDataEncoder::encode($ht['configuration']); return $inst; diff --git a/include/class.nav.php b/include/class.nav.php index 551d6265be765983d602570863eda20733500dca..375d5254f2cbb48ce121dce8639dc392ff0e5fdc 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -232,7 +232,6 @@ class AdminNav extends StaffNav{ $subnav[]=array('desc'=>__('Company'),'href'=>'settings.php?t=pages','iconclass'=>'pages'); $subnav[]=array('desc'=>__('System'),'href'=>'settings.php?t=system','iconclass'=>'preferences'); $subnav[]=array('desc'=>__('Tickets'),'href'=>'settings.php?t=tickets','iconclass'=>'ticket-settings'); - $subnav[]=array('desc'=>__('Emails'),'href'=>'settings.php?t=emails','iconclass'=>'email-settings'); $subnav[]=array('desc'=>__('Access'),'href'=>'settings.php?t=access','iconclass'=>'users'); $subnav[]=array('desc'=>__('Knowledgebase'),'href'=>'settings.php?t=kb','iconclass'=>'kb-settings'); $subnav[]=array('desc'=>__('Autoresponder'),'href'=>'settings.php?t=autoresp','iconclass'=>'email-autoresponders'); @@ -251,6 +250,7 @@ class AdminNav extends StaffNav{ break; case 'emails': $subnav[]=array('desc'=>__('Emails'),'href'=>'emails.php', 'title'=>__('Email Addresses'), 'iconclass'=>'emailSettings'); + $subnav[]=array('desc'=>__('Settings'),'href'=>'emailsettings.php','iconclass'=>'email-settings'); $subnav[]=array('desc'=>__('Banlist'),'href'=>'banlist.php', 'title'=>__('Banned Emails'),'iconclass'=>'emailDiagnostic'); $subnav[]=array('desc'=>__('Templates'),'href'=>'templates.php','title'=>__('Email Templates'),'iconclass'=>'emailTemplates'); diff --git a/include/class.orm.php b/include/class.orm.php index a1328a6fdb03e1b4c7ab8d2bd76aa30112840da2..58cf0a8f66067f9446dce449572d70bc8d3ef438 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -612,12 +612,34 @@ class SqlAggregate extends SqlFunction { } function toSql($compiler, $model=false, $alias=false) { - $options = array( - 'constraint' => $this->constraint, - 'distinct' => $this->distinct); + $options = array('constraint' => $this->constraint, 'model' => true); + + // For DISTINCT, require a field specification — not a relationship + // specification. + list($field, $rmodel) = $compiler->getField($this->expr, $model, $options); + if ($this->distinct) { + $pk = false; + foreach ($rmodel::$meta['pk'] as $f) { + $pk |= false !== strpos($field, $f); + } + if (!$pk) { + // Try and use the foriegn primary key + if (count($rmodel::$meta['pk']) == 1) { + list($field) = $compiler->getField( + $this->expr . '__' . $rmodel::$meta['pk'][0], + $model, $options); + } + else { + throw new OrmException( + sprintf('%s :: %s', $rmodel, $field) . + ': DISTINCT aggregate expressions require specification of a single primary key field of the remote model' + ); + } + } + } - list($field) = $compiler->getField($this->expr, $model, $options); - return sprintf('%s(%s)%s', $this->func, $field, + return sprintf('%s(%s%s)%s', $this->func, + $this->distinct ? 'DISTINCT ' : '', $field, $alias && $this->alias ? ' AS '.$compiler->quote($this->alias) : ''); } @@ -1377,9 +1399,6 @@ class SqlCompiler { else $field = $this->quote($field); - if (isset($options['distinct']) && $options['distinct']) - $field = " DISTINCT $field"; - if (isset($options['model']) && $options['model']) $operator = $model; return array($field, $operator); @@ -1469,7 +1488,7 @@ class SqlCompiler { // Handle simple field = <value> constraints else { list($field, $op) = $this->getField($field, $model); - if ($field instanceof Aggregate) { + if ($field instanceof SqlAggregate) { // This constraint has to go in the HAVING clause $field = $field->toSql($this, $model); $type = CompiledExpression::TYPE_HAVING; @@ -1590,7 +1609,7 @@ class MySqlCompiler extends SqlCompiler { static $operators = array( 'exact' => '%1$s = %2$s', 'contains' => array('self', '__contains'), - 'startwith' => array('self', '__startswith'), + 'startswith' => array('self', '__startswith'), 'endswith' => array('self', '__endswith'), 'gt' => '%1$s > %2$s', 'lt' => '%1$s < %2$s', @@ -1616,11 +1635,11 @@ class MySqlCompiler extends SqlCompiler { } function __startswith($a, $b) { $b = $this->like_escape($b); - return sprintf('%s LIKE %s', $a, $this->input("%$b")); + return sprintf('%s LIKE %s', $a, $this->input("$b%")); } function __endswith($a, $b) { $b = $this->like_escape($b); - return sprintf('%s LIKE %s', $a, $this->input("$b%")); + return sprintf('%s LIKE %s', $a, $this->input("%$b")); } function __in($a, $b) { @@ -1713,8 +1732,8 @@ class MySqlCompiler extends SqlCompiler { function input($what, $slot=false) { if ($what instanceof QuerySet) { $q = $what->getQuery(array('nosort'=>true)); - $this->params = array_merge($q->params); - return (string)$q; + $this->params = array_merge($this->params, $q->params); + return $q->sql; } elseif ($what instanceof SqlFunction) { return $what->toSql($this); @@ -1893,10 +1912,17 @@ class MySqlCompiler extends SqlCompiler { // Add in annotations if ($queryset->annotations) { foreach ($queryset->annotations as $alias=>$A) { - $fields[] = $T = $A->toSql($this, $model, $alias); - // TODO: Add to last fieldset in fieldMap - if ($fieldMap) + // The root model will receive the annotations, add in the + // annotation after the root model's fields + $T = $A->toSql($this, $model, $alias); + if ($fieldMap) { + array_splice($fields, count($fieldMap[0][0]), 0, array($T)); $fieldMap[0][0][] = $A->getAlias(); + } + else { + // No field map — just add to end of field list + $fields[] = $T; + } } foreach ($model::$meta['pk'] as $pk) $group_by[] = $rootAlias .'.'. $pk; diff --git a/include/class.staff.php b/include/class.staff.php index 63b87d9884d3e1b532c5b1fe3dbca171552d3a57..e72fac6f05796f51135e89c6c6735b4a15fc6bd4 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -71,6 +71,7 @@ implements AuthenticatedUser { function getHashtable() { $base = $this->ht; $base['group'] = $base['group_id']; + unset($base['teams']); return $base; } @@ -368,6 +369,10 @@ implements AuthenticatedUser { return ($deptId && in_array($deptId, $this->getDepts()) && !$this->isAccessLimited()); } + function showAssignedTickets() { + return $this->show_assigned_tickets; + } + function getTeams() { if (!isset($this->_teams)) { diff --git a/include/class.team.php b/include/class.team.php index b7bd6a5026bb1991f04e8e58f55dd55208d660f9..5a81170782a7225316d335b225ff3e0c2caeb622 100644 --- a/include/class.team.php +++ b/include/class.team.php @@ -213,12 +213,12 @@ class Team extends VerySimpleModel { ->order_by('name'); if (isset($criteria['active']) && $criteria['active']) { - $query->annotate(array('members_count'=>Aggregate::COUNT('members'))) + $query->annotate(array('members_count'=>SqlAggregate::COUNT('members'))) ->filter(array( 'isenabled'=>1, 'members__staff__isactive'=>1, 'members__staff__onvacation'=>0, - 'members__staff__group__group_enabled'=>1, + 'members__staff__group__flags__hasbit'=>Group::FLAG_ENABLED, )) ->filter(array('members_count__gt'=>0)); } diff --git a/include/client/templates/ticket-print.tmpl.php b/include/client/templates/ticket-print.tmpl.php index d30973911165381db1097ca253461e021565ceb0..67e24a1700af259f8e802017fd405675527d9dd7 100644 --- a/include/client/templates/ticket-print.tmpl.php +++ b/include/client/templates/ticket-print.tmpl.php @@ -108,7 +108,7 @@ div.hr { <div class="hr"> </div> <table><tr> <td class="flush-left"><?php echo (string) $ost->company; ?></td> - <td class="flush-right"><?php echo Format::db_daydatetime(Misc::gmtime()); ?></td> + <td class="flush-right"><?php echo Format::daydatetime(Misc::gmtime()); ?></td> </tr></table> </htmlpageheader> @@ -117,7 +117,7 @@ div.hr { <table width="100%"><tr><td class="flush-left"> Ticket #<?php echo $ticket->getNumber(); ?> printed by <?php echo $thisclient->getName()->getFirst(); ?> on - <?php echo Format::db_daydatetime(Misc::gmtime()); ?> + <?php echo Format::daydatetime(Misc::gmtime()); ?> </td> <td class="flush-right"> Page {PAGENO} @@ -149,7 +149,7 @@ div.hr { </tr> <tr> <th><?php echo __('Create Date'); ?></th> - <td><?php echo Format::db_datetime($ticket->getCreateDate()); ?></td> + <td><?php echo Format::datetime($ticket->getCreateDate()); ?></td> <th><?php echo __('Source'); ?></th> <td><?php echo $ticket->getSource(); ?></td> </tr> @@ -201,7 +201,7 @@ if ($thread = $ticket->getThreadEntries($types)) { <div class="thread-entry <?php echo $threadTypes[$entry['thread_type']]; ?>"> <table class="header"><tr><td> <span><?php - echo Format::db_datetime($entry['created']);?></span> + echo Format::datetime($entry['created']);?></span> <span style="padding:0 1em" class="faded title"><?php echo Format::truncate($entry['title'], 100); ?></span> </td> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index 59ddf6b0bc75cdb4fa564a5a53c255fa4e13e5e5..d2f55a008414ecd6ed265549f097db4dc2e8a949 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -181,8 +181,6 @@ if (!$ticket->isClosed() || $ticket->isReopenable()) { ?> list($draft, $attrs) = Draft::getDraftAndDataAttrs('ticket.client', $ticket->getId(), $info['message']); echo $attrs; ?>><?php echo $draft ?: $info['message']; ?></textarea> - </td> - </tr> <?php if ($messageField->isAttachmentsEnabled()) { ?> <?php diff --git a/include/i18n/en_US/help/tips/settings.email.yaml b/include/i18n/en_US/help/tips/settings.email.yaml index ddad061bf26697b757e4fea6c797b1fd0782a45a..6285ef4fcaf505acc9363472daf5616c11dada03 100644 --- a/include/i18n/en_US/help/tips/settings.email.yaml +++ b/include/i18n/en_US/help/tips/settings.email.yaml @@ -123,3 +123,9 @@ default_mta: content: > <span class="doc-desc-title">Default MTA</span> takes care of email delivery process for outgoing emails without SMTP setting. + +ticket_response_files: + title: Ticket Response Files + content: > + If enabled, any attachments an Agent may attach to a ticket response will + be also included in the email to the User. diff --git a/include/i18n/en_US/help/tips/settings.ticket.yaml b/include/i18n/en_US/help/tips/settings.ticket.yaml index 3c7b872046a878bf15c964f6d65882d43deea241..bb4d01fc5006717838617c843cc2c4ac0f2619da 100644 --- a/include/i18n/en_US/help/tips/settings.ticket.yaml +++ b/include/i18n/en_US/help/tips/settings.ticket.yaml @@ -142,9 +142,3 @@ max_file_size: Choose a maximum file size for attachments uploaded by agents. This includes canned attachments, knowledge base articles, and attachments to ticket replies. - -ticket_response_files: - title: Ticket Response Files - content: > - If enabled, any attachments an Agent may attach to a ticket response will - be also included in the email to the User. diff --git a/include/staff/cannedresponses.inc.php b/include/staff/cannedresponses.inc.php index 8f729bb0cb68f5fb03680e62326b229cb77c5b4a..1affca425353568f2191fbb5b04bf1e074700ff6 100644 --- a/include/staff/cannedresponses.inc.php +++ b/include/staff/cannedresponses.inc.php @@ -4,7 +4,7 @@ if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied'); $qstr=''; $sql='SELECT canned.*, count(attach.file_id) as files, dept.name as department '. ' FROM '.CANNED_TABLE.' canned '. - ' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=canned.dept_id) '. + ' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.id=canned.dept_id) '. ' LEFT JOIN '.ATTACHMENT_TABLE.' attach ON (attach.object_id=canned.canned_id AND attach.`type`=\'C\' AND NOT attach.inline)'; $sql.=' WHERE 1'; diff --git a/include/staff/groups.inc.php b/include/staff/groups.inc.php index ac3f438956466eaa2ba695303e8ddae8152ac076..69e5581448700e2bdf7a70dc14147677598b8ca2 100644 --- a/include/staff/groups.inc.php +++ b/include/staff/groups.inc.php @@ -72,7 +72,7 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); if ($count) { $groups= Group::objects() ->annotate(array( - 'members_count'=>SqlAggregate::COUNT('members', true), + 'members_count'=>SqlAggregate::COUNT('members__staff_id', true), 'depts_count'=>SqlAggregate::COUNT('depts', true), 'isenabled'=>new SqlExpr(array( 'flags__hasbit' => Group::FLAG_ENABLED)) diff --git a/include/staff/roles.inc.php b/include/staff/roles.inc.php index 17723f19df1f776006a3a36858faf10ade560b28..6ce76f7dc16702c24fc12e69710fc2d4cc9ed1d9 100644 --- a/include/staff/roles.inc.php +++ b/include/staff/roles.inc.php @@ -62,7 +62,7 @@ $showing=$pageNav->showing().' '._N('role', 'roles', $count); </tbody> <tfoot> <tr> - <td colspan="4"> + <td colspan="5"> <?php if($count){ ?> <?php echo __('Select'); ?>: <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a> diff --git a/include/staff/settings-emails.inc.php b/include/staff/settings-emails.inc.php index b10facabe98d61a05eccf10736e37b4269721c20..85a89370baf0af1e97672e0f748755be3c217670 100644 --- a/include/staff/settings-emails.inc.php +++ b/include/staff/settings-emails.inc.php @@ -2,7 +2,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied'); ?> <h2><?php echo __('Email Settings and Options');?></h2> -<form action="settings.php?t=emails" method="post" id="save"> +<form action="emailsettings.php" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="t" value="emails" > <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php index 956d1555c3a0ba30df719fd496a54cefddb3533f..175a4de25429bcf93739348a1097a087424eb159 100644 --- a/include/staff/settings-system.inc.php +++ b/include/staff/settings-system.inc.php @@ -244,9 +244,11 @@ $gmtime = Misc::gmtime(); <tr> <td style="vertical-align:top;padding-top:4px;"><?php echo __('Secondary Languages'); ?>:</td> <td><div id="secondary_langs" style="width: 300px"><?php - foreach ($cfg->getSecondaryLanguages() as $lang) { ?> + foreach ($cfg->getSecondaryLanguages() as $lang) { + $info = Internationalization::getLanguageInfo($lang); ?> <div class="secondary_lang" style="cursor:move"> - <i class="icon-sort"></i> + <i class="icon-sort"></i> + <span class="flag flag-<?php echo $info['flag']; ?>"></span> <?php echo Internationalization::getLanguageDescription($lang); ?> <input type="hidden" name="secondary_langs[]" value="<?php echo $lang; ?>"/> <div class="pull-right"> diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php index fe312a01f043c3918cfd9d547dee5d5f397e99fe..e1872334ec1cc67c413a2202789d5920af6b3190 100644 --- a/include/staff/staffmembers.inc.php +++ b/include/staff/staffmembers.inc.php @@ -56,6 +56,7 @@ $agents = Staff::objects() ->annotate(array( 'teams_count'=>SqlAggregate::COUNT('teams', true), )) + ->select_related('dept', 'group') ->order_by(sprintf('%s%s', strcasecmp($order, 'DESC') ? '' : '-', $order_column)); @@ -157,7 +158,7 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart()); <td><?php echo $agent->isActive() ? __('Active') :'<b>'.__('Locked').'</b>'; ?> <?php echo $agent->onvacation ? '<small>(<i>'.__('vacation').'</i>)</small>' : ''; ?></td> <td><a href="groups.php?id=<?php echo $agent->group_id; ?>"><?php - echo Format::htmlchars('FIXME'/*$agent->group->getName()*/); ?></a></td> + echo Format::htmlchars($agent->group->getName()); ?></a></td> <td><a href="departments.php?id=<?php echo $agent->getDeptId(); ?>"><?php echo Format::htmlchars((string) $agent->dept); ?></a></td> diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php index 65932bf656f9960ed7f80c540316646dd979d881..6eaad7dc0a8677eed5332cf2e96811c37091188a 100644 --- a/include/staff/templates/dynamic-field-config.tmpl.php +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -4,19 +4,17 @@ <form method="post" action="#form/field-config/<?php echo $field->get('id'); ?>"> <ul class="tabs"> - <li><a href="#config" class="active"><i class="icon-cogs"></i> Field Setup</a></li> - <li><a href="#visibility"><i class="icon-beaker"></i> Settings</a></li> + <li class="active"><a href="#config"><i class="icon-cogs"></i> <?php echo __('Field Setup'); ?></a></li> + <li><a href="#visibility"><i class="icon-beaker"></i> <?php echo __('Settings'); ?></a></li> </ul> -<div class="tab_content" id="visibility" style="display:none"> +<div class="hidden tab_content" id="visibility"> <div> <div class="span4"> - <div style="margin-bottom:5px"><strong>Enabled</strong> + <div style="margin-bottom:5px"><strong><?php echo __('Enabled'); ?></strong> <i class="help-tip icon-question-sign" - data-title="Enabled" - data-content="This field can be disabled which will remove it - from the form for new entries, but will preserve the data on all - current entries."></i> + data-title="<?php echo __('Enabled'); ?>" + data-content="<?php echo __('This field can be disabled which will remove it from the form for new entries, but will preserve the data on all current entries.'); ?>"></i> </div> </div> <div class="span6"> @@ -24,16 +22,15 @@ echo DynamicFormField::FLAG_ENABLED; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_ENABLED)) echo 'checked="checked"'; if ($field->hasFlag(DynamicFormField::FLAG_MASK_DISABLE)) echo ' disabled="disabled"'; - ?>> Enabled<br/> + ?>> <?php echo __('Enabled'); ?><br/> </div> <hr class="faded"/> <div class="span4"> - <div style="margin-bottom:5px"><strong>Visible</strong> + <div style="margin-bottom:5px"><strong><?php echo __('Visible'); ?></strong> <i class="help-tip icon-question-sign" - data-title="Visible" - data-content="Making fields <em>visible</em> allows agents and - endusers to view and create information in this field."></i> + data-title="<?php echo __('Visible'); ?>" + data-content="<?php echo __('Making fields <em>visible</em> allows agents and endusers to view and create information in this field.'); ?>"></i> </div> </div> <div class="span3"> @@ -41,25 +38,24 @@ echo DynamicFormField::FLAG_CLIENT_VIEW; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_VIEW)) echo 'checked="checked"'; if ($field->isPrivacyForced()) echo ' disabled="disabled"'; - ?>> For Clients<br/> + ?>> <?php echo __('For Clients'); ?><br/> </div> <div class="span3"> <input type="checkbox" name="flags[]" value="<?php echo DynamicFormField::FLAG_AGENT_VIEW; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_AGENT_VIEW)) echo 'checked="checked"'; if ($field->isPrivacyForced()) echo ' disabled="disabled"'; - ?>> For Agents<br/> + ?>> <?php echo __('For Agents'); ?><br/> </div> <?php if ($field->getImpl()->hasData()) { ?> <hr class="faded"/> <div class="span4"> - <div style="margin-bottom:5px"><strong>Required</strong> + <div style="margin-bottom:5px"><strong><?php echo __('Required'); ?></strong> <i class="help-tip icon-question-sign" - data-title="Required" - data-content="New entries cannot be created unless all - <em>required</em> fields have valid data."></i> + data-title="<?php echo __('Required'); ?>" + data-content="<?php echo __('New entries cannot be created unless all <em>required</em> fields have valid data.'); ?>"></i> </div> </div> <div class="span3"> @@ -67,23 +63,22 @@ echo DynamicFormField::FLAG_CLIENT_REQUIRED; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_REQUIRED)) echo 'checked="checked"'; if ($field->isRequirementForced()) echo ' disabled="disabled"'; - ?>> For Clients<br/> + ?>> <?php echo __('For Clients'); ?><br/> </div> <div class="span3"> <input type="checkbox" name="flags[]" value="<?php echo DynamicFormField::FLAG_AGENT_REQUIRED; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_AGENT_REQUIRED)) echo 'checked="checked"'; if ($field->isRequirementForced()) echo ' disabled="disabled"'; - ?>> For Agents<br/> + ?>> <?php echo __('For Agents'); ?><br/> </div> <hr class="faded"/> <div class="span4"> <div style="margin-bottom:5px"><strong>Editable</strong> <i class="help-tip icon-question-sign" - data-content="Fields marked editable allow agents and endusers to update the - content of this field after the form entry has been created." - data-title="Editable"></i> + data-content="<?php echo __('Fields marked editable allow agents and endusers to update the content of this field after the form entry has been created.'); ?>" + data-title="<?php echo __('Editable'); ?>"></i> </div> </div> @@ -91,29 +86,28 @@ <input type="checkbox" name="flags[]" value="<?php echo DynamicFormField::FLAG_CLIENT_EDIT; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_EDIT)) echo 'checked="checked"'; - ?>> For Clients<br/> + ?>> <?php echo __('For Clients'); ?><br/> </div> <div class="span3"> <input type="checkbox" name="flags[]" value="<?php echo DynamicFormField::FLAG_AGENT_EDIT; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_AGENT_EDIT)) echo 'checked="checked"'; - ?>> For Agents<br/> + ?>> <?php echo __('For Agents'); ?><br/> </div> <hr class="faded"/> <div class="span4"> <div style="margin-bottom:5px"><strong>Data Integrity</strong> <i class="help-tip icon-question-sign" - data-title="Required to close a ticket" - data-content="Optionally, this field can prevent closing a - ticket until it has valid data."></i> + data-title="<?php echo __('Required to close a ticket'); ?>" + data-content="<?php echo __('Optionally, this field can prevent closing a ticket until it has valid data.'); ?>"></i> </div> </div> <div class="span6"> <input type="checkbox" name="flags[]" value="<?php echo DynamicFormField::FLAG_CLOSE_REQUIRED; ?>" <?php if ($field->hasFlag(DynamicFormField::FLAG_CLOSE_REQUIRED)) echo 'checked="checked"'; - ?>> Required to close a ticket<br/> + ?>> <?php echo __('Required to close a ticket'); ?><br/> </div> <?php } ?> </div> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index 47be9addf0baadfec50f94f42d320d15ac6a3728..1be98b962556c81b0f87ef13011d9b43e2317d72 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -38,7 +38,7 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?> } foreach ($form->getFields() as $field) { try { - if (!$field->isEditableToStaff()) + if ($options['mode'] == 'edit' && !$field->isEditableToStaff()) continue; } catch (Exception $e) { diff --git a/include/staff/templates/ticket-print.tmpl.php b/include/staff/templates/ticket-print.tmpl.php index 430c775d9cd2490d45c45870f60017ca479d5101..e283e4e8f2da193598e1096b8d1cbe0eb86d00e4 100644 --- a/include/staff/templates/ticket-print.tmpl.php +++ b/include/staff/templates/ticket-print.tmpl.php @@ -108,7 +108,7 @@ div.hr { <div class="hr"> </div> <table><tr> <td class="flush-left"><?php echo (string) $ost->company; ?></td> - <td class="flush-right"><?php echo Format::db_daydatetime(Misc::gmtime()); ?></td> + <td class="flush-right"><?php echo Format::daydatetime(Misc::gmtime()); ?></td> </tr></table> </htmlpageheader> @@ -117,7 +117,7 @@ div.hr { <table width="100%"><tr><td class="flush-left"> Ticket #<?php echo $ticket->getNumber(); ?> printed by <?php echo $thisstaff->getUserName(); ?> on - <?php echo Format::db_daydatetime(Misc::gmtime()); ?> + <?php echo Format::daydatetime(Misc::gmtime()); ?> </td> <td class="flush-right"> Page {PAGENO} @@ -149,7 +149,7 @@ div.hr { </tr> <tr> <th><?php echo __('Create Date'); ?></th> - <td><?php echo Format::db_datetime($ticket->getCreateDate()); ?></td> + <td><?php echo Format::datetime($ticket->getCreateDate()); ?></td> <th><?php echo __('Source'); ?></th> <td><?php echo $ticket->getSource(); ?></td> </tr> @@ -168,13 +168,13 @@ div.hr { <th><?php echo __('SLA Plan'); ?></th> <td><?php if ($sla = $ticket->getSLA()) echo $sla->getName(); ?></td> <th><?php echo __('Last Response'); ?></th> - <td><?php echo Format::db_datetime($ticket->getLastResponseDate()); ?></td> + <td><?php echo Format::datetime($ticket->getLastResponseDate()); ?></td> </tr> <tr> <th><?php echo __('Due Date'); ?></th> - <td><?php echo Format::db_datetime($ticket->getEstDueDate()); ?></td> + <td><?php echo Format::datetime($ticket->getEstDueDate()); ?></td> <th><?php echo __('Last Message'); ?></th> - <td><?php echo Format::db_datetime($ticket->getLastMessageDate()); ?></td> + <td><?php echo Format::datetime($ticket->getLastMessageDate()); ?></td> </tr> </tbody> </table> @@ -225,7 +225,7 @@ if ($thread = $ticket->getThreadEntries($types)) { <div class="thread-entry <?php echo $threadTypes[$entry['thread_type']]; ?>"> <table class="header" style="width:100%"><tr><td> <span><?php - echo Format::db_datetime($entry['created']);?></span> + echo Format::datetime($entry['created']);?></span> <span style="padding:0 1em" class="faded title"><?php echo Format::truncate($entry['title'], 100); ?></span> </td> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index 9af71ea08c13084ff2aa55c4e25d8f6c87ac4ddc..7ec099aff6b315bbc6180c6f2f88b5f8841e0b40 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -312,7 +312,6 @@ if ($_POST) list($draft, $attrs) = Draft::getDraftAndDataAttrs('ticket.staff.response', false, $info['response']); echo $attrs; ?>><?php echo $draft ?: $info['response']; ?></textarea> - style="width:80%;"><?php echo $info['response']; ?></textarea> <div class="attachments"> <?php print $response_form->getField('attachments')->render(); diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index a1591aa36ea3f763601b59a3b824ee34c095bc9a..8edd1c22d3d38f9989d951955d048d1290b845ae 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -4,7 +4,18 @@ $tickets = TicketModel::objects(); $clear_button = false; $date_header = $date_col = false; -// Add "other" fields (via $_POST['other'][]) +// Figure out REFRESH url — which might not be accurate after posting a +// response +list($path,) = explode('?', $_SERVER['REQUEST_URI'], 2); +$args = array(); +parse_str($_SERVER['QUERY_STRING'], $args); + +// Remove commands from query +unset($args['id']); +unset($args['a']); + +$refresh_url = $path . '?' . http_build_query($args); + switch(strtolower($_REQUEST['status'])){ //Status is overloaded case 'closed': @@ -32,7 +43,24 @@ case 'answered': break; default: case 'search': - if (isset($_SESSION['advsearch'])) { + // Consider basic search + if ($_REQUEST['query']) { + $results_type=__('Search Results'); + // Use an index if possible + if (Validator::is_email($_REQUEST['query'])) { + $tickets = $tickets->filter(array( + 'user__emails__address' => $_REQUEST['query'], + )); + } + else { + $tickets = $tickets->filter(Q::any(array( + 'number__startswith' => $_REQUEST['query'], + 'user__emails__address__contains' => $_REQUEST['query'], + ))); + } + break; + } + elseif (isset($_SESSION['advsearch'])) { // XXX: De-duplicate and simplify this code $form = $search->getFormFromSession('advsearch'); $form->loadState($_SESSION['advsearch']); @@ -98,7 +126,7 @@ case 'due': $date_col = 'est_duedate'; $tickets->values('est_duedate'); $tickets->filter(array('est_duedate__isnull'=>false)); - $tickets->order_by(new SqlField('est_duedate')); + $tickets->order_by('est_duedate'); break; default: @@ -117,19 +145,17 @@ TicketForm::ensureDynamicDataView(); // Save the query to the session for exporting $_SESSION[':Q:tickets'] = $tickets; - ?> <!-- SEARCH FORM START --> <div id='basic_search'> <form action="tickets.php" method="get"> - <?php csrf_token(); ?> - <input type="hidden" name="a" value="search"> + <input type="hidden" name="status" value="search"> <table> <tr> <td><input type="text" id="basic-ticket-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>" autocomplete="off" autocorrect="off" autocapitalize="off"></td> - <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td> + <td><input type="submit" class="button" value="<?php echo __('Search'); ?>"></td> <td> <a href="#" onclick="javascript: $.dialog('ajax.php/tickets/search', 201);" >[<?php echo __('advanced'); ?>]</a> <i class="help-tip icon-question-sign" href="#advanced"></i></td> @@ -142,7 +168,7 @@ $_SESSION[':Q:tickets'] = $tickets; <div style="margin-bottom:20px; padding-top:10px;"> <div> <div class="pull-left flush-left"> - <h2><a href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>" + <h2><a href="<?php echo $refresh_url; ?>" title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo $results_type.$showing; ?></a></h2> </div> diff --git a/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql b/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql index 4c886287491690966973a793386ffc19262b6e74..0f29e473c460b400e58b8e23e6f9793d2d2891ab 100644 --- a/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql +++ b/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql @@ -22,10 +22,10 @@ ALTER TABLE `%TABLE_PREFIX%group_dept_access` ADD `role_id` INT UNSIGNED NOT NULL DEFAULT '0'; ALTER TABLE `%TABLE_PREFIX%groups` - ADD `role_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `group_id` , - ADD `flags` INT UNSIGNED NOT NULL DEFAULT '1' AFTER `role_id`, - CHANGE `group_name` `name` VARCHAR(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', CHANGE `group_id` `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + CHANGE `group_name` `name` VARCHAR(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + ADD `role_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `id` , + ADD `flags` INT UNSIGNED NOT NULL DEFAULT '1' AFTER `role_id`, ADD INDEX (`role_id`); RENAME TABLE `%TABLE_PREFIX%groups` TO `%TABLE_PREFIX%group`; @@ -38,4 +38,5 @@ ALTER TABLE `%TABLE_PREFIX%department` -- Finished with patch UPDATE `%TABLE_PREFIX%config` - SET `schema_signature`='c7c828356c88b462ba2e3e1437dca0df'; + SET `value`='c7c828356c88b462ba2e3e1437dca0df' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/1ee831c8-c7c82835.task.php b/include/upgrader/streams/core/1ee831c8-c7c82835.task.php index 09bf7a6def9a4abc5f762c59d0b0fe8a16e2a3f4..3785980847f16fdf290ed2c8f5f805ef89478bca 100644 --- a/include/upgrader/streams/core/1ee831c8-c7c82835.task.php +++ b/include/upgrader/streams/core/1ee831c8-c7c82835.task.php @@ -1,18 +1,20 @@ <?php class GroupRoles extends MigrationTask { + var $description = "Migrate permissions from Group to Role"; - var $pmap = array( - 'can_create_tickets' => 'ticket.create', - 'can_edit_tickets' => 'ticket.edit', + static $pmap = array( + 'can_create_tickets' => 'ticket.create', + 'can_edit_tickets' => 'ticket.edit', 'can_post_ticket_reply' => 'ticket.reply', - 'can_delete_tickets' => 'ticket.delete', - 'can_close_tickets' => 'ticket.close', - 'can_assign_tickets' => 'ticket.assign', - 'can_transfer_tickets' => 'ticket.transfer', - 'can_ban_emails' => 'emails.banlist', - 'can_manage_premade' => 'kb.premade', - 'can_manage_faq' => 'kb.faq', - 'can_view_staff_stats' => 'stats.agents'); + 'can_delete_tickets' => 'ticket.delete', + 'can_close_tickets' => 'ticket.close', + 'can_assign_tickets' => 'ticket.assign', + 'can_transfer_tickets' => 'ticket.transfer', + 'can_ban_emails' => 'emails.banlist', + 'can_manage_premade' => 'kb.premade', + 'can_manage_faq' => 'kb.faq', + 'can_view_staff_stats' => 'stats.agents', + ); function run($max_time) { global $cfg; @@ -20,8 +22,10 @@ class GroupRoles extends MigrationTask { // settings foreach (Group::objects() as $group) { $ht=array( - 'flags=1', + 'flags' => Group::FLAG_ENABLED, 'name' => sprintf('%s %s', $group->getName(), + // XXX: Translate based on the system language, not + // the current agent's __('Role')), 'notes' => $group->getName() ); diff --git a/include/upgrader/streams/core/b26f29a6-1ee831c8.task.php b/include/upgrader/streams/core/b26f29a6-1ee831c8.task.php index dcf1fe680774ab3ac8cdce2efb7b2a0251a7932c..65a58a1cb385eec37ba4d01b440087b9dcd26e5e 100644 --- a/include/upgrader/streams/core/b26f29a6-1ee831c8.task.php +++ b/include/upgrader/streams/core/b26f29a6-1ee831c8.task.php @@ -1,6 +1,8 @@ <?php class IntlMigrator extends MigrationTask { + var $description = "Date format migration from date() to ICU"; + static $dateToIntl = array( 'd' => 'dd', 'D' => 'EEE', diff --git a/scp/css/scp.css b/scp/css/scp.css index add15fde893acb7e58be9e9a6f639e2492f61424..b3a05e7a8cf7b60d80014a880d37d86dc257ca6c 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -1020,16 +1020,6 @@ ul.tabs.vertical li a { background-repeat:no-repeat; } -ul.tabs li a.active { - height:18px; - color:#184E81; - background-color:#f9f9f9; - border:1px solid #aaa; - border-top:2px solid #81a9d7; - border-bottom:none; - bottom: 0; -} - #response_options > form { padding:10px 5px; background:#f9f9f9; diff --git a/scp/emailsettings.php b/scp/emailsettings.php new file mode 100644 index 0000000000000000000000000000000000000000..175fa4575b2dfc2ac79246782b91adf11e97d2d5 --- /dev/null +++ b/scp/emailsettings.php @@ -0,0 +1,39 @@ +<?php +/********************************************************************* + emailsettings.php + + Handles settings for the email channel + + Peter Rotich <peter@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require('admin.inc.php'); + +$errors = array(); +$tip_namespace = 'settings.email'; +$inc = 'settings-emails.inc.php'; + +if ($_POST && !$errors) { + if($cfg && $cfg->updateSettings($_POST,$errors)) { + $msg=sprintf(__('Successfully updated %s'), Format::htmlchars($page[0])); + } elseif(!$errors['err']) { + $errors['err']=__('Unable to update settings - correct errors below and try again'); + } +} + +$config=($errors && $_POST)?Format::input($_POST):Format::htmlchars($cfg->getConfigInfo()); +$ost->addExtraHeader('<meta name="tip-namespace" content="'.$tip_namespace.'" />', + "$('#content').data('tipNamespace', '".$tip_namespace."');"); + +$nav->setTabActive('emails', 'emailsettings.php'); +require_once(STAFFINC_DIR.'header.inc.php'); +include_once(STAFFINC_DIR.$inc); +include_once(STAFFINC_DIR.'footer.inc.php'); +?> + diff --git a/scp/profile.php b/scp/profile.php index 827ae7869efe454d90e9730fea481ea0c97c86a8..f68ce131e1f0bafa49e5ba32df37aa8394826650 100644 --- a/scp/profile.php +++ b/scp/profile.php @@ -27,8 +27,6 @@ if($_POST && $_POST['id']!=$thisstaff->getId()) { //Check dummy ID used on the f $errors['err']=sprintf(__('%s: Unknown or invalid'), __('agent')); elseif($staff->updateProfile($_POST,$errors)){ $msg=__('Profile updated successfully'); - $thisstaff->reload(); - $staff->reload(); }elseif(!$errors['err']) $errors['err']=__('Profile update error. Try correcting the errors below and try again!'); } diff --git a/scp/settings.php b/scp/settings.php index b824ef7cabbf0305fae1c56d4c199147ec59b473..2fef525750d4ac1db73ccbdf6e576129f5ca5435 100644 --- a/scp/settings.php +++ b/scp/settings.php @@ -21,8 +21,6 @@ $settingOptions=array( array(__('System Settings'), 'settings.system'), 'tickets' => array(__('Ticket Settings and Options'), 'settings.ticket'), - 'emails' => - array(__('Email Settings'), 'settings.email'), 'pages' => array(__('Site Pages'), 'settings.pages'), 'access' => diff --git a/scp/tickets.php b/scp/tickets.php index f65cfd4c189bfd828d4531c5a4d1a7c25c80c9b2..ec7a3600a3c78f7e22406acff78d95aaa3166b39 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -58,7 +58,7 @@ if($_POST && !$errors): //More coffee please. $errors=array(); $lock=$ticket->getLock(); //Ticket lock if any - $role = $thistaff->getRole($ticket->getDeptId); + $role = $thisstaff->getRole($ticket->getDeptId); switch(strtolower($_POST['a'])): case 'reply': if(!$role || !$role->canPostReply()) @@ -448,7 +448,7 @@ if (isset($_SESSION['advsearch'])) { $tickets = $search->mangleQuerySet($tickets, $form); $count = $tickets->count(); $nav->addSubMenu(array('desc' => __('Search').' ('.number_format($count).')', - 'title'=>__('Advanced Search Query'), + 'title'=>__('Advanced Ticket Search'), 'href'=>'tickets.php?status=search', 'iconclass'=>'Ticket'), (!$_REQUEST['status'] || $_REQUEST['status']=='search'));