1703 lines
50 KiB
PHP
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();
|
|
}
|
|
} |