Files
IPB/applications/nexus/sources/Support/Request.php
T
2025-12-19 04:57:54 -08:00

1703 lines
50 KiB
PHP

<?php
/**
* @brief Support Request Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @subpackage Nexus
* @since 8 Apr 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\nexus\Support;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Support Request Model
*/
class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
{
/**
* @brief Multiton Store
*/
protected static $multitons;
/**
* @brief Application
*/
public static $application = 'nexus';
/**
* @brief Module
*/
public static $module = 'support';
/**
* @brief Database Table
*/
public static $databaseTable = 'nexus_support_requests';
/**
* @brief Database Prefix
*/
public static $databasePrefix = 'r_';
/**
* @brief Database Column Map
*/
public static $databaseColumnMap = array(
'title' => 'title',
'author' => 'member',
'date' => 'started',
'num_comments' => 'replies',
'last_comment' => 'last_reply',
'last_comment_by' => 'last_reply_by'
);
/**
* @brief Title
*/
public static $title = 'support_request';
/**
* @brief Form language prefix
*/
public static $formLangPrefix = 'support_';
/**
* @brief Comment Class
*/
public static $commentClass = 'IPS\nexus\Support\Reply';
/**
* @brief [Content\Item] First "comment" is part of the item?
*/
public static $firstCommentRequired = TRUE;
/**
* @brief Node Class
*/
public static $containerNodeClass = 'IPS\nexus\Support\Department';
/* !Generic */
/**
* Should posting this increment the poster's post count?
*
* @param \IPS\Node\Model|NULL $container Container
* @return void
*/
public static function incrementPostCount( \IPS\Node\Model $container = NULL )
{
return FALSE;
}
/**
* My Filters
*
* @param array|null $_filters Filters (NULL to get from cookie)
* @return array
*/
public static function myFilters( $_filters=null )
{
$myFilters = array();
$myDepartmentIds = NULL;
$myStatusIds = NULL;
if ( $_filters === null and isset( \IPS\Request::i()->cookie['support_filters'] ) )
{
$_filters = json_decode( \IPS\Request::i()->cookie['support_filters'], TRUE );
}
if ( $_filters !== null )
{
$myDepartmentIds = array_filter( $_filters['departments'] );
$myStatusIds = array_filter( $_filters['statuses'] );
}
if ( !$myStatusIds )
{
$myStatusIds = iterator_to_array( \IPS\Db::i()->select( 'status_id', 'nexus_support_statuses', array( 'status_open=1' ) ) );
}
if ( !$myDepartmentIds )
{
$myDepartmentIds = iterator_to_array( \IPS\Db::i()->select( 'dpt_id', 'nexus_support_departments', "dpt_staff='*' OR " . \IPS\Db::i()->findInSet( 'dpt_staff', \IPS\nexus\Support\Department::staffDepartmentPerms() ) ) );
}
$myFilters = \IPS\Db::i()->in( 'r_department', $myDepartmentIds ) . ' AND ' . \IPS\Db::i()->in( 'r_status', $myStatusIds );
return array( 'whereClause' => $myFilters, 'departments' => $myDepartmentIds, 'statuses' => $myStatusIds );
}
/**
* Change Filters
*
* @param array $currentFilters The current filters
* @param int $id The ID to set/unset
* @return array
*/
public static function changeFilters( $currentFilters, $id )
{
$index = array_search( $id, $currentFilters );
if ( $index === FALSE )
{
$currentFilters[] = $id;
}
else
{
unset( $currentFilters[ $index ] );
}
return $currentFilters;
}
/**
* Support Desk Staff
*
* @return array
*/
public static function staff()
{
$return = array();
foreach ( \IPS\Db::i()->select( '*', 'core_admin_permission_rows' ) as $row )
{
if ( $row['row_id_type'] === 'group' )
{
foreach ( \IPS\Db::i()->select( '*', 'core_members', array( 'member_group_id=? OR ' . \IPS\Db::i()->findInSet( 'mgroup_others', array( $row['row_id'] ) ), $row['row_id'] ) ) as $member )
{
$return[ $member['member_id'] ] = $member['name'];
}
}
else
{
$return[ $row['row_id'] ] = \IPS\Member::load( $row['row_id'] )->name;
}
}
asort( $return );
return $return;
}
/**
* Table
*
*
* @return \IPS\Helpers\Table\Db
*/
public static function table( \IPS\Http\Url $url, $where )
{
$table = new \IPS\Helpers\Table\Db( 'nexus_support_requests', $url, $where );
$table->joins = array(
array(
'select' => 'sev_position',
'from' => 'nexus_support_severities',
'where' => 'sev_id=r_severity'
),
array(
'select' => 'dpt_position, dpt_staff',
'from' => 'nexus_support_departments',
'where' => 'dpt_id=r_department'
),
array(
'select' => 'status_position',
'from' => 'nexus_support_statuses',
'where' => 'status_id=r_status'
),
array(
'select' => 'request_id',
'from' => 'nexus_support_tracker',
'where' => array( 'request_id=r_id AND member_id=?', \IPS\Member::loggedIn()->member_id )
)
);
$table->include = array( 'r_id', 'r_title', 'sev_position', 'dpt_position', 'r_member', 'r_replies', 'r_started', 'r_staff', 'r_last_new_reply', 'r_last_reply_by', 'r_last_reply', 'r_last_staff_reply', 'status_position' );
$table->keyField = 'r_id';
$table->extra = array();
$highlightCutoff = NULL;
if ( \IPS\Settings::i()->support_old_number )
{
$table->include[] = '_highlight';
$highlightCutoff = \IPS\DateTime::create()->sub( new \DateInterval( 'PT' . \IPS\Settings::i()->support_old_number . 'M' ) )->getTimestamp();
}
$table->sortBy = $table->sortBy ? "sev_position asc, {$table->sortBy}" : 'sev_position asc, r_last_new_reply';
$table->sortDirection = $table->sortDirection ?: 'asc';
$table->parsers = array(
'r_title' => function( $val, $row ) use ( $table )
{
$filter = NULL;
$sort = NULL;
if ( !isset( \IPS\Request::i()->member ) and !isset( \IPS\Request::i()->quicksearch ) and !isset( \IPS\Request::i()->advanced_search_submitted ) )
{
$filter = $table->filter;
$sort = $table->sortBy . ' ' . $table->sortDirection;
}
return \IPS\Theme::i()->getTemplate('support')->link( \IPS\nexus\Support\Request::load( $row['r_id'] ), $row['request_id'], $filter, $sort );
},
'sev_position'=> function( $val, $row )
{
return \IPS\Theme::i()->getTemplate('support')->severity( \IPS\nexus\Support\Severity::load( $row['r_severity'] ) );
},
'dpt_position'=> function( $val, $row )
{
return \IPS\Theme::i()->getTemplate('support')->department( \IPS\nexus\Support\Department::load( $row['r_department'] ) );
},
'r_member' => function( $val, $row )
{
if ( $val )
{
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( $val ) );
}
else
{
return htmlentities( $row['r_email'], \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
},
'r_last_reply_by' => function( $val, $row )
{
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( $val ) );
},
'r_replies' => function( $val )
{
return --$val;
},
'r_started' => function( $val )
{
/* Use formatInterval to show an elapsed time here rather than a simple date */
return \IPS\DateTime::formatInterval( \IPS\DateTime::ts( $val )->diff( new \IPS\DateTime ), 2 );
},
'r_staff' => function ( $val )
{
if ( $val )
{
return \IPS\Member::load( $val )->name;
}
},
'r_last_new_reply' => function( $val )
{
return \IPS\DateTime::ts( $val )->relative();
},
'r_last_reply' => function( $val )
{
return \IPS\DateTime::ts( $val )->relative();
},
'r_last_staff_reply' => function( $val )
{
return $val ? \IPS\DateTime::ts( $val )->relative() : \IPS\Member::loggedIn()->language()->addToStack('never');
},
'status_position' => function( $val, $row )
{
return \IPS\Theme::i()->getTemplate('support')->status( \IPS\nexus\Support\Status::load( $row['r_status'] ) );
},
'_highlight' => function( $val, $row ) use ( $table, $highlightCutoff )
{
if ( $row[ \IPS\Settings::i()->support_old_stat ] < $highlightCutoff )
{
$table->extra[] = $row['r_id'];
}
}
);
return $table;
}
/* !Getters/Setters */
/**
* Set Default Values
*
* @return void
*/
public function setDefaultValues()
{
$emailKey = '';
for ( $i = 0; $i < 3; $i++ )
{
do
{
$num = mt_rand( 48, 122 );
}
while ( in_array( $num, array( 58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96 ) ) );
$emailKey .= chr( $num );
}
$this->email_key = $emailKey;
$this->last_new_reply = time();
$this->staff = NULL;
$this->purchase = NULL;
}
/**
* Get department
*
* @return \IPS\nexus\Support\Department
*/
public function get_department()
{
return Department::load( $this->_data['department'] );
}
/**
* Set department
*
* @param \IPS\nexus\Support\Department
* @return void
*/
public function set_department( Department $department )
{
$this->_data['department'] = $department->id;
}
/**
* Get purchase
*
* @return \IPS\nexus\Purchase
*/
public function get_purchase()
{
try
{
return $this->_data['purchase'] ? \IPS\nexus\Purchase::load( $this->_data['purchase'] ) : NULL;
}
catch ( \OutOfRangeException $e )
{
return NULL;
}
}
/**
* Set purchase
*
* @param \IPS\nexus\Purchase
* @return void
*/
public function set_purchase( \IPS\nexus\Purchase $purchase = NULL )
{
$this->_data['purchase'] = $purchase ? $purchase->id : 0;
}
/**
* Get status
*
* @return \IPS\nexus\Support\Status
*/
public function get_status()
{
return Status::load( $this->_data['status'] );
}
/**
* Set purchase
*
* @param \IPS\nexus\Support\Status
* @return void
*/
public function set_status( Status $status )
{
$this->_data['status'] = $status->id;
}
/**
* Get severity
*
* @return \IPS\nexus\Support\Severity
*/
public function get_severity()
{
return Severity::load( $this->_data['severity'] );
}
/**
* Set severity
*
* @param \IPS\nexus\Support\Severity
* @return void
*/
public function set_severity( Severity $severity )
{
$this->_data['severity'] = $severity->id;
}
/**
* Set last reply by
*
* @param int $lastReplyBy
* @return void
*/
public function set_last_reply_by( $lastReplyBy )
{
if ( array_key_exists( $lastReplyBy, static::staff() ) )
{
$this->_data['last_staff_reply'] = time();
\IPS\Db::i()->update( 'nexus_support_views', array( 'view_reply' => time() ), array( 'view_rid=? AND view_member=?', $this->id, $lastReplyBy ) );
}
elseif ( !isset( $this->_data['last_reply_by'] ) or $lastReplyBy != $this->_data['last_reply_by'] )
{
$this->_data['last_new_reply'] = time();
}
$this->_data['last_reply_by'] = $lastReplyBy;
}
/**
* Get staff
*
* @return \IPS\Member|NULL
*/
public function get_staff()
{
return $this->_data['staff'] ? \IPS\Member::load( $this->_data['staff'] ) : NULL;
}
/**
* Set staff
*
* @param \IPS\Member|NULL $member The staff member to assign to
* @return void
*/
public function set_staff( \IPS\Member $member = NULL )
{
$this->_data['staff'] = $member ? $member->member_id : 0;
}
/**
* Get notify
*
* @return array
*/
public function get_notify()
{
return isset( $this->_data['notify'] ) ? json_decode( $this->_data['notify'], TRUE ) : array();
}
/**
* Set notify
*
* @param array $notify Notify data
* @return void
*/
public function set_notify( array $notify )
{
$this->_data['notify'] = json_encode( $notify );
}
/**
* Get custom fields
*
* @return array
*/
public function get_cfields()
{
return $this->_data['cfields'] ? json_decode( $this->_data['cfields'], TRUE ) : array();
}
/**
* Set custom fields
*
* @param array $values Values
* @return void
*/
public function set_cfields( array $values )
{
$this->_data['cfields'] = json_encode( $values );
}
/**
* Get PPI invoice
*
* @return \IPS\nexus\Invoice|NULL
*/
public function get_ppi_invoice()
{
return $this->_data['ppi_invoice'] ? \IPS\nexus\Invoice::load( $this->_data['ppi_invoice'] ) : NULL;
}
/**
* Set PPI invoice
*
* @param \IPS\nexus\Invoice|NULL
* @return void
*/
public function set_ppi_invoice( \IPS\nexus\Invoice $invoice = NULL )
{
$this->_data['ppi_invoice'] = $invoice ? $invoice->id : NULL;
}
/* !Forms */
/**
* Get elements for add/edit form
*
* @param \IPS\Content\Item|NULL $item The current item if editing or NULL if creating
* @param \IPS\Node\Model|NULL $container Container (e.g. forum), if appropriate
* @return array
*/
public static function formElements( $item=NULL, \IPS\Node\Model $container=NULL )
{
/* Account */
if ( isset( \IPS\Request::i()->account ) and ( \IPS\Request::i()->account == \IPS\nexus\Customer::loggedIn()->member_id or array_key_exists( \IPS\Request::i()->account, iterator_to_array( \IPS\nexus\Customer::loggedIn()->parentContacts( array( 'support=1' ) ) ) ) ) )
{
$account = \IPS\nexus\Customer::load( \IPS\Request::i()->account );
$purchaseWhere = array( array( 'ps_member=?', $account->member_id ) );
}
else
{
$account = \IPS\nexus\Customer::loggedIn();
$purchaseWhere = array();
foreach ( $account->parentContacts() as $contact )
{
foreach ( $contact->purchaseIds() as $id )
{
$purchaseWhere[] = "ps_id={$id}";
}
}
if ( count( $purchaseWhere ) )
{
$purchaseWhere = array( array( "ps_member={$account->member_id} OR ( " . implode( ' OR ', $purchaseWhere ) . ' )' ) );
}
else
{
$purchaseWhere = array( array( 'ps_member=?', $account->member_id ) );
}
}
/* Basic elements */
$return = parent::formElements( $item, $container );
$content = $return['content'];
unset( $return['content'] );
unset( $return['container'] );
/* Init */
$availableDepartments = array();
$doNotShowPurchasesForDepartments = array();
foreach ( Department::roots( NULL, NULL, 'dpt_open=1' ) as $department )
{
if ( !\IPS\nexus\Customer::loggedIn()->member_id or !count( \IPS\Db::i()->select( '*', 'nexus_purchases', array_merge( $purchaseWhere, array( array( 'ps_app=? AND ps_type=?', 'nexus', 'package' ) ), array( \IPS\Db::i()->in( 'ps_item_id', explode( ',', $department->packages ) ) ) ) ) ) )
{
if ( $department->require_package )
{
continue;
}
else
{
$doNotShowPurchasesForDepartments[] = $department->id;
}
}
$availableDepartments[ $department->id ] = $department;
}
if ( !count( $availableDepartments ) )
{
\IPS\Output::i()->error( \IPS\nexus\Customer::loggedIn()->member_id ? 'no_module_permission' : 'no_module_permission_guest', '1X248/1', 403, '' );
}
$departmentToggles = array();
/* What custom fields need to be triggered by departments? */
$customFields = \IPS\nexus\Support\CustomField::roots( NULL, NULL, "sf_departments='*' OR " . \IPS\Db::i()->findInSet( 'sf_departments', array_keys( $availableDepartments ) ) );
foreach ( $customFields as $field )
{
if ( $field->departments and $field->departments !== '*' )
{
foreach ( explode( ',', $field->departments ) as $departmentId )
{
if ( !isset( $departmentToggles[ $departmentId ] ) )
{
$departmentToggles[ $departmentId ] = array();
}
$departmentToggles[ $departmentId ][] = "nexus_cfield_{$field->id}";
}
}
}
/* What purchase fields need to be triggered by departments? */
$departments = array();
$purchaseFields = array();
$ppiDepartments = array();
foreach ( $availableDepartments as $department )
{
$departments[ $department->id ] = $department->_title;
if ( !isset( $departmentToggles[ $department->id ] ) )
{
$departmentToggles[ $department->id ] = array();
}
$departmentToggles[ $department->id ][] = "department_message_{$department->id}";
if ( $department->ppiCost() and !\IPS\Db::i()->select( 'COUNT(*)', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=? AND ps_member=?', 'nexus', 'ppi', $department->id, \IPS\Member::loggedIn()->member_id ) )->first() )
{
$content->required = FALSE;
if ( $content->error === 'form_required' )
{
$content->error = NULL;
}
$ppiDepartments[] = $department->id;
}
else
{
$departmentToggles[ $department->id ][] = static::$formLangPrefix . 'content_editor';
if ( $department->packages and !in_array( $department->id, $doNotShowPurchasesForDepartments ) )
{
$allowedPackageIds = explode( ',', $department->packages );
$allowedPackages = $department->serializedPackageIds();
$departmentToggles[ $department->id ][] = 'support_purchase_' . $allowedPackages;
if ( !isset( $purchaseFields[ $allowedPackages ] ) )
{
$field = new \IPS\Helpers\Form\Node(
'support_purchase_' . $allowedPackages,
isset( \IPS\Request::i()->purchase ) ? \IPS\Request::i()->purchase : NULL,
$department->require_package ? NULL : FALSE,
array(
'class' => 'IPS\nexus\Purchase',
'where' => $purchaseWhere,
'permissionCheck' => function( $node ) use ( $allowedPackageIds )
{
return $node->active and $node->app === 'nexus' and $node->type === 'package' and in_array( $node->item_id, $allowedPackageIds );
}
),
$department->require_package ? function( $val ) use ( $allowedPackages, $departmentToggles )
{
if ( !$val and in_array( 'support_purchase_' . $allowedPackages, $departmentToggles[ \IPS\Request::i()->support_department ] ) )
{
throw new \DomainException('support_purchase_required');
}
} : NULL,
NULL,
NULL,
'support_purchase_' . $allowedPackages
);
$field->label = \IPS\Member::loggedIn()->language()->addToStack('support_purchase');
$purchaseFields[ $allowedPackages ] = $field;
}
}
}
}
/* Do severities need to be triggered by departments? */
$defaultSeverityId = Severity::load( TRUE, 'sev_default' )->id;
$severityFields = array();
foreach ( $availableDepartments as $department )
{
if ( count( $department->availableSeverities() ) > 1 )
{
$serialized = "severity_{$department->serializedSeverityIds()}";
if ( !isset( $severityFields[ $serialized ] ) )
{
$options = array();
foreach ( $department->availableSeverities() as $severity )
{
$options[ $severity->id ] = "nexus_severity_{$severity->id}";
}
$field = new \IPS\Helpers\Form\Radio( 'support_severity_' . $serialized, in_array( $defaultSeverityId, array_keys( $options ) ) ? $defaultSeverityId : NULL, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'support_severity_' . $serialized );
$field->label = \IPS\Member::loggedIn()->language()->addToStack('support_severity');
$severityFields[ $serialized ] = $field;
}
if ( !isset( $departmentToggles[ $department->id ] ) )
{
$departmentToggles[ $department->id ] = array();
}
$departmentToggles[ $department->id ][] = 'support_severity_' . $serialized;
}
}
/* Add department */
$return['department'] = new \IPS\Helpers\Form\Select( 'support_department', isset( \IPS\Request::i()->department ) ? \IPS\Request::i()->department : NULL, TRUE, array( 'options' => $departments, 'toggles' => $departmentToggles ) );
foreach ( $availableDepartments as $department )
{
if ( \IPS\Member::loggedIn()->language()->checkKeyExists("nexus_department_{$department->id}_desc") )
{
$return[ "department_message_{$department->id}" ] = \IPS\Member::loggedIn()->language()->addToStack("nexus_department_{$department->id}_desc");
}
}
/* Severity */
$return = array_merge( $return, $severityFields );
/* Purchase */
$return = array_merge( $return, $purchaseFields );
/* Custom Fields */
foreach ( $customFields as $field )
{
$validation = NULL;
if ( $field->required )
{
$validation = function( $val ) use ( $field, $departmentToggles ) {
if ( $val === '' and ( !isset( \IPS\Request::i()->support_department ) or in_array( "nexus_cfield_{$field->id}", $departmentToggles[ \IPS\Request::i()->support_department ] ) ) )
{
throw new \DomainException('form_required');
}
};
}
$return[] = $field->buildHelper( NULL, $validation );
}
/* Return */
$return['content'] = $content;
return $return;
}
/**
* Process create/edit form
*
* @param array $values Values from form
* @return void
*/
public function processForm( $values )
{
parent::processForm( $values );
/* Pay-Per-Incident? */
$department = Department::load( $values['support_department'] );
if ( $ppiCost = $department->ppiCost() )
{
try
{
$purchase = \IPS\nexus\Purchase::constructFromData( \IPS\Db::i()->select( '*', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=?', 'nexus', 'ppi', $department->id ) )->first() );
$this->ppi_invoice = $purchase->original_invoice;
$purchase->delete();
}
catch ( \UnderflowException $e )
{
$invoice = new \IPS\nexus\Invoice;
$invoice->member = \IPS\nexus\Customer::loggedIn();
$invoice->return_uri = "app=nexus&module=support&controller=home&do=create&department={$department->id}&title=" . urlencode( $values['support_title'] );
$item = new \IPS\nexus\extensions\nexus\Item\SupportCharge( $department->_title, $ppiCost );
$item->id = $department->id;
if ( $department->ppi_tax )
{
try
{
$item->tax = \IPS\nexus\Tax::load( $department->ppi_tax );
}
catch ( \OutOfRangeException $e ) {}
}
$invoice->addItem( $item );
$invoice->save();
\IPS\Output::i()->redirect( $invoice->checkoutUrl() );
}
}
$this->department = $department;
/* Purchase */
if ( $department->packages and isset( $values[ 'support_purchase_' . $department->serializedPackageIds() ] ) )
{
$purchase = $values[ 'support_purchase_' . $department->serializedPackageIds() ];
if ( $purchase )
{
$this->purchase = $values[ 'support_purchase_' . $department->serializedPackageIds() ];
}
}
/* Account */
if ( isset( \IPS\Request::i()->account ) and ( \IPS\Request::i()->account == \IPS\nexus\Customer::loggedIn()->member_id or array_key_exists( \IPS\Request::i()->account, iterator_to_array( \IPS\nexus\Customer::loggedIn()->parentContacts( array( 'support=1' ) ) ) ) ) )
{
$this->member = \IPS\nexus\Customer::load( \IPS\Request::i()->account )->member_id;
}
elseif ( $this->purchase )
{
$this->member = $this->purchase->member->member_id;
}
/* Status */
if ( !isset( $values['support_status'] ) )
{
$this->status = Status::load( TRUE, 'status_default_member' );
}
/* Selected Severity */
$defaultSeverity = Severity::load( TRUE, 'sev_default' );
if ( isset( $values[ 'support_severity_' . $department->serializedSeverityIds() ] ) )
{
$this->severity = Severity::load( $values[ 'support_severity_' . $department->serializedSeverityIds() ] );
}
else
{
$availableSeverities = $department->availableSeverities();
if ( count( $availableSeverities ) === 1 )
{
$this->severity = array_pop( $availableSeverities );
}
else
{
$this->severity = $defaultSeverity;
}
}
/* Purchase sets its own severity? */
if ( $this->severity->id === $defaultSeverity->id and $this->purchase )
{
if ( $overrideSeverity = $this->purchase->supportSeverity() )
{
$this->severity = $overrideSeverity;
}
elseif ( $parent = $this->purchase->parent() and $overrideSeverity = $parent->supportSeverity() )
{
$this->severity = $overrideSeverity;
}
else
{
foreach ( $this->purchase->children() as $childPurchase )
{
if ( $overrideSeverity = $childPurchase->supportSeverity() )
{
$this->severity = $overrideSeverity;
break;
}
}
}
}
/* Custom Fields */
$customFieldObjects = CustomField::roots();
$cfields = array();
foreach ( $values as $k => $v )
{
if ( mb_substr( $k, 0, 13 ) === 'nexus_cfield_' )
{
$k = mb_substr( $k, 13 );
$class = $customFieldObjects[ $k ]->buildHelper();
$cfields[ $k ] = $class::stringValue( $v );
}
}
$this->cfields = $cfields;
}
/**
* Process created object AFTER the object has been created
*
* @param \IPS\Content\Comment|NULL $comment The first comment
* @param array $values Values from form
* @return void
*/
public function processAfterCreate( $comment, $values )
{
if ( $this->member )
{
$data = array( 'id' => $this->id, 'title' => $this->title );
if ( $comment->type === Reply::REPLY_STAFF )
{
$data['type'] = 'staff';
}
elseif ( $comment->type === Reply::REPLY_EMAIL )
{
$data['type'] = 'email';
}
\IPS\nexus\Customer::load( $this->member )->log( 'support', $data );
}
parent::processAfterCreate( $comment, $values );
}
/**
* Set buttons
*
* @param \IPS\Helpers\Form $form The form
* @param string $key 'note' or 'reply'
* @return void
*/
protected function _staffFormButtons( \IPS\Helpers\Form $form, $key )
{
if ( isset( \IPS\Request::i()->sort ) )
{
$form->hiddenValues['sort'] = \IPS\Request::i()->sort;
$form->hiddenValues['filter'] = \IPS\Request::i()->filter;
$form->addButton( $key . '_and_return_to_list', 'submit', NULL, 'ipsButton ipsButton_alternate', array( 'name' => 'goto', 'value' => 'list' ) );
if ( $next = $this->nextPrevious( \IPS\Request::i()->sort, \IPS\Request::i()->filter ) )
{
$form->addButton( $key . '_and_go_to_next', 'submit', NULL, 'ipsButton ipsButton_alternate', array( 'name' => 'goto', 'value' => 'next' ) );
}
}
else
{
$form->addButton( $key . '_and_go_to_list', 'submit', NULL, 'ipsButton ipsButton_alternate', array( 'name' => 'goto', 'value' => 'list' ) );
}
}
/**
* Redirect after submission
*
* @param bool $pending If the submitted comment is pending
* @return void
*/
protected function _staffFormRedirect( $pending=FALSE )
{
if ( !$pending and isset( \IPS\Request::i()->goto ) )
{
switch ( \IPS\Request::i()->goto )
{
case 'next':
if ( $next = $this->nextPrevious( \IPS\Request::i()->sort, \IPS\Request::i()->filter ) )
{
\IPS\Output::i()->redirect( $next->acpUrl()->setQueryString( array( 'sort' => \IPS\Request::i()->sort, 'filter' => \IPS\Request::i()->filter ) ) );
}
break;
case 'list':
$listUrl = \IPS\Http\Url::internal("app=nexus&module=support&controller=requests");
if ( isset( \IPS\Request::i()->sort ) )
{
preg_match( '/^(([a-z_]+(, )?)+)( ((a|de)sc))?$/i', trim( \IPS\Request::i()->sort ), $matches );
if ( isset( $matches[1] ) )
{
$listUrl = $listUrl->setQueryString( 'sortby', $matches[1] );
}
if ( isset( $matches[5] ) )
{
$listUrl = $listUrl->setQueryString( 'sortdirection', $matches[5] );
}
}
if ( isset( \IPS\Request::i()->filter ) )
{
$listUrl = $listUrl->setQueryString( 'filter', \IPS\Request::i()->filter );
}
\IPS\Output::i()->redirect( $listUrl );
break;
}
}
$lastPageUrl = $this->acpUrl();
$order = isset( \IPS\Request::i()->order ) ? \IPS\Request::i()->order : ( isset( \IPS\Request::i()->cookie['support_order'] ) ? \IPS\Request::i()->cookie['support_order'] : 'desc' );
if ( $order === 'asc' )
{
$lastPageUrl = $lastPageUrl->setQueryString( 'page', $this->commentPageCount() );
}
if ( $pending )
{
$lastPageUrl = $lastPageUrl->setQueryString( 'pending', 1 );
}
\IPS\Output::i()->redirect( $lastPageUrl );
}
/**
* Add Note Form
*
* @return \IPS\Helpers\Form
*/
public function noteForm()
{
$form = new \IPS\Helpers\Form( 'note', 'add_note' );
$form->class = 'ipsForm_vertical';
$form->add( new \IPS\Helpers\Form\Editor( 'note', NULL, TRUE, array( 'app' => 'nexus', 'key' => 'Support', 'minimize' => 'support__note_placeholder', 'autoSaveKey' => md5( "req{$this->id}-note" ) ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'department', $this->department->id, FALSE, array( 'options' => \IPS\nexus\Support\Department::rootsAsArray() ), NULL, NULL, NULL, 'staffNoteDepartment' ) );
$form->add( new \IPS\Helpers\Form\Select( 'status', $this->status->id, FALSE, array( 'options' => \IPS\nexus\Support\Status::rootsAsArray() ), NULL, NULL, NULL, 'staffNoteStatus' ) );
$form->add( new \IPS\Helpers\Form\Select( 'assign_to', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'options' => array_merge( array( 0 => 'unassigned' ), static::staff() ) ), NULL, NULL, NULL, 'staffNoteAssign' ) );
/* Buttons */
$this->_staffFormButtons( $form, 'note' );
/* Handle submissions */
if ( $values = $form->values() )
{
$message = new Reply;
$message->request = $this->id;
$message->member = \IPS\Member::loggedIn()->member_id;
$message->type = Reply::REPLY_HIDDEN;
$message->post = $values['note'];
$message->hidden = TRUE;
$message->date = time();
$message->ip_address = \IPS\Request::i()->ipAddress();
$message->save();
\IPS\File::claimAttachments( 'reply-nexus/support-' . $this->id, $this->id, $message->id );
/* Update the request */
$this->department = Department::load( $values['department'] );
$this->status = Status::load( $values['status'] );
$this->staff = $values['assign_to'] ? \IPS\Member::load( $values['assign_to'] ) : NULL;
$this->save();
$message->sendNotifications();
/* Redirect */
$this->_staffFormRedirect();
}
return $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'support', 'nexus' ) ), 'noteForm' ), $this );
}
/**
* Staff Reply Form
*
* @return \IPS\Helpers\Form
*/
public function staffReplyForm()
{
/* Init */
$form = new \IPS\Helpers\Form( 'reply', 'reply' );
$form->attributes = array( 'data-controller' => 'nexus.admin.support.staffreply' );
$lastReply = $this->comments( 1, 0, 'date', 'desc' );
$lastReplyId = $lastReply->id;
$form->hiddenValues['latestReply'] = $lastReplyId;
/* Stock Actions */
$stockActions = array( 0 => '' );
foreach ( \IPS\nexus\Support\StockAction::roots( NULL, NULL, "action_show_in='*' OR " . \IPS\Db::i()->findInSet( 'action_show_in', array( $this->department->id ) ) ) as $action )
{
$stockActions[ $action->id ] = $action->_title;
}
/* To/Cc/Bcc */
$defaultRecipients = $this->getDefaultRecipients();
$form->add( new \IPS\Helpers\Form\Email( 'to', $defaultRecipients['to'], FALSE, array( 'disabled' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'cc', $defaultRecipients['cc'], FALSE, array( 'autocomplete' => array( 'unique' => TRUE, 'forceLower' => TRUE ) ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'bcc', $defaultRecipients['bcc'], FALSE, array( 'autocomplete' => array( 'unique' => TRUE, 'forceLower' => TRUE ) ) ) );
/* Attributes */
if ( count( \IPS\nexus\Support\StockAction::roots() ) )
{
$form->add( new \IPS\Helpers\Form\Select( 'stock_action', NULL, FALSE, array( 'options' => $stockActions ), NULL, NULL, NULL, 'stock_action' ) );
}
$form->add( new \IPS\Helpers\Form\Editor( 'message', NULL, TRUE, array( 'app' => 'nexus', 'key' => 'Support', 'minimize' => 'support__comment_placeholder', 'autoSaveKey' => md5( "req{$this->id}-reply" ) ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'department', $this->department->id, FALSE, array( 'options' => \IPS\nexus\Support\Department::rootsAsArray() ), NULL, NULL, NULL, 'staffReplyDepartment' ) );
$form->add( new \IPS\Helpers\Form\Select( 'status', \IPS\nexus\Support\Status::load( TRUE, 'status_default_staff' )->id, FALSE, array( 'options' => \IPS\nexus\Support\Status::rootsAsArray() ), NULL, NULL, NULL, 'staffReplyStatus' ) );
$form->add( new \IPS\Helpers\Form\Select( 'assign_to', $this->staff_lock ? ( $this->staff ? $this->staff->member_id : 0 ) : 0, FALSE, array( 'options' => array( 0 => 'unassigned' ) + static::staff() ), NULL, NULL, NULL, 'staffReplyAssign' ) );
/* Buttons */
$this->_staffFormButtons( $form, 'reply' );
/* Handle Submissions */
if ( $values = $form->values() )
{
/* Update notify */
$notify = $this->notify;
foreach ( $values['cc'] as $cc )
{
foreach ( $notify as $n )
{
if ( $n['value'] === $cc )
{
continue 2;
}
}
$notify[] = array( 'type' => 'e', 'value' => $cc );
}
foreach ( $values['bcc'] as $cc )
{
foreach ( $notify as $k => $n )
{
if ( $n['value'] === $cc )
{
if ( $n['bcc'] )
{
unset( $notify[ $k ]['bcc'] );
}
continue 2;
}
}
$notify[] = array( 'type' => 'e', 'value' => $cc, 'bcc' => 1 );
}
$this->notify = $notify;
/* Are we blocking this? */
$pending = FALSE;
if ( \IPS\Request::i()->latestReply != $lastReplyId )
{
$pending = TRUE;
}
/* Create the message */
$message = new Reply;
$message->request = $this->id;
$message->member = \IPS\Member::loggedIn()->member_id;
$message->type = $pending ? Reply::REPLY_PENDING : Reply::REPLY_STAFF;
$message->post = $values['message'];
$message->hidden = $pending;
$message->date = time();
$message->cc = implode( ',', $values['cc'] );
$message->bcc = implode( ',', $values['bcc'] );
$message->ip_address = \IPS\Request::i()->ipAddress();
$message->save();
\IPS\File::claimAttachments( 'reply-nexus/support-' . $this->id, $this->id, $message->id );
$message->postCreate();
/* Update the request */
$newDepartment = Department::load( $values['department'] );
if ( $this->department != $newDepartment )
{
$this->log( 'department', $this->department, $newDepartment );
$this->department = $newDepartment;
}
$this->status = Status::load( $values['status'] );
$newStaff = $values['assign_to'] ? \IPS\Member::load( $values['assign_to'] ) : NULL;
if ( $this->staff != $newStaff )
{
$this->log( 'staff', $this->staff, $newStaff );
$this->staff = $newStaff;
}
$this->save();
/* Send notifications */
if ( !$pending )
{
$message->sendCustomerNotifications( $values['to'] ?: $defaultRecipients['to'], $values['cc'], $values['bcc'] );
$message->sendNotifications();
}
/* Redirect */
$this->_staffFormRedirect( $pending );
}
/* Display */
return $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'support', 'nexus' ) ), 'staffReplyForm' ), $this );
}
/**
* Validate email
*
* @param string $email Email address
* @return void
* @throws \DomainException
*/
public static function _validateEmail( $email )
{
if ( $email and filter_var( $email, FILTER_VALIDATE_EMAIL ) === FALSE )
{
throw new \InvalidArgumentException('form_email_bad');
}
}
/* !Permissions */
/**
* Get items with permisison check
*
* @param array $where Where clause
* @param string $order MySQL ORDER BY clause (NULL to order by date)
* @param int|array $limit Limit clause
* @param string|NULL $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index or NULL to ignore permissions
* @param bool|NULL $includeHiddenItems Include hidden files? Boolean or NULL to detect if currently logged member has permission
* @param int $queryFlags Select bitwise flags
* @param \IPS\Member $member The member (NULL to use currently logged in member)
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
* @param bool $joinComments If true, will join comment data (set to TRUE if your $where clause depends on this data)
* @param bool $joinReviews If true, will join review data (set to TRUE if your $where clause depends on this data)
* @param bool $countOnly If true will return the count
* @param array|null $joins Additional arbitrary joins for the query
* @param mixed $skipPermission If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip conatiner-based permission. You must still specify this in the $where clause
* @param bool $joinTags If true, will join the tags table
* @param bool $joinAuthor If true, will join the members table for the author
* @param bool $joinLastCommenter If true, will join the members table for the last commenter
* @return \IPS\Patterns\ActiveRecordIterator|int
*/
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=NULL, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE )
{
/* Get customer object */
if ( !$member )
{
$member = \IPS\nexus\Customer::loggedIn();
}
elseif ( !( $member instanceof \IPS\nexus\Customer ) )
{
$member = \IPS\nexus\Customer::load( $member->member_id );
}
$extraClause = array( 'r_member=?', $member->member_id );
/* Work out the clause for parent alternative contacts */
$alternativeContactWhere = array();
foreach ( $member->parentContacts() as $contact )
{
if ( $contact->support )
{
$alternativeContactWhere[] = '( r_member=' . $contact->main_id->member_id . ' )';
}
else
{
$alternativeContactWhere[] = '( r_member=' . $contact->main_id->member_id . ' AND ' . \IPS\Db::i()->in( 'r_purchase', $contact->purchaseIds() ) . ' )';
}
}
if ( count( $alternativeContactWhere ) )
{
$extraClause[0] = '( ' . $extraClause[0] . ' OR ( ' . implode( ' OR ', $alternativeContactWhere ) . ' ) )';
}
/* Work out the clause for admins */
if ( ( !\IPS\Dispatcher::hasInstance() or \IPS\Dispatcher::i()->controllerLocation === 'admin' ) and $member->isAdmin() )
{
$extraClause[0] = '( ' . $extraClause[0] . " OR dpt_staff='*' OR " . \IPS\Db::i()->findInSet( 'dpt_staff', Department::staffDepartmentPerms( $member ) ) . ' )';
$joins[] = array(
'from' => 'nexus_support_departments',
'where' => 'dpt_id=r_department'
);
}
/* Do it */
$where[] = $extraClause;
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter );
}
/**
* Can view?
*
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
* @return bool
*/
public function canView( $member=NULL )
{
/* Get customer object */
if ( !$member )
{
$member = \IPS\nexus\Customer::loggedIn();
}
elseif ( !( $member instanceof \IPS\nexus\Customer ) )
{
$member = \IPS\nexus\Customer::load( $member->member_id );
}
/* Owner */
if ( $member->member_id == $this->author()->member_id )
{
return TRUE;
}
/* Staff */
elseif ( ( !\IPS\Dispatcher::hasInstance() or \IPS\Dispatcher::i()->controllerLocation === 'admin' ) and $member->isAdmin() and ( $this->department->staff === '*' or count( array_intersect( explode( ',', $this->department->staff ), Department::staffDepartmentPerms( $member ) ) ) ) )
{
return TRUE;
}
/* Altcontact */
elseif ( in_array( $this->author()->member_id, array_keys( iterator_to_array( $member->parentContacts( $this->purchase ? array( 'support=1 OR ' . \IPS\Db::i()->findInSet( 'purchases', array( $this->purchase->id ) ) ) : array( 'support=1' ) ) ) ) ) )
{
return TRUE;
}
return FALSE;
}
/**
* Can comment?
*
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
* @return bool
*/
public function canComment( $member=NULL )
{
return parent::canComment( $member ) and !$this->status->is_locked;
}
/* !Notifications */
/**
* Get default to/cc/bcc
*
* @return array
*/
public function getDefaultRecipients()
{
$to = $this->member ? $this->author()->email : $this->email;
$cc = array();
$bcc = array();
foreach ( $this->notify as $notify )
{
try
{
$email = ( $notify['type'] === 'm' ) ? \IPS\Member::load( $notify['value'] )->email : $notify['value'];
}
catch ( \OutOfRangeException $e )
{
continue;
}
if ( $email != $to )
{
if ( isset( $notify['bcc'] ) and $notify['bcc'] )
{
$bcc[] = $email;
}
else
{
$cc[] = $email;
}
}
}
return array( 'to' => $to, 'cc' => $cc, 'bcc' => $bcc );
}
/**
* Send notifications
*
* @return void
*/
public function sendNotifications()
{
$staffIds = array_keys( static::staff() );
foreach ( \IPS\Db::i()->select( 'staff_id', 'nexus_support_notify', array( array( 'type=?', 'n' ), array( "(departments='*' OR " . \IPS\Db::i()->findInSet( 'departments', array( $this->department->id ) ) . ')' ) ) ) as $staffId )
{
if ( in_array( $staffId, $staffIds ) )
{
$member = \IPS\Member::load( $staffId );
if ( $this->department->staff === '*' or count( array_intersect( \IPS\nexus\Support\Department::staffDepartmentPerms( $member ), explode( ',', $this->department->staff ) ) ) )
{
$email = \IPS\Email::buildFromTemplate( 'nexus', 'staffNotifyNew', array( $this ) );
$email->from = $this->department->email;
switch ( \IPS\Settings::i()->nexus_sout_from )
{
case 'staff':
$email->fromName = $this->supportAuthor()->name();
break;
case 'dpt':
$email->fromName = $email->language->get( 'nexus_department_' . $this->department->_id );
break;
default:
$email->fromName = \IPS\Settings::i()->nexus_sout_from;
break;
}
$email->unsubscribe = array( 'text' => 'manage_notification_settings', 'link' => \IPS\Http\Url::internal( 'app=nexus&module=support&controller=requests&do=notifications', 'admin' ) );
$email->send( $member );
}
}
else
{
\IPS\Db::i()->delete( 'nexus_support_notify', array( 'staff_id=?', $staffId ) );
}
}
}
/* !Other */
/**
* Get replies, and the log, for staff view
*
* @param string $orderDirection "asc" or "desc"
* @return \IPS\Patterns\UnionIterator
*/
public function repliesAndLog( $orderDirection='asc' )
{
$orderDirection = ( $orderDirection === 'desc' ) ? $orderDirection : 'asc';
$replies = $this->comments( NULL, NULL, 'date', $orderDirection );
$where = array( array( 'rlog_request=?', $this->id ) );
$first = NULL;
foreach ( $replies as $last )
{
if ( $first === NULL )
{
$first = $last;
}
}
if ( $first )
{
if ( $orderDirection === 'asc' )
{
$where[] = array( 'rlog_date>?', $first->date );
if ( $firstOnNextPage = $this->comments( 1, NULL, 'date', 'asc', NULL, NULL, \IPS\DateTime::ts( $last->date ) ) )
{
$where[] = array( 'rlog_date<?', $firstOnNextPage->date );
}
}
else
{
$where[] = array( 'rlog_date>?', $last->date );
if ( $firstOnNextPage = $this->comments( 1, NULL, 'date', 'asc', NULL, NULL, NULL, array( 'reply_date<?', $last->date ) ) )
{
$where[] = array( 'rlog_date<?', $firstOnNextPage->date );
}
}
}
$iterator = new \IPS\Patterns\UnionIterator( $orderDirection );
$iterator->attachIterator( new \ArrayIterator( $replies ), 'date' );
$iterator->attachIterator( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_support_request_log', $where, 'rlog_date ' . $orderDirection ), 'IPS\nexus\Support\Log' ), 'date' );
return $iterator;
}
/**
* @brief Author
*/
protected $_author;
/**
* Get author
*
* @return \IPS\nexus\Support\Author
*/
public function supportAuthor()
{
if ( $this->_author === NULL )
{
if ( $this->member )
{
try
{
$this->_author = new Author\Member( \IPS\nexus\Customer::load( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
$this->_author = new Author\Member( new \IPS\nexus\Customer );
}
}
else
{
$this->_author = new Author\Email( $this->email );
}
}
return $this->_author;
}
/**
* @brief Staff views
*/
protected $_staffViews;
/**
* Staff views
*
* @return array
*/
public function staffViews()
{
if ( $this->_staffViews === NULL )
{
$this->_staffViews = iterator_to_array( \IPS\Db::i()->select( '*', 'nexus_support_views', array( 'view_rid=?', $this->id ), 'view_last DESC' )->setKeyField( 'view_member' ) );
}
return $this->_staffViews;
}
/**
* Set staff view
*
* @param \IPS\Member $staff The staff member viewing
* @return void
*/
public function setStaffView( \IPS\Member $staff )
{
$this->staffViews();
if ( !isset( $this->_staffViews[ $staff->member_id ] ) )
{
$view = array(
'view_rid' => $this->id,
'view_member' => $staff->member_id,
'view_first' => time(),
'view_last' => time(),
'view_reply' => 0
);
\IPS\Db::i()->insert( 'nexus_support_views', $view, TRUE );
}
else
{
$view = $this->_staffViews[ $staff->member_id ];
$view['view_last'] = time();
\IPS\Db::i()->update( 'nexus_support_views', array( 'view_last' => time() ), array( 'view_rid=? AND view_member=?', $this->id, $staff->member_id ) );
unset( $this->_staffViews[ $staff->member_id ] );
}
array_unshift( $this->_staffViews, $view );
}
/**
* Next/Previous Cache
*/
protected $nextPrevious = array();
/**
* Get Next/Previous Request (for viewing in the ACP)
*
* @param string $sort ORDER BY clause
* @param string $filter The filter to use
* @param bool $previous If true, will return previous (otherwise will return next)
* @return \IPS\nexus\Support\Request|NULL
*/
public function nextPrevious( $sort, $filter=NULL, $previous=FALSE )
{
if ( !isset( $this->nextPrevious[ md5( $sort . $filter . $previous ) ] ) )
{
/* Init */
$select = array( 'nexus_support_requests.*, nexus_support_departments.dpt_staff' );
$joins = array( 'nexus_support_departments' => 'dpt_id=r_department' );
$where = array( array( 'r_id<>?', $this->id ) );
/* Validate the sort */
if ( !preg_match( '/^(([a-z_]+(, )?)+)( ((a|de)sc))?$/i', trim( $sort ), $matches ) )
{
return NULL;
}
$lastColumn = NULL;
$ascDesc= isset( $matches[5] ) ? mb_strtolower( trim( $matches[5] ) ) : 'desc';
foreach ( explode( ',', $matches[1] ) as $column )
{
$column = trim( $column );
if ( !in_array( $column, array( 'r_title', 'sev_position', 'dpt_position', 'r_member', 'r_replies', 'r_started', 'r_last_new_reply', 'r_last_reply', 'r_last_staff_reply', 'status_position' ) ) )
{
return NULL;
}
if ( $column === 'sev_position' )
{
$select[] = 'nexus_support_severities.sev_position';
$joins['nexus_support_severities'] = 'sev_id=r_severity';
}
if ( $column === 'dpt_position' )
{
$select[] = 'nexus_support_departments.dpt_position';
}
if ( $column === 'status_position' )
{
$select[] = 'nexus_support_statuses.status_position';
$joins['nexus_support_statuses'] = 'status_id=r_status';
}
if ( mb_substr( $column, 0, 2 ) === 'r_' )
{
$lastColumn = mb_substr( $column, 2 );
}
}
if ( !$lastColumn )
{
return NULL;
}
/* Build the where */
$where[] = array( "( dpt_staff='*' OR " . \IPS\Db::i()->findInSet( 'dpt_staff', \IPS\nexus\Support\Department::staffDepartmentPerms() ) . ')' );
switch ( $filter )
{
case 'my_departments':
$myFilters = static::myFilters();
$where[] = array( $myFilters['whereClause'] );
break;
case 'tracked':
$where[] = 'request_id IS NOT NULL';
break;
case 'my_last_replies':
$where[] = \IPS\Db::i()->in( 'r_id', iterator_to_array( \IPS\Db::i()->select( 'DISTINCT(reply_request)', 'nexus_support_replies', array( 'reply_member=?', \IPS\Member::loggedIn()->member_id ), 'reply_date DESC', 100 ) ) );
$select = array( 'nexus_support_tracker.request_id' );
$joins['nexus_support_tracker'] = array( 'request_id=r_id AND member_id=?', \IPS\Member::loggedIn()->member_id );
break;
}
/* And stick the next/previous where on */
if ( $previous )
{
$symbol = ( $ascDesc === 'asc' ? '<' : '>' );
}
else
{
$symbol = ( $ascDesc === 'asc' ? '>' : '<' );
}
$where[] = array( "r_{$lastColumn}{$symbol}?", $this->$lastColumn );
/* Build the query */
$select = \IPS\Db::i()->select( implode( ', ', $select ), 'nexus_support_requests', $where, $sort, 1 );
foreach ( $joins as $table => $clause )
{
$select->join( $table, $clause );
}
/* Return */
try
{
$this->nextPrevious[ md5( $sort . $filter . $previous ) ] = static::constructFromData( $select->first() );
}
catch ( \UnderflowException $e )
{
$this->nextPrevious[ md5( $sort . $filter . $previous ) ] = NULL;
}
}
return $this->nextPrevious[ md5( $sort . $filter . $previous ) ];
}
/**
* Log that something happened
*
* @param string $action What changed
* @param mixed $old Old value
* @param mixed $new New value
* @return void
*/
public function log( $action, $old, $new )
{
$log = new \IPS\nexus\Support\Log;
$log->request = $this;
$log->member = \IPS\Member::loggedIn();
$log->action = $action;
$log->old = $old;
$log->new = $new;
$log->date = \IPS\DateTime::create();
$log->save();
}
/* !URLs */
/**
* @brief Cached URLs
*/
protected $_url = array();
/**
* Get URL
*
* @param string|NULL $action Action
* @return \IPS\Http\Url
*/
public function url( $action=NULL )
{
$_key = md5( $action );
if( !isset( $this->_url[ $_key ] ) )
{
$this->_url[ $_key ] = \IPS\Http\Url::internal( "app=nexus&module=support&controller=view&id={$this->id}", 'front', 'support_view' );
if ( $action )
{
$this->_url[ $_key ] = $this->_url[ $_key ]->setQueryString( 'do', $action );
}
}
return $this->_url[ $_key ];
}
/**
* Get ACP URL
*
* @param string|NULL $action Action
* @return \IPS\Http\Url
*/
public function acpUrl( $action=NULL )
{
$url = \IPS\Http\Url::internal( "app=nexus&module=support&controller=request&id={$this->id}", 'admin' );
if( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'admin' and isset( \IPS\Request::i()->sort ) )
{
$url = $url->setQueryString( 'sort', \IPS\Request::i()->sort );
}
return $url;
}
/* !Delete */
/**
* Delete
*
* @return void
*/
public function delete()
{
\IPS\Db::i()->delete( 'nexus_support_tracker', array( 'request_id=?', $this->id ) );
\IPS\Db::i()->delete( 'nexus_support_views', array( 'view_rid=?', $this->id ) );
parent::delete();
}
}