Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+109 -35
View File
@@ -425,14 +425,14 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
}
if ( count( $purchaseWhere ) )
{
$purchaseWhere = array( array( "ps_member={$account->member_id} OR ( " . implode( ' OR ', $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'];
@@ -458,6 +458,27 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
$availableDepartments[ $department->id ] = $department;
}
if ( \IPS\Settings::i()->nexus_subs_enabled )
{
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', 'subscription')), array(\IPS\Db::i()->in( 'ps_item_id', explode( ',', $department->subscriptions ) ) ) ) ) ) )
{
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, 'no_support_departments' );
@@ -510,6 +531,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
if ( $department->packages and !in_array( $department->id, $doNotShowPurchasesForDepartments ) )
{
$allowedPackageIds = explode( ',', $department->packages );
$allowedSubscriptionIds = explode( ',', $department->subscriptions );
$allowedPackages = $department->serializedPackageIds();
$departmentToggles[ $department->id ][] = 'support_purchase_' . $allowedPackages;
if ( !isset( $purchaseFields[ $allowedPackages ] ) )
@@ -521,9 +543,12 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
array(
'class' => 'IPS\nexus\Purchase',
'where' => $purchaseWhere,
'permissionCheck' => function( $node ) use ( $allowedPackageIds )
'permissionCheck' => function( $node ) use ( $allowedPackageIds, $allowedSubscriptionIds )
{
return $node->active and $node->app === 'nexus' and $node->type === 'package' and in_array( $node->item_id, $allowedPackageIds );
return (
( $node->active and $node->app === 'nexus' and ( $node->type === 'package' and in_array( $node->item_id, $allowedPackageIds ) ) )
or ( $node->active and $node->app === 'nexus' and ( $node->type === 'subscription' and in_array( $node->item_id, $allowedSubscriptionIds ) ) )
);
},
'forceOwner' => FALSE,
'zeroVal' => $department->require_package ? NULL : 'support_purchase_none',
@@ -580,7 +605,15 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
}
/* Add department */
$return['department'] = new \IPS\Helpers\Form\Select( 'support_department', isset( \IPS\Request::i()->department ) ? intval( \IPS\Request::i()->department ) : NULL, TRUE, array( 'options' => $departments, 'toggles' => $departmentToggles ) );
if ( !empty( $ppiDepartments ) and count( $departments ) == count( $ppiDepartments ) )
{
$departmentFieldOptions = array( 'options' => array_merge( array( 0 => 'support_department' ), $departments ), 'toggles' => array_merge( array( 0 => array( static::$formLangPrefix . 'content_editor' ) ), $departmentToggles ), 'disabled' => array( 0 ) );
}
else
{
$departmentFieldOptions = array( 'options' => $departments, 'toggles' => $departmentToggles );
}
$return['department'] = new \IPS\Helpers\Form\Select( 'support_department', isset( \IPS\Request::i()->department ) ? intval( \IPS\Request::i()->department ) : NULL, TRUE, $departmentFieldOptions );
foreach ( $availableDepartments as $department )
{
if ( \IPS\Member::loggedIn()->language()->checkKeyExists("nexus_department_{$department->id}_desc") )
@@ -633,7 +666,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
{
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() );
$purchase = \IPS\nexus\Purchase::constructFromData( \IPS\Db::i()->select( '*', '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() );
$this->ppi_invoice = $purchase->original_invoice;
$purchase->delete();
}
@@ -739,7 +772,14 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
{
$k = mb_substr( $k, 13 );
$class = $customFieldObjects[ $k ]->buildHelper();
$cfields[ $k ] = $class::stringValue( $v );
if ( $class instanceof \IPS\Helpers\Form\Upload )
{
$cfields[ $k ] = (string) $v;
}
else
{
$cfields[ $k ] = $class::stringValue( $v );
}
}
}
$this->cfields = $cfields;
@@ -855,7 +895,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
$form->add( new \IPS\Helpers\Form\Editor( 'note', NULL, TRUE, array( 'app' => 'nexus', 'key' => 'Support', 'minimize' => 'keyboard_shortcut_note', 'autoSaveKey' => "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( 0 => 'unassigned' ) + static::staff() ), NULL, NULL, NULL, 'staffNoteAssign' ) );
$form->add( new \IPS\Helpers\Form\Select( 'assign_to', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'parse' => 'normal', 'options' => array( 0 => \IPS\Member::loggedIn()->language()->addToStack('unassigned') ) + static::staff() ), NULL, NULL, NULL, 'staffNoteAssign' ) );
/* Handle submissions */
if ( $values = $form->values() )
@@ -873,11 +913,18 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
\IPS\File::claimAttachments( "req{$this->id}-note", $this->id, $message->id );
/* Update the request */
$goToList = FALSE;
$newDepartment = Department::load( $values['department'] );
if ( $this->department != $newDepartment )
{
$this->log( 'department', $this->department, $newDepartment );
$this->department = $newDepartment;
/* If we're adding a note and sending the request to a department we do not have access to, we should redirect to the support request list with a flash message explaining what happened */
if ( !array_key_exists( $values['department'], iterator_to_array( \IPS\nexus\Support\Department::departmentsWithPermission() ) ) )
{
$goToList = TRUE;
}
}
$newStatus = Status::load( $values['status'] );
if ( $this->status != $newStatus )
@@ -896,7 +943,14 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
$message->sendNotifications();
/* Redirect */
$this->_staffFormRedirect( FALSE, TRUE );
if ( $goToList )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=nexus&module=support&controller=requests"), \IPS\Member::loggedIn()->language()->addToStack( 'note_added_sent_to', NULL, array( 'sprintf' => $newDepartment->_title ) ) );
}
else
{
$this->_staffFormRedirect( FALSE, TRUE );
}
}
return $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'support', 'nexus' ) ), 'noteForm' ), $this );
}
@@ -924,8 +978,8 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
/* 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 ) ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'cc', $defaultRecipients['cc'], FALSE, array( 'autocomplete' => array( 'minimized' => FALSE, 'unique' => TRUE, 'forceLower' => TRUE ) ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'bcc', $defaultRecipients['bcc'], FALSE, array( 'autocomplete' => array( 'minimized' => FALSE, 'unique' => TRUE, 'forceLower' => TRUE ) ) ) );
/* Do we have default content? */
try
@@ -956,7 +1010,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
$form->add( new \IPS\Helpers\Form\Editor( 'message', $defaultContent, TRUE, array( 'app' => 'nexus', 'key' => 'Support', 'minimize' => 'keyboard_shortcut_reply', 'autoSaveKey' => "req{$this->id}-reply", 'defaultIfNoAutoSave' => TRUE, 'minimizeWithContent' => TRUE ) ) );
$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' ) );
$form->add( new \IPS\Helpers\Form\Select( 'assign_to', $this->staff_lock ? ( $this->staff ? $this->staff->member_id : 0 ) : 0, FALSE, array( 'parse' => 'normal', 'options' => array( 0 => \IPS\Member::loggedIn()->language()->addToStack('unassigned') ) + static::staff() ), NULL, NULL, NULL, 'staffReplyAssign' ) );
/* Handle Submissions */
if ( $values = $form->values() )
@@ -1269,11 +1323,12 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
/**
* Get comment page count in ACP (which includes hidden notes)
*
* @param bool $recache TRUE to recache the value
* @return int
*/
public function commentPageCountIncludingNotes()
public function commentPageCountIncludingNotes( $recache=FALSE )
{
if ( $this->commentPageCountIncludingNotes === NULL )
if ( $this->commentPageCountIncludingNotes === NULL OR $recache === TRUE )
{
$this->commentPageCountIncludingNotes = ceil( \IPS\Db::i()->select( 'COUNT(*)', 'nexus_support_replies', array( 'reply_request=?', $this->id ) )->first() / $this->getCommentsPerPage() );
@@ -1285,6 +1340,24 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
return $this->commentPageCountIncludingNotes;
}
/**
* Get comment page count
*
* @param bool $recache TRUE to recache the value
* @return int
*/
public function commentPageCount( $recache=FALSE )
{
if ( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation === 'admin' AND static::canViewHiddenComments() )
{
return $this->commentPageCountIncludingNotes( $recache );
}
else
{
return parent::commentPageCount( $recache );
}
}
/**
* Should new comments be moderated?
*
@@ -1702,36 +1775,37 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse int id ID number
* @apiresponse string title Title
* @apiresponse \IPS\Member member If the support request was created by a member, the member object
* @apiresponse string email If the support request was created by an email which does not belong to a member, that email address
* @apiresponse \IPS\nexus\Support\Status status Status
* @apiresponse \IPS\nexus\Support\Department department Department
* @apiresponse \IPS\nexus\Support\Severity severity Severity
* @apiresponse \IPS\Member staff Assigned staff member
* @apiresponse \IPS\nexus\Purchase purchase Associated purchase
* @apiresponse \IPS\nexus\Invoice ppiInvoice If this is a pay-per-incident support request, the associated invoice
* @apiresponse int replies Number of replies
* @apiresponse \IPS\nexus\Support\Reply firstMessage The first message
* @apiresponse int id ID number
* @apiresponse string title Title
* @apiresponse \IPS\Member member If the support request was created by a member, the member object
* @apiresponse string email If the support request was created by an email which does not belong to a member, that email address
* @apiresponse \IPS\nexus\Support\Status status Status
* @apiresponse \IPS\nexus\Support\Department department Department
* @apiresponse \IPS\nexus\Support\Severity severity Severity
* @clientapiresponse \IPS\Member staff Assigned staff member
* @apiresponse \IPS\nexus\Purchase purchase Associated purchase
* @apiresponse \IPS\nexus\Invoice ppiInvoice If this is a pay-per-incident support request, the associated invoice
* @apiresponse int replies Number of replies
* @apiresponse \IPS\nexus\Support\Reply firstMessage The first message
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$firstMessage = $this->comments( 1, 0, 'date', 'asc' );
return array(
'id' => $this->id,
'title' => $this->title,
'member' => $this->member ? \IPS\Member::load( $this->member )->apiOutput() : null,
'member' => $this->member ? \IPS\Member::load( $this->member )->apiOutput( $authorizedMember ) : null,
'email' => $this->email ?: null,
'status' => $this->status->apiOutput(),
'department' => $this->department->apiOutput(),
'severity' => $this->severity ? $this->severity->apiOutput() : null,
'staff' => $this->staff ? $this->staff->apiOutput() : null,
'purchase' => $this->purchase ? $this->purchase->apiOutput() : null,
'ppiInvoice' => $this->ppi_invoice ? $this->ppi_invoice->apiOutput() : null,
'status' => $this->status->apiOutput( $authorizedMember ),
'department' => $this->department->apiOutput( $authorizedMember ),
'severity' => $this->severity ? $this->severity->apiOutput( $authorizedMember ) : null,
'staff' => $this->staff ? $this->staff->apiOutput( $authorizedMember ) : null,
'purchase' => $this->purchase ? $this->purchase->apiOutput( $authorizedMember ) : null,
'ppiInvoice' => $this->ppi_invoice ? $this->ppi_invoice->apiOutput( $authorizedMember ) : null,
'replies' => $this->replies,
'firstMessage' => $firstMessage->apiOutput()
'firstMessage' => $firstMessage->apiOutput( $authorizedMember )
);
}