Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

368 lines (318 sloc) 14.82 kB
<?php /*
ocPortal
Copyright (c) ocProducts, 2004-2012
See text/EN/licence.txt for full licencing information.
NOTE TO PROGRAMMERS:
Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
**** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****
*/
/**
* @license http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
* @copyright ocProducts Ltd
* @package tickets
*/
/**
* Add a ticket type.
*
* @param SHORT_TEXT The ticket type
* @param BINARY Whether guest e-mail addresses are mandatory for new tickets
* @param BINARY Whether the FAQ should be searched before submitting a new ticket
*/
function add_ticket_type($ticket_type,$guest_emails_mandatory=0,$search_faq=0)
{
$GLOBALS['SITE_DB']->query_insert('ticket_types',array('ticket_type'=>insert_lang($ticket_type,1),'guest_emails_mandatory'=>$guest_emails_mandatory,'search_faq'=>$search_faq,'cache_lead_time'=>NULL));
log_it('ADD_TICKET_TYPE',$ticket_type);
}
/**
* Edit a ticket type, keeping the integer ID the same.
*
* @param integer The old ticket type
* @param ?SHORT_TEXT The new ticket type (NULL: do not change)
* @param BINARY Whether guest e-mail addresses are mandatory for new tickets
* @param BINARY Whether the FAQ should be searched before submitting a new ticket
*/
function edit_ticket_type($old_ticket_type,$new_ticket_type,$guest_emails_mandatory,$search_faq)
{
$GLOBALS['SITE_DB']->query_update('ticket_types',array('guest_emails_mandatory'=>$guest_emails_mandatory,'search_faq'=>$search_faq),array('ticket_type'=>$old_ticket_type),'',1);
if (!is_null($new_ticket_type))
lang_remap($old_ticket_type,$new_ticket_type);
log_it('EDIT_TICKET_TYPE',strval($old_ticket_type));
}
/**
* Delete a ticket type.
*
* @param integer The ticket type
*/
function delete_ticket_type($ticket_type)
{
$_ticket_type=get_translated_text($ticket_type);
$GLOBALS['SITE_DB']->query_delete('group_category_access',array('module_the_name'=>'tickets','category_name'=>strval($_ticket_type)));
$GLOBALS['SITE_DB']->query_delete('gsp',array('module_the_name'=>'tickets','category_name'=>strval($_ticket_type)));
//delete_lang($ticket_type); Needed for if existing ticket looked up
$GLOBALS['SITE_DB']->query_delete('ticket_types',array('ticket_type'=>$ticket_type),'',1);
log_it('DELETE_TICKET_TYPE',strval($ticket_type),$_ticket_type);
}
/**
* Get a map of properties for the given ticket type.
*
* @param ?integer The ticket type (NULL: fallback for old tickets)
* @return ?array Array of properties (NULL: ticket type not found)
*/
function get_ticket_type($ticket_type)
{
if (is_null($ticket_type)) return array('ticket_type'=>NULL,'guest_emails_mandatory'=>false,'search_faq'=>false,'cache_lead_time'=>NULL);
$row=$GLOBALS['SITE_DB']->query_select('ticket_types',NULL,array('ticket_type'=>$ticket_type),'',1);
if (count($row)==0) return NULL;
return $row[0];
}
/**
* Update the cache of ticket type lead times (average time taken for a response to tickets of that type) in the database.
* This is a query-intensive function, so should only be run occasionally.
*/
function update_ticket_type_lead_times()
{
require_code('feedback');
$ticket_types=$GLOBALS['SITE_DB']->query_select('ticket_types',NULL);
foreach ($ticket_types as $ticket_type)
{
$total_lead_time=0;
$tickets_counted=0;
$tickets=$GLOBALS['SITE_DB']->query_select('tickets',NULL,array('ticket_type'=>$ticket_type['ticket_type']));
foreach ($tickets as $ticket)
{
$max_rows=0;
$topic=$GLOBALS['FORUM_DRIVER']->show_forum_topics($ticket['forum_id'],1,0,$max_rows,$ticket['ticket_id'],true,'lasttime',false,do_lang('SUPPORT_TICKET').': #'.$ticket['ticket_id']);
if (is_null($topic)) continue;
$topic=$topic[0];
// We need to have two posts for new-style tickets, or three for old-style tickets (with spacers)
if (($topic['num']<2) || (($topic['firstusername']==do_lang('SYSTEM')) && ($topic['num']<3)))
continue;
$ticket_id=extract_topic_identifier($topic['description']);
$_forum=1; $_topic_id=1; $_ticket_type=1; // These will be returned by reference
$posts=get_ticket_posts($ticket_id,$_forum,$_topic_id,$_ticket_type);
// Differentiate between old- and new-style tickets
if ($topic['firstusername']==do_lang('SYSTEM')) $first_key=1;
else $first_key=0;
// Find the first post by someone other than the ticket owner
$i=$first_key+1;
while ((array_key_exists($i,$posts)) && ($posts[$i]['user']!=$posts[$first_key]['user'])) $i++;
if (array_key_exists($i,$posts))
{
$total_lead_time+=$posts[$i]['date']-$posts[$first_key]['date'];
$tickets_counted++;
}
}
/* Calculate the new lead time and store it in the DB */
if ($tickets_counted>0)
$GLOBALS['SITE_DB']->query_update('ticket_types',array('cache_lead_time'=>$total_lead_time/$tickets_counted),array('ticket_type'=>$ticket_type['ticket_type']),'',1);
}
}
/**
* Get an array of tickets for the given member and ticket type. If the member has permission to see others' tickets, it will be a list of all tickets
* in the system, restricted by ticket type as appropriate. Otherwise, it will be a list of that member's tickets, as restricted by ticket type.
*
* @param AUTO_LINK The member ID
* @param ?AUTO_LINK The ticket type (NULL: all ticket types)
* @param boolean Don't view others' tickets, even if the member has permission to
* @param boolean Whether to skip showing errors, returning NULL instead
* @return array Array of tickets, empty on failure
*/
function get_tickets($member,$ticket_type=NULL,$override_view_others_tickets=false,$silent_error_handling=false)
{
if ((!is_null($ticket_type)) && (!has_category_access(get_member(),'tickets',get_translated_text($ticket_type))))
return array();
$restrict='';
$restrict_description='';
$view_others_tickets=(!$override_view_others_tickets) && (has_specific_permission($member,'view_others_tickets'));
if (!$view_others_tickets)
{
$restrict=strval($member).'\_%';
$restrict_description=do_lang('SUPPORT_TICKET').': #'.$restrict;
}
if ((get_option('ticket_member_forums')=='1') || (get_option('ticket_type_forums')=='1'))
{
if (get_forum_type()=='ocf')
{
$fid=($view_others_tickets)?get_ticket_forum_id(NULL,NULL,false,$silent_error_handling):get_ticket_forum_id(get_member(),NULL,false,$silent_error_handling);
if (is_null($fid)) return array();
if (is_null($ticket_type))
{
require_code('ocf_forums');
$forums=ocf_get_all_subordinate_forums($fid,NULL,NULL,true);
}
else
{
$query='SELECT id FROM '.$GLOBALS['FORUM_DB']->get_table_prefix().'f_forums WHERE '.db_string_equal_to('f_name',get_translated_text($ticket_type)).' AND ';
if ($view_others_tickets) $query.='f_parent_forum IN (SELECT id FROM '.$GLOBALS['FORUM_DB']->get_table_prefix().'f_forums WHERE f_parent_forum='.strval((integer)$fid).')';
else $query.='f_parent_forum='.strval((integer)$fid);
$rows=$GLOBALS['FORUM_DB']->query($query);
$forums=collapse_2d_complexity('id','id',$rows);
}
}
else $forums=array(get_ticket_forum_id($member,$ticket_type,false,$silent_error_handling));
}
else $forums=array(get_ticket_forum_id(NULL,NULL,false,$silent_error_handling));
if ((count($forums)==1) && (array_key_exists(0,$forums)) && (is_null($forums[0]))) return array();
$max_rows=0;
$topics=$GLOBALS['FORUM_DRIVER']->show_forum_topics(array_flip($forums),100,0,$max_rows,$restrict,true,'lasttime',false,$restrict_description);
if (is_null($topics)) return array();
$filtered_topics=array();
foreach ($topics as $topic)
{
$fp=$topic['firstpost'];
unset($topic['firstpost']); // To stop Tempcode randomly making serialization sometimes change such that the refresh_if_changed is triggered
if ((is_null($ticket_type)) || (strpos($fp->evaluate(),do_lang('TICKET_TYPE').': '.get_translated_text($ticket_type))!==false))
{
$filtered_topics[]=$topic;
}
}
return $filtered_topics;
}
/**
* Get the posts from a given ticket, and also return the IDs of the forum and topic containing it. The return value is the same as
* that of get_forum_topic_posts(), except in error conditions
*
* @param string The ticket ID
* @param AUTO_LINK Return location for the forum ID
* @param AUTO_LINK Return location for the topic ID
* @param integer Return location for the ticket type
* @param integer Start offset in pagination
* @param ?integer Max per page in pagination (NULL: no limit)
* @return mixed The array of maps (Each map is: title, message, member, date) (NULL: no such ticket)
*/
function get_ticket_posts($ticket_id,&$forum,&$topic_id,&$ticket_type,$start=0,$max=NULL)
{
$ticket=$GLOBALS['SITE_DB']->query_select('tickets',NULL,array('ticket_id'=>$ticket_id),'',1,NULL,true);
if (count($ticket)==1) // We know about it, so grab details from tickets table
{
$ticket_type=$ticket[0]['ticket_type'];
if (!has_category_access(get_member(),'tickets',get_translated_text($ticket_type)))
access_denied('CATEGORY_ACCESS_LEVEL');
$forum=$ticket[0]['forum_id'];
$topic_id=$ticket[0]['topic_id'];
$count=0;
return $GLOBALS['FORUM_DRIVER']->get_forum_topic_posts($GLOBALS['FORUM_DRIVER']->find_topic_id_for_topic_identifier($forum,$ticket_id),$count,$max,$start);
}
// It must be an old-style ticket, residing in the root ticket forum
$forum=get_ticket_forum_id();
$topic_id=$GLOBALS['FORUM_DRIVER']->find_topic_id_for_topic_identifier(get_option('ticket_forum_name'),$ticket_id);
$ticket_type=NULL;
$count=0;
return $GLOBALS['FORUM_DRIVER']->get_forum_topic_posts($GLOBALS['FORUM_DRIVER']->find_topic_id_for_topic_identifier($forum,$ticket_id),$count);
}
/**
* Remove a ticket from the database. This does not remove the associated forum topic.
*
* @param AUTO_LINK The associated topic ID
*/
function delete_ticket_by_topic_id($topic_id)
{
$GLOBALS['SITE_DB']->query_delete('tickets',array('topic_id'=>$topic_id));
}
/**
* Add a new post to a ticket, or create a new ticket if a ticket with the given ID doesn't exist.
* It has the same return value as make_post_forum_topic().
*
* @param AUTO_LINK The member ID
* @param string The ticket ID (doesn't have to exist)
* @param integer The ticket type
* @param LONG_TEXT The post title
* @param LONG_TEXT The post content in Comcode format
* @param string The home URL
* @param boolean Whether the reply is staff only (invisible to ticket owner, only on OCF)
*/
function ticket_add_post($member,$ticket_id,$ticket_type,$title,$post,$ticket_url,$staff_only=false)
{
// Get the forum ID first
$fid=$GLOBALS['SITE_DB']->query_value_null_ok('tickets','forum_id',array('ticket_id'=>$ticket_id));
if (is_null($fid)) $fid=get_ticket_forum_id($member,$ticket_type);
$GLOBALS['FORUM_DRIVER']->make_post_forum_topic(
$fid,
$ticket_id,
$member,
$title,
$post,
$title,
do_lang('SUPPORT_TICKET'),
$ticket_url,
NULL,
NULL,
1,
1,
false,
'',
NULL,
$staff_only
);
$topic_id=$GLOBALS['LAST_TOPIC_ID'];
$is_new=$GLOBALS['LAST_TOPIC_IS_NEW'];
if (($is_new) && ($ticket_type!=-1))
{
$GLOBALS['SITE_DB']->query_insert('tickets',array('ticket_id'=>$ticket_id,'forum_id'=>$fid,'topic_id'=>$topic_id,'ticket_type'=>$ticket_type));
}
}
/**
* Send an e-mail notification for a new post in a support ticket, either to the staff or to the ticket's owner.
*
* @param string The ticket ID
* @param LONG_TEXT The ticket title
* @param LONG_TEXT The ticket post's content
* @param mixed The home URL (to view the ticket) (URLPATH or Tempcode URL)
* @param string Ticket owner's e-mail address, in the case of a new ticket
* @param integer The new ticket type, or -1 if it is a reply to an existing ticket
*/
function send_ticket_email($ticket_id,$title,$post,$ticket_url,$email,$ticket_type_if_new)
{
require_lang('tickets');
require_code('notifications');
$_temp=explode('_',$ticket_id);
$uid=intval($_temp[0]);
$username=$GLOBALS['FORUM_DRIVER']->get_username($uid);
if (is_null($username)) $username=do_lang('UNKNOWN');
$new_ticket=($ticket_type_if_new!=-1);
$ticket_type_id=$GLOBALS['SITE_DB']->query_value_null_ok('tickets','ticket_type',array('ticket_id'=>$ticket_id));
$ticket_type_text=mixed();
if (($uid!=get_member()) && (!is_guest($uid)))
{
// Reply from staff, notification to user
$ticket_type_text=$GLOBALS['SITE_DB']->query_value_null_ok('tickets t LEFT JOIN '.$GLOBALS['SITE_DB']->get_table_prefix().'translate tr ON t.ticket_type=tr.id','text_original',array('ticket_id'=>$ticket_id));
$their_lang=get_lang($uid);
$subject=do_lang('TICKET_REPLY',$ticket_type_text,$ticket_type_text,($title=='')?do_lang('UNKNOWN'):$title,$their_lang);
$post_tempcode=comcode_to_tempcode($post);
if (trim($post_tempcode->evaluate())!='')
{
$message=do_lang('TICKET_REPLY_MESSAGE',comcode_escape(($title=='')?do_lang('UNKNOWN'):$title),comcode_escape($ticket_url),array(comcode_escape($GLOBALS['FORUM_DRIVER']->get_username(get_member())),$post,comcode_escape($ticket_type_text)),$their_lang);
dispatch_notification('ticket_reply',strval($ticket_type_id),$subject,$message,array($uid));
}
}
elseif ($uid==get_member())
{
// Reply from user, notification to staff
if (is_object($ticket_url)) $ticket_url=$ticket_url->evaluate();
if (is_null($ticket_type_text))
{
$ticket_type_text=($ticket_type_if_new==-1)?'':get_translated_text($ticket_type_if_new);
}
$subject=do_lang($new_ticket?'TICKET_NEW_STAFF':'TICKET_REPLY_STAFF',$ticket_type_text,($title=='')?do_lang('UNKNOWN'):$title,NULL,get_site_default_lang());
$message=do_lang($new_ticket?'TICKET_NEW_MESSAGE_FOR_STAFF':'TICKET_REPLY_MESSAGE_FOR_STAFF',comcode_escape(($title=='')?do_lang('UNKNOWN'):$title),comcode_escape($ticket_url),array(comcode_escape($username),$post,comcode_escape($ticket_type_text)),get_site_default_lang());
dispatch_notification($new_ticket?'ticket_new_staff':'ticket_reply_staff',strval($ticket_type_id),$subject,$message);
// Tell user that their message was received
if ($email!='')
{
require_code('mail');
mail_wrap(do_lang('YOUR_MESSAGE_WAS_SENT_SUBJECT',($title=='')?do_lang('UNKNOWN'):$title),do_lang('YOUR_MESSAGE_WAS_SENT_BODY',$post),array($email),NULL,'','',3,NULL,false,get_member());
}
}
}
/**
* Is the given ticket post intended for staff only? Works only on OCF.
*
* @param array Array of data for the post
* @return boolean Whether the post's staff only
*/
function is_ticket_post_staff_only($post)
{
if (get_forum_type()!='ocf')
return false;
return ((!is_null($post['p_intended_solely_for'])) && (is_guest($post['p_intended_solely_for'])));
}
Jump to Line
Something went wrong with that request. Please try again.