Version 4.3.0
This commit is contained in:
1 parent
96997ddd8e
commit
fe1acf984a
1349 files changed
+116159
-67711
No files matched your search
@@ -119,7 +119,7 @@ class _Coupon extends \IPS\Node\Model
|
||||
|
||||
$form->addHeader( 'coupon_basic_settings' );
|
||||
$id = $this->id ?: NULL;
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'c_code', $this->id ? $this->code : mb_strtoupper( mb_substr( md5( uniqid() ), 0, 5 ) ), TRUE, array( 'maxLength' => 25 ), function( $val ) use ( $id )
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'c_code', $this->id ? $this->code : mb_strtoupper( mb_substr( md5( mt_rand() ), 0, 5 ) ), TRUE, array( 'maxLength' => 25 ), function( $val ) use ( $id )
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -428,4 +428,23 @@ class _Coupon extends \IPS\Node\Model
|
||||
$item->extra['items'] = $items;
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Duplicate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ( $this->skipCloneDuplication === TRUE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
parent::__clone();
|
||||
|
||||
$this->code = mb_strtoupper( mb_substr( md5( mt_rand() ), 0, 5 ) );
|
||||
$this->used_by = array();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class _CreditCard
|
||||
}
|
||||
$obj->expMonth = str_pad( $expMonth, 2, '0', STR_PAD_LEFT );
|
||||
$obj->expYear = intval( $expYear );
|
||||
if ( mktime( 0, 0, 0, $expMonth, 1, $expYear ) < time() )
|
||||
if ( mktime( 23, 59, 59, $expMonth + 1, 0, $expYear ) < time() )
|
||||
{
|
||||
throw new \DomainException;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,8 @@ class _BillingAgreement extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
$this->member->log( 'billingagreement', array( 'type' => 'cancel', 'id' => $this->id, 'gw_id' => $this->gw_id ) );
|
||||
$this->next_cycle = NULL;
|
||||
$this->canceled = TRUE;
|
||||
$this->canceled = TRUE;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -113,7 +113,7 @@ class _CustomField extends \IPS\CustomField
|
||||
{
|
||||
parent::form( $form );
|
||||
|
||||
if ( ! \IPS\Settings::i()->quick_register )
|
||||
if ( \IPS\Login::registrationType() == 'full' )
|
||||
{
|
||||
/* Quick register is disabled */
|
||||
$form->addHeader( 'customer_field_registration' );
|
||||
@@ -189,9 +189,7 @@ class _CustomField extends \IPS\CustomField
|
||||
protected static function recountCustomerFields()
|
||||
{
|
||||
$count = count( static::roots() );
|
||||
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $count ), array( 'conf_key=?', 'customer_fields' ) );
|
||||
\IPS\Settings::i()->customer_fields = $count;
|
||||
unset( \IPS\Data\Store::i()->settings );
|
||||
\IPS\Settings::i()->changeValues( array( 'customer_fields' => $count ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -499,6 +499,6 @@ class _Customer extends \IPS\Member
|
||||
*/
|
||||
public function acpUrl()
|
||||
{
|
||||
return \IPS\Http\Url::internal( "app=nexus&module=customers&controller=view&id={$this->member_id}", 'admin' );
|
||||
return parent::acpUrl()->setQueryString( 'tab', 'nexus_Main' );
|
||||
}
|
||||
}
|
||||
@@ -178,9 +178,7 @@ class _Goal extends \IPS\Node\Model
|
||||
protected static function recountDonationGoals()
|
||||
{
|
||||
$count = count( static::roots() );
|
||||
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $count ), array( 'conf_key=?', 'donation_goals' ) );
|
||||
\IPS\Settings::i()->donation_goals = $count;
|
||||
unset( \IPS\Data\Store::i()->settings );
|
||||
\IPS\Settings::i()->changeValues( array( 'donation_goals' => $count ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,7 +203,7 @@ class _Goal extends \IPS\Node\Model
|
||||
|
||||
if( $this->_url === null )
|
||||
{
|
||||
$this->_url = \IPS\Http\Url::internal( 'app=nexus&module=clients&controller=donations&id=' . $this->_id, 'front', 'clientsdonate', array( $this->name_seo ), \IPS\Settings::i()->nexus_https );
|
||||
$this->_url = \IPS\Http\Url::internal( 'app=nexus&module=clients&controller=donations&id=' . $this->_id, 'front', 'clientsdonate', array( $this->name_seo ) );
|
||||
}
|
||||
|
||||
return $this->_url;
|
||||
|
||||
@@ -43,6 +43,7 @@ class _CreditCard extends \IPS\Helpers\Form\FormAbstract
|
||||
'attr' => NULL,
|
||||
'jsRequired'=> FALSE,
|
||||
'names' => TRUE,
|
||||
'dummy' => FALSE,
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -63,7 +64,7 @@ class _CreditCard extends \IPS\Helpers\Form\FormAbstract
|
||||
$number = $this->value['number'];
|
||||
$expMonth = $this->value['exp_month'];
|
||||
$expYear = $this->value['exp_year'];
|
||||
$expYear = $this->value['ccv'];
|
||||
$ccv = $this->value['ccv'];
|
||||
}
|
||||
elseif ( $this->value instanceof \IPS\nexus\CreditCard )
|
||||
{
|
||||
@@ -150,10 +151,10 @@ class _CreditCard extends \IPS\Helpers\Form\FormAbstract
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/* Or if we've just not given anything, return NULL */
|
||||
/* Or if we've just not given anything, return no value */
|
||||
if ( !$this->value['number'] )
|
||||
{
|
||||
return NULL;
|
||||
return '';
|
||||
}
|
||||
|
||||
/* But if we do, we can build an object */
|
||||
@@ -163,7 +164,7 @@ class _CreditCard extends \IPS\Helpers\Form\FormAbstract
|
||||
}
|
||||
catch ( \InvalidArgumentException $e )
|
||||
{
|
||||
throw new \DomainException( 'card_number_invalid' );
|
||||
throw new \DomainException( $e->getMessage() );
|
||||
}
|
||||
catch ( \DomainException $e )
|
||||
{
|
||||
|
||||
@@ -89,6 +89,22 @@ class _Rule extends \IPS\Node\Model
|
||||
{
|
||||
$conditions = array();
|
||||
$results = array();
|
||||
$warning = NULL;
|
||||
|
||||
/* Preceeding rules */
|
||||
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_fraud_rules', array( 'f_order<?', $this->order ), 'f_order' ), 'IPS\nexus\Fraud\Rule' ) as $otherRule )
|
||||
{
|
||||
if ( !count( $conditions ) )
|
||||
{
|
||||
$conditions[] = \IPS\Member::loggedIn()->language()->addToStack('f_has_preceeding');
|
||||
}
|
||||
|
||||
if ( $otherRule->isSubsetOf( $this ) )
|
||||
{
|
||||
$warning = $otherRule;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Amount */
|
||||
if ( $this->amount )
|
||||
@@ -279,7 +295,7 @@ class _Rule extends \IPS\Node\Model
|
||||
$results[] = "→ " . \IPS\Member::loggedIn()->language()->addToStack( 'f_action_' . $this->action );
|
||||
|
||||
/* Return */
|
||||
return implode( \IPS\Member::loggedIn()->language()->addToStack( 'f_blurb_join' ), $conditions ) . '<br>' . implode( \IPS\Member::loggedIn()->language()->addToStack( 'f_blurb_join' ), $results );
|
||||
return \IPS\Theme::i()->getTemplate( 'store' )->fraudRuleDesc( $conditions, $results, $warning );//implode( \IPS\Member::loggedIn()->language()->addToStack( 'f_blurb_join' ), $conditions ) . '<br>' . implode( \IPS\Member::loggedIn()->language()->addToStack( 'f_blurb_join' ), $results );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,7 +702,7 @@ class _Rule extends \IPS\Node\Model
|
||||
if ( $this->amount )
|
||||
{
|
||||
$amounts = $this->amount_unit;
|
||||
if ( !$this->_checkCondition( $transaction->amount->amount, $this->amount, $amounts[ $transaction->currency ] ) )
|
||||
if ( !isset( $amounts[ $transaction->currency ] ) or !$this->_checkCondition( $transaction->amount->amount, $this->amount, $amounts[ $transaction->currency ] ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -991,17 +1007,20 @@ class _Rule extends \IPS\Node\Model
|
||||
{
|
||||
case 'Checkbox':
|
||||
case 'YesNo':
|
||||
if ( $memberValue === NULL )
|
||||
if ( $valueToCheck != 0 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if ( $valueToCheck == 1 and !$memberValue )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if ( $valueToCheck == -1 and $memberValue )
|
||||
{
|
||||
return FALSE;
|
||||
if ( $memberValue === NULL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if ( $valueToCheck == 1 and !$memberValue )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if ( $valueToCheck == -1 and $memberValue )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -134,8 +134,6 @@ class _AuthorizeNet extends \IPS\nexus\Gateway
|
||||
*/
|
||||
public function paymentScreen( \IPS\nexus\Invoice $invoice, \IPS\nexus\Money $amount, \IPS\nexus\Customer $member = NULL, $recurrings = array() )
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_gateways.js', 'nexus', 'global' ) );
|
||||
|
||||
$return = array();
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
@@ -308,7 +306,7 @@ class _AuthorizeNet extends \IPS\nexus\Gateway
|
||||
$storedCard->card = $card;
|
||||
$storedCard->save();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch ( \Exception $e ) { /* If there's any issue with saving (which may happen for a duplicate card) we can just carry on since we already auth'd */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
$this->member->cm_profiles = $profiles;
|
||||
$this->member->save();
|
||||
}
|
||||
|
||||
|
||||
/* Add the card */
|
||||
$xml = $this->createAuthenticationXml( 'createCustomerPaymentProfileRequest' );
|
||||
$xml->addChild( 'customerProfileId', $profiles[ $this->method->id ] );
|
||||
@@ -133,6 +133,12 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
throw new \UnexpectedValueException;
|
||||
}
|
||||
|
||||
/* Check it doesn't already exist */
|
||||
if ( count( \IPS\Db::i()->select( 'card_data', 'nexus_customer_cards', array( 'card_member=? AND card_method=? AND card_data=?', $this->member->member_id, $this->method->id, (string) $response->customerPaymentProfileId ) ) ) )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('card_is_duplicate') );
|
||||
}
|
||||
|
||||
/* Save */
|
||||
$this->data = (string) $response->customerPaymentProfileId;
|
||||
$this->save();
|
||||
|
||||
@@ -280,12 +280,18 @@ class _Gateway extends \IPS\Node\Model
|
||||
*/
|
||||
protected static function recountCardStorageGateways()
|
||||
{
|
||||
foreach ( array( 'card_storage_gateways' => count( static::cardStorageGateways() ), 'billing_agreement_gateways' => count( static::billingAgreementGateways() ) ) as $k => $v )
|
||||
$counts = array();
|
||||
foreach ( static::roots() as $gateway )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $v ), array( 'conf_key=?', $k ) );
|
||||
\IPS\Settings::i()->$k = $v;
|
||||
if ( !isset( $counts[ $gateway->gateway ] ) )
|
||||
{
|
||||
$counts[ $gateway->gateway ] = 0;
|
||||
}
|
||||
|
||||
$counts[ $gateway->gateway ]++;
|
||||
}
|
||||
unset( \IPS\Data\Store::i()->settings );
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'card_storage_gateways' => count( static::cardStorageGateways() ), 'billing_agreement_gateways' => count( static::billingAgreementGateways() ), 'gateways_counts' => json_encode( $counts ) ) );
|
||||
}
|
||||
|
||||
/* !Features (Each gateway will override) */
|
||||
@@ -351,6 +357,16 @@ class _Gateway extends \IPS\Node\Model
|
||||
|
||||
/* !Payment Gateway */
|
||||
|
||||
/**
|
||||
* Should the submit button show when this payment method is shown?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function showSubmitButton()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment Screen Fields
|
||||
*
|
||||
@@ -364,6 +380,18 @@ class _Gateway extends \IPS\Node\Model
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual Payment Instructions
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @param string|NULL $email If this is for the email, will be 'html' or 'plaintext'. If for UI, will be NULL.
|
||||
* @return array
|
||||
*/
|
||||
public function manualPaymentInstructions( \IPS\nexus\Transaction $transaction, $email = NULL )
|
||||
{
|
||||
return $transaction->member->language()->addToStack( "nexus_gateway_{$this->id}_ins" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize
|
||||
@@ -417,6 +445,16 @@ class _Gateway extends \IPS\Node\Model
|
||||
throw new \Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund Reasons that the gateway understands, if the gateway supports this
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function refundReasons()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data to show on the ACP transaction page
|
||||
*
|
||||
@@ -428,14 +466,50 @@ class _Gateway extends \IPS\Node\Model
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data to show on the ACP transaction page for a dispute
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @param string $ref Dispute reference ID
|
||||
* @return string
|
||||
*/
|
||||
public function disputeData( \IPS\nexus\Transaction $transaction, $ref )
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run any gateway-specific anti-fraud checks and return status for transaction
|
||||
* This is only called if our local anti-fraud rules have not matched
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return string
|
||||
*/
|
||||
public function fraudCheck( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
return $transaction::STATUS_PAID;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to view transaction in gateway
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return \IPS\Http\Url|NULL
|
||||
*/
|
||||
public function gatewayUrl( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
|
||||
@@ -96,4 +96,16 @@ class _Manual extends \IPS\nexus\Gateway
|
||||
{
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction to be refunded
|
||||
* @param float|NULL $amount Amount to refund (NULL for full amount - always in same currency as transaction)
|
||||
* @return mixed Gateway reference ID for refund, if applicable
|
||||
*/
|
||||
public function refund( \IPS\nexus\Transaction $transaction, $amount = NULL )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -83,40 +83,49 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
|
||||
return new \IPS\DateTime( $data['agreement_details']['next_billing_date'] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get latest unclaimed transaction (only gets transactions within the last 31 days which do not yet have a matching transaction)
|
||||
*
|
||||
* @return \IPS\nexus\Transaction
|
||||
* @throws \IPS\nexus\Gateway\PayPal\Exception
|
||||
* @throws \OutOfRangeException
|
||||
* @throws \IPS\Http\Exception
|
||||
*/
|
||||
public function latestUnclaimedTransaction()
|
||||
{
|
||||
$url = "payments/billing-agreements/{$this->gw_id}/transactions?start_date=" . date( 'Y-m-d', time() - ( 86400 * 31 ) ) . '&end_date=' . date( 'Y-m-d' );
|
||||
$transactions = $this->method->api( $url, NULL, 'get' );
|
||||
foreach ( array_reverse( $transactions['agreement_transaction_list'] ) as $t )
|
||||
$url = "payments/billing-agreements/{$this->gw_id}/transactions?start_date=" . date('Y-m-d', time() - (86400 * 31)) . '&end_date=' . date('Y-m-d');
|
||||
try
|
||||
{
|
||||
if ( $t['status'] == 'Completed' )
|
||||
$transactions = $this->method->api( $url, NULL, 'get' );
|
||||
foreach ( array_reverse( $transactions['agreement_transaction_list'] ) as $t )
|
||||
{
|
||||
try
|
||||
if ( $t['status'] == 'Completed' )
|
||||
{
|
||||
$existingTransaction = \IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['transaction_id'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$transaction = new \IPS\nexus\Transaction;
|
||||
$transaction->member = $this->member;
|
||||
$transaction->method = $this->method;
|
||||
$transaction->amount = new \IPS\nexus\Money( $t['amount']['value'], $t['amount']['currency'] );
|
||||
$transaction->date = new \IPS\DateTime( $t['time_stamp'] );
|
||||
$transaction->extra = array( 'automatic' => TRUE );
|
||||
$transaction->gw_id = $t['transaction_id'];
|
||||
$transaction->billing_agreement = $this;
|
||||
return $transaction;
|
||||
try
|
||||
{
|
||||
$existingTransaction = \IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['transaction_id'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$transaction = new \IPS\nexus\Transaction;
|
||||
$transaction->member = $this->member;
|
||||
$transaction->method = $this->method;
|
||||
$transaction->amount = new \IPS\nexus\Money( $t['amount']['value'], $t['amount']['currency'] );
|
||||
$transaction->date = new \IPS\DateTime( $t['time_stamp'] );
|
||||
$transaction->extra = array( 'automatic' => TRUE );
|
||||
$transaction->gw_id = $t['transaction_id'];
|
||||
$transaction->billing_agreement = $this;
|
||||
return $transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Exception $e )
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new \OutOfRangeException( "{$this->id} ({$this->gw_id})\n{$url}\n\n" . print_r( $transactions, TRUE ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,21 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check it doesn't already exist */
|
||||
$otherCards = \IPS\Db::i()->select( 'card_data', 'nexus_customer_cards', array( 'card_member=? AND card_method=?', $this->member->member_id, $this->method->id ) );
|
||||
if ( count( $otherCards ) )
|
||||
{
|
||||
foreach ( $otherCards as $otherCardId )
|
||||
{
|
||||
$otherCardData = $this->method->api( 'vault/credit-card/' . $otherCardId, NULL, 'get' );
|
||||
if ( $otherCardData['state'] === 'ok' and mb_substr( $otherCardData['number'], -4 ) === mb_substr( $card->number, -4 ) and intval( $otherCardData['expire_month'] ) === intval( $card->expMonth ) and intval( $otherCardData['expire_year'] ) === intval( $card->expYear ) )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('card_is_duplicate') );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save */
|
||||
$response = $this->method->api( 'vault/credit-card', array(
|
||||
'number' => $card->number,
|
||||
'type' => $cardType,
|
||||
@@ -95,7 +110,6 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
'first_name' => $this->member->cm_first_name,
|
||||
'last_name' => $this->member->cm_last_name,
|
||||
) );
|
||||
|
||||
$this->data = $response['id'];
|
||||
$this->save();
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
if ( $settings['type'] === 'card' )
|
||||
{
|
||||
return array( 'card' => new \IPS\nexus\Form\CreditCard( $this->id . '_card', NULL, FALSE, array(
|
||||
return array( 'card' => new \IPS\nexus\Form\CreditCard( $this->id . '_card', NULL, TRUE, array(
|
||||
'types' => array( \IPS\nexus\CreditCard::TYPE_VISA, \IPS\nexus\CreditCard::TYPE_MASTERCARD, \IPS\nexus\CreditCard::TYPE_DISCOVER, \IPS\nexus\CreditCard::TYPE_AMERICAN_EXPRESS ),
|
||||
'save' => $settings['vault'] ? $this : NULL,
|
||||
'member' => $member
|
||||
@@ -417,7 +417,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
$storedCard->card = $card;
|
||||
$storedCard->save();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch ( \Exception $e ) { /* If there's any issue with saving (which may happen for a duplicate card) we can just carry on since we already auth'd */ }
|
||||
}
|
||||
|
||||
/* And return */
|
||||
@@ -1008,7 +1008,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
{
|
||||
$payPalTransactionData['item_list']['items'][] = array(
|
||||
'quantity' => $item->quantity,
|
||||
'name' => $item->name,
|
||||
'name' => mb_strlen( $item->name ) > 127 ? mb_substr( $item->name, 0, 124 ) . '...' : $item->name,
|
||||
'price' => $item->price->amountAsString(),
|
||||
'currency' => $transaction->amount->currency,
|
||||
);
|
||||
|
||||
@@ -36,40 +36,73 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
public function get_card()
|
||||
{
|
||||
if ( !$this->_card )
|
||||
{
|
||||
$profiles = $this->member->cm_profiles;
|
||||
if ( !isset( $profiles[ $this->method->id ] ) )
|
||||
{
|
||||
if ( mb_substr( $this->data, 0, 4 ) === 'src_' )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
$response = $this->method->api( "sources/{$this->data}", NULL, 'get' );
|
||||
|
||||
$this->_card = new \IPS\nexus\CreditCard;
|
||||
$this->_card->lastFour = $response['card']['last4'];
|
||||
switch ( $response['card']['brand'] )
|
||||
{
|
||||
case 'Visa':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_VISA;
|
||||
break;
|
||||
case 'American Express':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_AMERICAN_EXPRESS;
|
||||
break;
|
||||
case 'MasterCard':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_MASTERCARD;
|
||||
break;
|
||||
case 'Discover':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DISCOVER;
|
||||
break;
|
||||
case 'JCB':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_JCB;
|
||||
break;
|
||||
case 'Diners Club':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DINERS_CLUB;
|
||||
break;
|
||||
}
|
||||
$this->_card->expMonth = $response['card']['exp_month'];
|
||||
$this->_card->expYear = $response['card']['exp_year'];
|
||||
}
|
||||
|
||||
$response = $this->method->api( "customers/{$profiles[ $this->method->id ]}/cards/{$this->data}", NULL, 'get' );
|
||||
|
||||
$this->_card = new \IPS\nexus\CreditCard;
|
||||
$this->_card->lastFour = $response['last4'];
|
||||
switch ( $response['type'] )
|
||||
{
|
||||
case 'Visa':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_VISA;
|
||||
break;
|
||||
case 'American Express':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_AMERICAN_EXPRESS;
|
||||
break;
|
||||
case 'MasterCard':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_MASTERCARD;
|
||||
break;
|
||||
case 'Discover':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DISCOVER;
|
||||
break;
|
||||
case 'JCB':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_JCB;
|
||||
break;
|
||||
case 'Diners Club':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DINERS_CLUB;
|
||||
break;
|
||||
else
|
||||
{
|
||||
$profiles = $this->member->cm_profiles;
|
||||
if ( !isset( $profiles[ $this->method->id ] ) )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
}
|
||||
|
||||
$response = $this->method->api( "customers/{$profiles[ $this->method->id ]}/cards/{$this->data}", NULL, 'get' );
|
||||
|
||||
$this->_card = new \IPS\nexus\CreditCard;
|
||||
$this->_card->lastFour = $response['last4'];
|
||||
switch ( $response['type'] )
|
||||
{
|
||||
case 'Visa':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_VISA;
|
||||
break;
|
||||
case 'American Express':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_AMERICAN_EXPRESS;
|
||||
break;
|
||||
case 'MasterCard':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_MASTERCARD;
|
||||
break;
|
||||
case 'Discover':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DISCOVER;
|
||||
break;
|
||||
case 'JCB':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_JCB;
|
||||
break;
|
||||
case 'Diners Club':
|
||||
$this->_card->type = \IPS\nexus\CreditCard::TYPE_DINERS_CLUB;
|
||||
break;
|
||||
}
|
||||
$this->_card->expMonth = $response['exp_month'];
|
||||
$this->_card->expYear = $response['exp_year'];
|
||||
}
|
||||
$this->_card->expMonth = $response['exp_month'];
|
||||
$this->_card->expYear = $response['exp_year'];
|
||||
}
|
||||
return $this->_card;
|
||||
}
|
||||
@@ -82,8 +115,8 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
*/
|
||||
public function set_card( \IPS\nexus\CreditCard $card )
|
||||
{
|
||||
/* Create a customer object if we don't have one */
|
||||
$profiles = $this->member->cm_profiles;
|
||||
|
||||
if ( !isset( $profiles[ $this->method->id ] ) )
|
||||
{
|
||||
$response = $this->method->api( 'customers', array(
|
||||
@@ -98,11 +131,37 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
$this->member->save();
|
||||
}
|
||||
|
||||
$response = $this->method->api( "customers/{$profiles[ $this->method->id ]}/cards", array(
|
||||
'card' => isset( $card->token ) ? $card->token : NULL
|
||||
/* If the source is a 3D Secure Authorization, get the actual card */
|
||||
$sourceId = $card->token;
|
||||
$response = $this->method->api( "sources/{$card->token}", NULL, 'get' );
|
||||
if ( $response['type'] === 'three_d_secure' )
|
||||
{
|
||||
$sourceId = $response['three_d_secure']['card'];
|
||||
}
|
||||
|
||||
/* Check it doesn't already exist */
|
||||
$otherCards = \IPS\Db::i()->select( 'card_data', 'nexus_customer_cards', array( 'card_member=? AND card_method=?', $this->member->member_id, $this->method->id ) );
|
||||
if ( count( $otherCards ) )
|
||||
{
|
||||
$sourceData = $this->method->api( "sources/{$sourceId}", NULL, 'get' );
|
||||
if ( isset( $sourceData['card']['fingerprint'] ) )
|
||||
{
|
||||
foreach ( $otherCards as $otherCardId )
|
||||
{
|
||||
$otherCardData = $this->method->api( "sources/{$otherCardId}", NULL, 'get' );
|
||||
if ( isset( $otherCardData['card']['fingerprint'] ) and $otherCardData['card']['fingerprint'] === $sourceData['card']['fingerprint'] )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('card_is_duplicate') );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the card */
|
||||
$response = $this->method->api( "customers/{$profiles[ $this->method->id ]}/sources", array(
|
||||
'source' => $sourceId
|
||||
) );
|
||||
$this->data = $response['id'];
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,6 @@ class _Exception extends \DomainException
|
||||
{
|
||||
parent::__construct( $response['message'] );
|
||||
}
|
||||
elseif ( $response['type'] == 'invalid_request_error' )
|
||||
{
|
||||
parent::__construct( \IPS\Member::loggedIn()->language()->get( 'card_number_invalid' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
parent::__construct( \IPS\Member::loggedIn()->language()->get( 'gateway_err' ) );
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -50,7 +50,7 @@ class _Account extends \IPS\Patterns\ActiveRecord
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
/* Initiate an object */
|
||||
$classname = 'IPS\\nexus\Hosting\\' . ucfirst( \IPS\nexus\Hosting\Server::load( $data['account_server'] )->type ) . '\\Account';
|
||||
$classname = 'IPS\\nexus\Hosting\\' . mb_ucfirst( \IPS\nexus\Hosting\Server::load( $data['account_server'] )->type ) . '\\Account';
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class _Server extends \IPS\Node\Model
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
/* Initiate an object */
|
||||
$classname = 'IPS\\nexus\Hosting\\' . ucfirst( $data['server_type'] ) . '\\Server';
|
||||
$classname = 'IPS\\nexus\Hosting\\' . mb_ucfirst( $data['server_type'] ) . '\\Server';
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
@@ -157,7 +157,7 @@ class _Server extends \IPS\Node\Model
|
||||
{
|
||||
$types[ mb_strtolower( $file ) ] = 'nexus_server_' . $file;
|
||||
|
||||
$class = 'IPS\nexus\Hosting\\' . ucfirst( $file ) . '\Server';
|
||||
$class = 'IPS\nexus\Hosting\\' . mb_ucfirst( $file ) . '\Server';
|
||||
$extraFields[ mb_strtolower( mb_substr( $file, 0, -4 ) ) ] = $class::extraFormFields( $this );
|
||||
$toggles[ mb_strtolower( $file ) ] = array_keys( $extraFields[ mb_strtolower( mb_substr( $file, 0, -4 ) ) ] );
|
||||
}
|
||||
@@ -221,13 +221,13 @@ class _Server extends \IPS\Node\Model
|
||||
unset( $values['server_nameservers_default'] );
|
||||
unset( $values['server_monitor_on'] );
|
||||
|
||||
$values = call_user_func( array( 'IPS\nexus\Hosting\\' . ucfirst( $values['server_type'] ) . '\Server', '_formatFormValues' ), $values );
|
||||
$values = call_user_func( array( 'IPS\nexus\Hosting\\' . mb_ucfirst( $values['server_type'] ) . '\Server', '_formatFormValues' ), $values );
|
||||
|
||||
foreach ( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/nexus/sources/Hosting' ) as $file )
|
||||
{
|
||||
if ( !$file->isDot() and mb_substr( $file, 0, 1 ) !== '.' and $file->isDir() )
|
||||
{
|
||||
$class = 'IPS\nexus\Hosting\\' . ucfirst( $file ) . '\Server';
|
||||
$class = 'IPS\nexus\Hosting\\' . mb_ucfirst( $file ) . '\Server';
|
||||
foreach ( array_keys( $class::extraFormFields( $this ) ) as $k )
|
||||
{
|
||||
unset( $values[ $k ] );
|
||||
|
||||
@@ -98,22 +98,22 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$table->parsers = array(
|
||||
'i_status' => function( $val )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('invoices')->status( $val );
|
||||
return \IPS\Theme::i()->getTemplate( 'invoices', 'nexus' )->status( $val );
|
||||
},
|
||||
'i_member' => function ( $val, $row )
|
||||
{
|
||||
if ( $val )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( $val ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'nexus' )->userLink( \IPS\Member::load( $val ) );
|
||||
}
|
||||
elseif ( $row['i_guest_data'] )
|
||||
{
|
||||
$data = json_decode( $row['i_guest_data'], TRUE );
|
||||
return \IPS\nexus\Customer::constructFromData( $data['member'] )->cm_name;
|
||||
return htmlspecialchars( \IPS\nexus\Customer::constructFromData( $data['member'] )->cm_name, ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( 0 ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'nexus' )->userLink( \IPS\Member::load( 0 ) );
|
||||
}
|
||||
},
|
||||
'i_total' => function( $val, $row )
|
||||
@@ -1408,6 +1408,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* 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
|
||||
@@ -1433,14 +1434,14 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
* @apiresponse string viewUrl The URL to where the customer can view this invoice
|
||||
* @apiresponse string checkoutUrl The URL to where the customer can pay this invoice
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
$summary = $this->summary();
|
||||
|
||||
$tax = array();
|
||||
foreach ( $summary['tax'] as $classId => $data )
|
||||
{
|
||||
$tax[] = ( new \IPS\nexus\Invoice\Item\TaxItem( $classId, $data ) )->apiOutput();
|
||||
$tax[] = ( new \IPS\nexus\Invoice\Item\TaxItem( $classId, $data ) )->apiOutput( $authorizedMember );
|
||||
}
|
||||
|
||||
return array(
|
||||
@@ -1449,31 +1450,31 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
'status' => $this->status,
|
||||
'issueDate' => $this->date->rfc3339(),
|
||||
'paidDate' => $this->paid ? $this->paid->rfc3339() : null,
|
||||
'customer' => $this->member->apiOutput(),
|
||||
'items' => array_map( function( $item ){
|
||||
return $item->apiOutput();
|
||||
'customer' => $this->member->apiOutput( $authorizedMember ),
|
||||
'items' => array_map( function( $item ) use ( $authorizedMember ) {
|
||||
return $item->apiOutput( $authorizedMember );
|
||||
}, $summary['items'] ),
|
||||
'subTotal' => $summary['subtotal']->apiOutput(),
|
||||
'shippingCharges' => array_map( function( $item ){
|
||||
return $item->apiOutput();
|
||||
'subTotal' => $summary['subtotal']->apiOutput( $authorizedMember ),
|
||||
'shippingCharges' => array_map( function( $item ) use ( $authorizedMember ) {
|
||||
return $item->apiOutput( $authorizedMember );
|
||||
}, $summary['shipping'] ),
|
||||
'shippingTotal' => $summary['shippingTotal']->apiOutput(),
|
||||
'shippingTotal' => $summary['shippingTotal']->apiOutput( $authorizedMember ),
|
||||
'tax' => $tax,
|
||||
'taxTotal' => $summary['taxTotal']->apiOutput(),
|
||||
'total' => $summary['total']->apiOutput(),
|
||||
'outstanding' => $this->amountToPay()->apiOutput(),
|
||||
'billingAddress' => $this->billaddress ? $this->billaddress->apiOutput() : null,
|
||||
'shippingAddress' => $this->shipaddress ? $this->shipaddress->apiOutput() : null,
|
||||
'taxTotal' => $summary['taxTotal']->apiOutput( $authorizedMember ),
|
||||
'total' => $summary['total']->apiOutput( $authorizedMember ),
|
||||
'outstanding' => $this->amountToPay()->apiOutput( $authorizedMember ),
|
||||
'billingAddress' => $this->billaddress ? $this->billaddress->apiOutput( $authorizedMember ) : null,
|
||||
'shippingAddress' => $this->shipaddress ? $this->shipaddress->apiOutput( $authorizedMember ) : null,
|
||||
'poNumber' => $this->po,
|
||||
'notes' => $this->notes,
|
||||
'transactions' => array_map( function( $transaction ){
|
||||
return $transaction->apiOutput();
|
||||
'transactions' => array_map( function( $transaction ) use ( $authorizedMember ) {
|
||||
return $transaction->apiOutput( $authorizedMember );
|
||||
}, iterator_to_array( $this->transactions() ) ),
|
||||
'shipments' => array_map( function( $shipment ){
|
||||
return $shipment->apiOutput();
|
||||
'shipments' => array_map( function( $shipment ) use ( $authorizedMember ) {
|
||||
return $shipment->apiOutput( $authorizedMember );
|
||||
}, iterator_to_array( $this->shipments() ) ),
|
||||
'purchases' => array_map( function( $purchase ){
|
||||
return $purchase->apiOutput();
|
||||
'purchases' => array_map( function( $purchase ) use ( $authorizedMember ) {
|
||||
return $purchase->apiOutput( $authorizedMember );
|
||||
}, iterator_to_array( $this->purchasesCreated() ) ),
|
||||
'viewUrl' => (string) $this->url(),
|
||||
'checkoutUrl' => (string) $this->checkoutUrl(),
|
||||
@@ -1539,6 +1540,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$profileFields = $this->guest_data['profileFields'];
|
||||
$securityAnswers = isset( $this->guest_data['securityAnswers'] ) ? $this->guest_data['securityAnswers'] : array();
|
||||
$spamData = $this->guest_data['spamData'];
|
||||
|
||||
/* If the member has already been created (the user made two separate purchases with the same data), use that, otherwise create one */
|
||||
try
|
||||
@@ -1554,6 +1556,8 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$memberToSave->changed[ $k ] = $v;
|
||||
}
|
||||
$memberToSave->save();
|
||||
|
||||
$memberToSave->logHistory( 'core', 'account', array( 'type' => 'register_checkout', 'spamCode' => $spamData['code'], 'spamAction' => $spamData['action'], 'invoice' => $this->id ), FALSE );
|
||||
}
|
||||
$this->member = $memberToSave;
|
||||
$this->guest_data = NULL;
|
||||
@@ -1719,12 +1723,14 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$purchase->app = $item::$application;
|
||||
$purchase->type = $item::$type;
|
||||
$purchase->item_id = intval( $item->id );
|
||||
$purchase->custom_fields = $item->details;
|
||||
$purchase->custom_fields = $item->purchaseDetails;
|
||||
$purchase->original_invoice = $this;
|
||||
$purchase->tax = $item->tax ? $item->tax->id : 0;
|
||||
if ( $item->payTo )
|
||||
{
|
||||
$purchase->pay_to = $item->payTo;
|
||||
$purchase->commission = $item->commission;
|
||||
$purchase->fee = $item->fee ?: NULL;
|
||||
}
|
||||
if ( $item->parent !== NULL )
|
||||
{
|
||||
@@ -2095,7 +2101,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$return['purchases'] = array();
|
||||
foreach ( $purchases as $purchase )
|
||||
{
|
||||
$return['purchases'][] = \IPS\Theme::i()->getTemplate('purchases')->link( $purchase );
|
||||
$return['purchases'][] = \IPS\Theme::i()->getTemplate( 'purchases', 'nexus' )->link( $purchase );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2107,12 +2113,12 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if ( $shipment->status === \IPS\nexus\Shipping\Order::STATUS_SHIPPED )
|
||||
{
|
||||
$return['shipments'][] = \IPS\Theme::i()->getTemplate('shiporders')->link( $shipment );
|
||||
$return['shipments'][] = \IPS\Theme::i()->getTemplate( 'shiporders', 'nexus' )->link( $shipment );
|
||||
}
|
||||
else
|
||||
{
|
||||
$return['shipments'][] = array(
|
||||
'message' => \IPS\Theme::i()->getTemplate('shiporders')->link( $shipment ),
|
||||
'message' => \IPS\Theme::i()->getTemplate( 'shiporders', 'nexus' )->link( $shipment ),
|
||||
'warning' => \IPS\Member::loggedIn()->language()->addToStack('invoice_unpaid_shipments_shipped'),
|
||||
);
|
||||
}
|
||||
@@ -2184,7 +2190,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return \IPS\Http\Url::internal( 'app=nexus&module=checkout&controller=checkout&id=' . $this->id, 'front', 'nexus_checkout', array(), \IPS\Settings::i()->nexus_https );
|
||||
return \IPS\Http\Url::internal( 'app=nexus&module=checkout&controller=checkout&id=' . $this->id, 'front', 'nexus_checkout' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,10 +64,15 @@ abstract class _Item
|
||||
public $paymentMethodIds;
|
||||
|
||||
/**
|
||||
* @brief Key/Value array of extra details
|
||||
* @brief Key/Value array of extra details to display on the invoice
|
||||
*/
|
||||
public $details = array();
|
||||
|
||||
/**
|
||||
* @brief Key/Value array of extra details to store on the purchase
|
||||
*/
|
||||
public $purchaseDetails = array();
|
||||
|
||||
/**
|
||||
* @brief Physical?
|
||||
*/
|
||||
@@ -359,6 +364,7 @@ abstract class _Item
|
||||
/**
|
||||
* 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 string name Item name
|
||||
* @apiresponse string itemApp Key for application. For example, 'nexus' for products and renewals; 'downloads' for Downloads files
|
||||
@@ -385,7 +391,7 @@ abstract class _Item
|
||||
* @apiresonse float commission If the payment for this item goes to another user (for example for Downloads files), the percentage of the price that will be retained by the site (in addition to fee)
|
||||
* @apiresonse \IPS\nexus\Money fee If the payment for this item goes to another user (for example for Downloads files), the fee that will be deducted by the site (in addition to commission)
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'name' => $this->name,
|
||||
@@ -395,23 +401,23 @@ abstract class _Item
|
||||
'itemUrl' => $this->url() ? ( (string) $this->url() ) : null,
|
||||
'itemImage' => $this->image() ? ( (string) $this->image()->url ) : null,
|
||||
'quantity' => $this->quantity,
|
||||
'itemPrice' => $this->price->apiOutput(),
|
||||
'linePrice' => $this->linePrice()->apiOutput(),
|
||||
'taxClass' => $this->tax ? $this->tax->apiOutput() : null,
|
||||
'itemPrice' => $this->price->apiOutput( $authorizedMember ),
|
||||
'linePrice' => $this->linePrice()->apiOutput( $authorizedMember ),
|
||||
'taxClass' => $this->tax ? $this->tax->apiOutput( $authorizedMember ) : null,
|
||||
'physical' => $this->physical,
|
||||
'weight' => isset( $this->weight ) ? $this->weight->kilograms : null,
|
||||
'length' => isset( $this->length ) ? $this->length->metres : null,
|
||||
'width' => isset( $this->width ) ? $this->width->metres : null,
|
||||
'height' => isset( $this->height ) ? $this->height->metres : null,
|
||||
'renewalTerm' => isset( $this->renewalTerm ) ? $this->renewalTerm->apiOutput() : null,
|
||||
'renewalTerm' => isset( $this->renewalTerm ) ? $this->renewalTerm->apiOutput( $authorizedMember ) : null,
|
||||
'expireDate' => isset( $this->expireDate ) ? $this->expireDate->rfc3339() : null,
|
||||
'details' => $this->details,
|
||||
'parentPurchase' => ( isset( $this->parent ) and $this->parent instanceof \IPS\nexus\Purchase ) ? $this->parent->apiOutput() : null,
|
||||
'parentPurchase' => ( isset( $this->parent ) and $this->parent instanceof \IPS\nexus\Purchase ) ? $this->parent->apiOutput( $authorizedMember ) : null,
|
||||
'parentItem' => ( isset( $this->parent ) and is_int( $this->parent ) ) ? $this->parent : null,
|
||||
'groupWithParent' => isset( $this->groupWithParent ) ? $this->groupWithParent : false,
|
||||
'payTo' => $this->payTo ? $this->payTo->apiOutput() : null,
|
||||
'payTo' => $this->payTo ? $this->payTo->apiOutput( $authorizedMember ) : null,
|
||||
'commission' => $this->commission,
|
||||
'fee' => $this->fee ? $this->fee->apiOutput() : null,
|
||||
'fee' => $this->fee ? $this->fee->apiOutput( $authorizedMember ) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
|
||||
{
|
||||
$where[] = array( 'ps_cancelled=0' );
|
||||
}
|
||||
|
||||
|
||||
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_purchases', $where ), 'IPS\nexus\Purchase' );
|
||||
}
|
||||
|
||||
@@ -260,6 +260,17 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase can be renewed?
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @return boolean
|
||||
*/
|
||||
public static function canBeRenewed( \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can Renew Until
|
||||
@@ -341,6 +352,18 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On expire soon
|
||||
* If returns TRUE, the normal expire warning email will not be sent
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @return void
|
||||
*/
|
||||
public static function onExpireWarning( \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* On Purchase Expired
|
||||
*
|
||||
|
||||
@@ -68,6 +68,7 @@ class _Renewal extends \IPS\nexus\Invoice\Item
|
||||
{
|
||||
$obj->payTo = $purchase->pay_to;
|
||||
$obj->commission = $purchase->commission;
|
||||
$obj->fee = $purchase->fee;
|
||||
}
|
||||
|
||||
if ( $renewalPaymentMethodIds = $purchase->renewalPaymentMethodIds() )
|
||||
|
||||
@@ -49,15 +49,16 @@ class _TaxItem
|
||||
/**
|
||||
* 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 \IPS\nexus\Tax class The tax class
|
||||
* @apiresponse float rate The rate (for example 0.2 means 20%)
|
||||
* @apiresponse \IPS\nexus\Money amount Amount to pay
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'class' => \IPS\nexus\Tax::load( $this->class )->apiOutput(),
|
||||
'class' => \IPS\nexus\Tax::load( $this->class )->apiOutput( $authorizedMember ),
|
||||
'rate' => $this->data['rate'],
|
||||
'amount' => $this->data['amount']
|
||||
);
|
||||
|
||||
@@ -82,10 +82,12 @@ class _ItemsIterator extends \ArrayIterator
|
||||
|
||||
/* Details */
|
||||
$details = array();
|
||||
$purchaseDetails = array();
|
||||
if ( isset( $data['cfields'] ) and $data['cfields'] )
|
||||
{
|
||||
foreach( $data['cfields'] AS $key => $value )
|
||||
{
|
||||
$purchaseDetails[ $key ] = $value;
|
||||
try
|
||||
{
|
||||
$field = \IPS\nexus\Package\CustomField::load( $key );
|
||||
@@ -133,6 +135,7 @@ class _ItemsIterator extends \ArrayIterator
|
||||
}
|
||||
|
||||
$obj->details = $details;
|
||||
$obj->purchaseDetails = $purchaseDetails;
|
||||
|
||||
/* Tax */
|
||||
if ( isset( $data['tax'] ) AND $data['tax'] )
|
||||
|
||||
@@ -83,6 +83,8 @@ class _Money
|
||||
case 'VND':
|
||||
case 'VUV':
|
||||
case 'XAF':
|
||||
case 'XOF':
|
||||
case 'XPF':
|
||||
return 0;
|
||||
|
||||
default:
|
||||
@@ -126,7 +128,7 @@ class _Money
|
||||
$amount = new \IPS\Math\Number( number_format( $amount, $this->numberOfDecimalPlaces, '.', '' ) );
|
||||
}
|
||||
|
||||
$this->amount = $amount->round( $this->numberOfDecimalPlaces, \IPS\Math\Number::ROUND_UP );
|
||||
$this->amount = $amount->round( $this->numberOfDecimalPlaces );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,11 +249,12 @@ class _Money
|
||||
/**
|
||||
* 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 string currency The currency code (e.g. 'USD')
|
||||
* @apiresponse string amount The amount
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'currency' => $this->currency,
|
||||
|
||||
@@ -339,6 +339,7 @@ class _Ad extends \IPS\nexus\Package
|
||||
*/
|
||||
public function generateInvoiceForm( \IPS\Helpers\Form $form, $k )
|
||||
{
|
||||
$form->attributes['data-bypassValidation'] = true;
|
||||
$field = new \IPS\Helpers\Form\Url( 'advertisement_url' . $k, NULL, TRUE );
|
||||
$field->label = \IPS\Member::loggedIn()->language()->addToStack('advertisement_url');
|
||||
$form->add( $field );
|
||||
|
||||
@@ -51,7 +51,7 @@ class _Group extends \IPS\Node\Model
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'menu__nexus_store_packages';
|
||||
public static $nodeTitle = 'product_groups';
|
||||
|
||||
/**
|
||||
* @brief [Node] Subnode class
|
||||
@@ -207,6 +207,19 @@ class _Group extends \IPS\Node\Model
|
||||
return $this->_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL from index data
|
||||
*
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public static function urlFromIndexData( $indexData, $itemData, $containerData )
|
||||
{
|
||||
return \IPS\Http\Url::internal( "app=nexus&module=store&controller=store&cat={$indexData['index_container_id']}", 'front', 'store_group', \IPS\Member::loggedIn()->language()->addToStack( 'nexus_pgroup_' . $indexData['index_container_id'], FALSE, array( 'seotitle' => TRUE ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full image URL
|
||||
*
|
||||
@@ -214,7 +227,7 @@ class _Group extends \IPS\Node\Model
|
||||
*/
|
||||
public function get_image()
|
||||
{
|
||||
return ( isset( $this->_data['image'] ) ) ? (string) \IPS\File::get( 'nexus_Products', $this->_data['image'] )->url : NULL;
|
||||
return ( isset( $this->_data['image'] ) ) ? (string) \IPS\File::get( 'nexus_PackageGroups', $this->_data['image'] )->url : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1132,7 +1132,7 @@ class _Hosting extends \IPS\nexus\Package
|
||||
while ( count( $select ) or !$server->checkUsername( $username ) );
|
||||
|
||||
/* Create the account */
|
||||
$class = 'IPS\nexus\Hosting\\' . ucfirst( $server->type ) . '\Account';
|
||||
$class = 'IPS\nexus\Hosting\\' . mb_ucfirst( $server->type ) . '\Account';
|
||||
$account = new $class;
|
||||
$account->ps_id = $purchase->id;
|
||||
$account->server = $server;
|
||||
|
||||
@@ -19,9 +19,9 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
}
|
||||
|
||||
/**
|
||||
* File Model
|
||||
* Package Item Model
|
||||
*/
|
||||
class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\Content\Shareable, \IPS\Content\Embeddable, \IPS\Content\MetaData
|
||||
class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\Content\Shareable, \IPS\Content\Embeddable, \IPS\Content\MetaData, \IPS\Content\Searchable
|
||||
{
|
||||
/**
|
||||
* @brief Application
|
||||
@@ -69,7 +69,9 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
'unapproved_reviews' => 'unapproved_reviews',
|
||||
'hidden_reviews' => 'hidden_reviews',
|
||||
'rating' => 'rating',
|
||||
'meta_data' => 'meta_data'
|
||||
'meta_data' => 'meta_data',
|
||||
'date' => 'date_added',
|
||||
'updated' => 'date_updated'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -82,6 +84,16 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
*/
|
||||
public static $icon = 'archive';
|
||||
|
||||
/**
|
||||
* @brief Include In Sitemap
|
||||
*/
|
||||
public static $includeInSitemap = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Can this content be moderated normally from the front-end (will be FALSE for things like Pages and Commerce Products)
|
||||
*/
|
||||
public static $canBeModeratedFromFrontend = FALSE;
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
@@ -288,6 +300,10 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
elseif ( $key === 'date' )
|
||||
{
|
||||
return $this->date_added;
|
||||
}
|
||||
return parent::mapped($key);
|
||||
}
|
||||
|
||||
@@ -298,6 +314,204 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
*/
|
||||
public static function supportedMetaDataTypes()
|
||||
{
|
||||
return array( 'core_FeaturedComments' );
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/* !Search */
|
||||
|
||||
/**
|
||||
* Title for search index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function searchIndexTitle()
|
||||
{
|
||||
$titles = array();
|
||||
foreach ( \IPS\Lang::languages() as $lang )
|
||||
{
|
||||
$titles[] = $lang->get("nexus_package_{$this->id}");
|
||||
}
|
||||
return implode( ' ', $titles );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content for search index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function searchIndexContent()
|
||||
{
|
||||
$descriptions = array();
|
||||
foreach ( \IPS\Lang::languages() as $lang )
|
||||
{
|
||||
try
|
||||
{
|
||||
$descriptions[] = $lang->get("nexus_package_{$this->id}_desc");
|
||||
}
|
||||
catch ( \UnderflowException $e ) { }
|
||||
}
|
||||
return implode( ' ', $descriptions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Index Permissions
|
||||
*
|
||||
* @return string Comma-delimited values or '*'
|
||||
* @li Number indicates a group
|
||||
* @li Number prepended by "m" indicates a member
|
||||
* @li Number prepended by "s" indicates a social group
|
||||
*/
|
||||
public function searchIndexPermissions()
|
||||
{
|
||||
return $this->store ? $this->member_groups : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Columns needed to query for search result / stream view
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function basicDataColumns()
|
||||
{
|
||||
return array( 'p_id', 'p_base_price', 'p_reviews', 'p_discounts', 'p_renew_options', 'p_tax', 'p_stock' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL from index data
|
||||
*
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public static function urlFromIndexData( $indexData, $itemData )
|
||||
{
|
||||
return \IPS\Http\Url::internal( "app=nexus&module=store&controller=product&id={$indexData['index_item_id']}", 'front', 'store_product', \IPS\Member::loggedIn()->language()->addToStack( 'nexus_package_' . $indexData['index_item_id'], FALSE, array( 'seotitle' => TRUE ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML for search result display
|
||||
*
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param array $reputationData Array of people who have given reputation and the reputation they gave
|
||||
* @param int|NULL $reviewRating If this is a review, the rating
|
||||
* @param bool $iPostedIn If the user has posted in the item
|
||||
* @param string $view 'expanded' or 'condensed'
|
||||
* @param bool $asItem Displaying results as items?
|
||||
* @param bool $canIgnoreComments Can ignore comments in the result stream? Activity stream can, but search results cannot.
|
||||
* @param array $template Optional custom template
|
||||
* @param array $reactions Reaction Data
|
||||
* @return string
|
||||
*/
|
||||
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL, $reactions=array() )
|
||||
{
|
||||
$indexData['index_title'] = \IPS\Member::loggedIn()->language()->addToStack( 'nexus_package_' . $indexData['index_item_id'] );
|
||||
return parent::searchResult( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments, $template, $reactions );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query to get additional data for search result / stream view
|
||||
*
|
||||
* @param array $items Item data (will be an array containing values from basicDataColumns())
|
||||
* @return array
|
||||
*/
|
||||
public static function searchResultExtraData( $items )
|
||||
{
|
||||
$images = iterator_to_array( \IPS\Db::i()->select( array( 'image_product', 'image_location' ), 'nexus_package_images', array( array( \IPS\Db::i()->in( 'image_product', array_keys( $items ) ) ), array( 'image_primary=1' ) ) )->setKeyField( 'image_product' )->setValueField( 'image_location' ) );
|
||||
|
||||
$taxIds = array();
|
||||
foreach ( $items as $k => $data )
|
||||
{
|
||||
if ( $data['p_tax'] )
|
||||
{
|
||||
$taxIds[ $data['p_tax'] ] = $data['p_tax'];
|
||||
}
|
||||
}
|
||||
|
||||
$taxData = array();
|
||||
if ( $taxIds )
|
||||
{
|
||||
$taxData = iterator_to_array( \IPS\Db::i()->select( '*', 'nexus_tax', \IPS\Db::i()->in( 't_id', $taxIds ) )->setKeyField('t_id') );
|
||||
}
|
||||
|
||||
$return = array();
|
||||
foreach ( $items as $k => $data )
|
||||
{
|
||||
$return[ $k ]['image'] = isset( $images[ $k ] ) ? $images[ $k ] : NULL;
|
||||
$return[ $k ]['tax'] = ( $data['p_tax'] and isset( $taxData[ $data['p_tax'] ] ) ) ? $taxData[ $data['p_tax'] ] : NULL;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get snippet HTML for search result display
|
||||
*
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param array $reputationData Array of people who have given reputation and the reputation they gave
|
||||
* @param int|NULL $reviewRating If this is a review, the rating
|
||||
* @param string $view 'expanded' or 'condensed'
|
||||
* @return callable
|
||||
*/
|
||||
public static function searchResultSnippet( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $view )
|
||||
{
|
||||
$url = static::urlFromIndexData( $indexData, $itemData );
|
||||
|
||||
/* Work out the price to display @todo - make this more efficient */
|
||||
try
|
||||
{
|
||||
$customer = \IPS\nexus\Customer::loggedIn();
|
||||
$currency = ( isset( $_SESSION['currency'] ) and in_array( $_SESSION['currency'], \IPS\nexus\Money::currencies() ) ) ? $_SESSION['currency'] : $customer->defaultCurrency();
|
||||
|
||||
$priceMayChange = FALSE;
|
||||
$priceNotIncludingDiscounts = NULL;
|
||||
$renewOptions = $itemData['p_renew_options'] ? json_decode( $itemData['p_renew_options'], TRUE ) : array();
|
||||
$price = \IPS\nexus\Package::lowestPriceFromData( $customer, $currency, $indexData['index_item_id'], json_decode( $itemData['p_base_price'], TRUE ), json_decode( $itemData['p_discounts'], TRUE ), $renewOptions, $itemData['p_stock'], $itemData['extra']['tax'] ? \IPS\nexus\Tax::constructFromData( $itemData['extra']['tax'] ) : NULL, $priceMayChange, $priceNotIncludingDiscounts );
|
||||
|
||||
if ( $price < $priceNotIncludingDiscounts )
|
||||
{
|
||||
$price = \IPS\Theme::i()->getTemplate( 'store', 'nexus' )->priceDiscounted( new \IPS\nexus\Money( $priceNotIncludingDiscounts, $currency ), new \IPS\nexus\Money( $price, $currency ), $priceMayChange, TRUE, 'ipsStream_price' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$price = \IPS\Theme::i()->getTemplate( 'store', 'nexus' )->price( new \IPS\nexus\Money( $price, $currency ), $priceMayChange, TRUE, 'ipsStream_price' );
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
$price = NULL;
|
||||
}
|
||||
|
||||
/* Display */
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'nexus', 'front' )->searchResultProductSnippet( $indexData, $itemData, isset( $itemData['extra']['image'] ) ? $itemData['extra']['image'] : NULL, $url, $price, $view == 'condensed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Moderator Permission
|
||||
*
|
||||
* @param string $type 'edit', 'hide', 'unhide', 'delete', etc.
|
||||
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @param \IPS\Node\Model|NULL $container The container
|
||||
* @return bool
|
||||
*/
|
||||
public static function modPermission( $type, \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
|
||||
{
|
||||
/* Commerce Items have no own content type, so we can use only the general mod permissions */
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
if ( in_array( $type, array( 'hide', 'unhide', 'delete' ) ) and $container )
|
||||
{
|
||||
if ( $member->modPermission( "can_{$type}" ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::modPermission( $type, $member, $container );
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ class _Package extends \IPS\Node\Model
|
||||
*/
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
$classname = 'IPS\nexus\Package\\' . ucfirst( $data['p_type'] );
|
||||
$classname = 'IPS\nexus\Package\\' . mb_ucfirst( $data['p_type'] );
|
||||
if ( isset( $classname::$packageDatabaseTable ) )
|
||||
{
|
||||
$data = array_merge( $data, \IPS\Db::i()->select( '*', $classname::$packageDatabaseTable, array( 'p_id=?', $data['p_id'] ) )->first() );
|
||||
@@ -163,7 +163,7 @@ class _Package extends \IPS\Node\Model
|
||||
$secondaryTable[ "p_{$k}" ] = $v;
|
||||
unset( $this->changed[ $k ] );
|
||||
}
|
||||
elseif ( !in_array( "p_{$k}", array( 'p_id', 'p_name', 'p_seo_name', 'p_desc', 'p_group', 'p_stock', 'p_reg', 'p_store', 'p_member_groups', 'p_allow_upgrading', 'p_upgrade_charge', 'p_allow_downgrading', 'p_downgrade_refund', 'p_base_price', 'p_tax', 'p_renewal_days', 'p_primary_group', 'p_secondary_group', 'p_return_primary', 'p_return_secondary', 'p_position', 'p_associable', 'p_force_assoc', 'p_assoc_error', 'p_discounts', 'p_page', 'p_support', 'p_support_department', 'p_support_severity', 'p_featured', 'p_upsell', 'p_notify', 'p_type', 'p_custom', 'p_reviewable', 'p_review_moderate', 'p_image', 'p_methods', 'p_renew_options', 'p_group_renewals', 'p_rebuild_thumb', 'p_renewal_days_advance', 'p_date_added', 'p_reviews', 'p_rating', 'p_grace_period' ) ) )
|
||||
elseif ( !in_array( "p_{$k}", array( 'p_id', 'p_name', 'p_seo_name', 'p_desc', 'p_group', 'p_stock', 'p_reg', 'p_store', 'p_member_groups', 'p_allow_upgrading', 'p_upgrade_charge', 'p_allow_downgrading', 'p_downgrade_refund', 'p_base_price', 'p_tax', 'p_renewal_days', 'p_primary_group', 'p_secondary_group', 'p_return_primary', 'p_return_secondary', 'p_position', 'p_associable', 'p_force_assoc', 'p_assoc_error', 'p_discounts', 'p_page', 'p_support', 'p_support_department', 'p_support_severity', 'p_featured', 'p_upsell', 'p_notify', 'p_type', 'p_custom', 'p_reviewable', 'p_review_moderate', 'p_image', 'p_methods', 'p_renew_options', 'p_group_renewals', 'p_rebuild_thumb', 'p_renewal_days_advance', 'p_date_added', 'p_reviews', 'p_rating', 'p_grace_period', 'p_email_purchase_type', 'p_email_purchase', 'p_email_expire_soon_type', 'p_email_expire_soon', 'p_email_expire_type', 'p_email_expire' ) ) )
|
||||
{
|
||||
unset( $this->changed[ $k ] );
|
||||
}
|
||||
@@ -189,7 +189,8 @@ class _Package extends \IPS\Node\Model
|
||||
\IPS\Db::i()->replace( 'nexus_package_base_prices', $prices );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->date_updated = time();
|
||||
parent::save();
|
||||
$this->changed = $data;
|
||||
|
||||
@@ -208,6 +209,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
parent::delete();
|
||||
\IPS\Db::i()->delete( static::$packageDatabaseTable, array( 'p_id=?', $this->id ) );
|
||||
\IPS\Content\Search\Index::i()->removeFromSearchIndex( $this->item() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,20 +334,53 @@ class _Package extends \IPS\Node\Model
|
||||
* @param bool $bulkDiscounts Account for bulk discounts?
|
||||
* @param int $initialCount Will assume that number of additional purchases for this package
|
||||
* @return \IPS\nexus\Money
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public function price( \IPS\nexus\Customer $customer = NULL, $usergroupDiscounts = TRUE, $loyaltyDiscounts = TRUE, $bulkDiscounts = TRUE, $initialCount = 0 )
|
||||
{
|
||||
return static::priceFromData(
|
||||
$this->id,
|
||||
json_decode( $this->base_price, TRUE ),
|
||||
json_decode( $this->discounts, TRUE ),
|
||||
$customer,
|
||||
NULL,
|
||||
$usergroupDiscounts,
|
||||
$loyaltyDiscounts,
|
||||
$bulkDiscounts,
|
||||
$initialCount
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic purchase price
|
||||
* May be further adjusted by renewal terms which add
|
||||
*
|
||||
* @param int $packageId Package ID
|
||||
* @param array $prices Base Price data
|
||||
* @param array $discounts Discount data
|
||||
* @param \IPS\nexus\Customer $customer The customer (NULL for currently logged in member)
|
||||
* @param string $currency The currency (NULL to autodetect based on $customer)
|
||||
* @param bool $usergroupDiscounts Account for usergroup discounts?
|
||||
* @param bool $loyaltyDiscounts Account for loyalty discounts?
|
||||
* @param bool $bulkDiscounts Account for bulk discounts?
|
||||
* @param int $initialCount Will assume that number of additional purchases for this package
|
||||
* @return \IPS\nexus\Money
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public static function priceFromData( $packageId, $prices, $discounts, \IPS\nexus\Customer $customer = NULL, $currency = NULL, $usergroupDiscounts = TRUE, $loyaltyDiscounts = TRUE, $bulkDiscounts = TRUE, $initialCount = 0 )
|
||||
{
|
||||
/* Base */
|
||||
$customer = $customer ?: \IPS\nexus\Customer::loggedIn();
|
||||
$currency = ( $customer->member_id === \IPS\nexus\Customer::loggedIn()->member_id ) ? ( ( isset( $_SESSION['currency'] ) and in_array( $_SESSION['currency'], \IPS\nexus\Money::currencies() ) ) ? $_SESSION['currency'] : $customer->defaultCurrency() ) : $customer->defaultCurrency();
|
||||
$prices = json_decode( $this->base_price, TRUE );
|
||||
if ( !$currency )
|
||||
{
|
||||
$currency = ( $customer->member_id === \IPS\nexus\Customer::loggedIn()->member_id ) ? ( ( isset( $_SESSION['currency'] ) and in_array( $_SESSION['currency'], \IPS\nexus\Money::currencies() ) ) ? $_SESSION['currency'] : $customer->defaultCurrency() ) : $customer->defaultCurrency();
|
||||
}
|
||||
if ( !isset( $prices[ $currency ] ) )
|
||||
{
|
||||
throw new \OutOfBoundsException;
|
||||
}
|
||||
$price = $prices[ $currency ]['amount'];
|
||||
$discounts = json_decode( $this->discounts, TRUE );
|
||||
|
||||
|
||||
/* Usergroup discounts */
|
||||
if ( $usergroupDiscounts and isset( $discounts['usergroup'] ) )
|
||||
{
|
||||
@@ -375,11 +410,11 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
foreach ( $discounts['loyalty'] as $discount )
|
||||
{
|
||||
$comparingPackageId = ( $discount['package'] ?: $this->id );
|
||||
$count = ( $comparingPackageId == $this->id ) ? $initialCount : 0;
|
||||
$comparingPackageId = ( $discount['package'] ?: $packageId );
|
||||
$count = ( $comparingPackageId == $packageId ) ? $initialCount : 0;
|
||||
if ( $discount['price'][ $currency ] < $price )
|
||||
{
|
||||
$count += $customer->member_id ? $customer->previousPurchasesCount( $discount['package'] ?: $this->id, $discount['active'] ) : 0;
|
||||
$count += $customer->member_id ? $customer->previousPurchasesCount( $discount['package'] ?: $packageId, $discount['active'] ) : 0;
|
||||
}
|
||||
if ( $customer->member_id === \IPS\Member::loggedIn()->member_id and isset( $_SESSION['cart'] ) )
|
||||
{
|
||||
@@ -406,8 +441,8 @@ class _Package extends \IPS\Node\Model
|
||||
rsort( $discounts['bulk'] );
|
||||
$discount = array_shift( $discounts['bulk'] );
|
||||
|
||||
$discountPackageId = ( $discount['package'] ?: $this->id );
|
||||
$count = ( $discountPackageId == $this->id ) ? $initialCount : 0;
|
||||
$discountPackageId = ( $discount['package'] ?: $packageId );
|
||||
$count = ( $discountPackageId == $packageId ) ? $initialCount : 0;
|
||||
if ( $customer->member_id === \IPS\Member::loggedIn()->member_id and isset( $_SESSION['cart'] ) )
|
||||
{
|
||||
foreach ( $_SESSION['cart'] as $item )
|
||||
@@ -440,15 +475,46 @@ class _Package extends \IPS\Node\Model
|
||||
*/
|
||||
protected function _lowestPrice( \IPS\nexus\Customer $customer, $currency, &$priceMayChange = FALSE, &$priceNotIncludingDiscounts = NULL )
|
||||
{
|
||||
/* What's the base price with and without discounts? */
|
||||
$baseNoDiscounts = $this->price( $customer, FALSE, FALSE, FALSE )->amount;
|
||||
$base = $this->price( $customer )->amount;
|
||||
$renewOptions = $this->renew_options ? json_decode( $this->renew_options, TRUE ) : array();
|
||||
|
||||
$tax = NULL;
|
||||
if ( \IPS\Settings::i()->nexus_show_tax and $this->tax )
|
||||
{
|
||||
try
|
||||
{
|
||||
$tax = \IPS\nexus\Tax::load( $this->tax );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
return static::lowestPriceFromData( $customer, $currency, $this->id, json_decode( $this->base_price, TRUE ), json_decode( $this->discounts, TRUE ), $renewOptions, $this->stock, $tax, $priceMayChange, $priceNotIncludingDiscounts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lowest price
|
||||
*
|
||||
* @param \IPS\nexus\Customer $customer The customer
|
||||
* @param int $packageId Package ID
|
||||
* @param array $prices Base Price data
|
||||
* @param array $discounts Discount data
|
||||
* @param array $renewOptions Renewal options data
|
||||
* @param int $stock Stock type (-2 for if it adjusts on fields)
|
||||
* @param \IPS\nexus\Tax|NULL $tax Tax rate
|
||||
* @param bool $priceMayChange [Reference] will be set to a value indicating if the price might change (i.e. should be displayed as "From $x")
|
||||
* @param float $priceNotIncludingDiscounts [Reference] will be set to the lowest price not taking discounts into consideration
|
||||
* @return float
|
||||
*/
|
||||
static function lowestPriceFromData( \IPS\nexus\Customer $customer, $currency, $packageId, $prices, $discounts, $renewOptions, $stock, $tax, &$priceMayChange = FALSE, &$priceNotIncludingDiscounts = NULL )
|
||||
{
|
||||
/* What's the base price with and without discounts? */
|
||||
$baseNoDiscounts = static::priceFromData( $packageId, $prices, $discounts, $customer, $currency, FALSE, FALSE, FALSE )->amount;
|
||||
$base = static::priceFromData( $packageId, $prices, $discounts, $customer, $currency )->amount;
|
||||
|
||||
/* Adjustments based on custom fields */
|
||||
if ( $this->stock == -2 )
|
||||
if ( $stock == -2 )
|
||||
{
|
||||
$mostReducedOption = NULL;
|
||||
foreach ( \IPS\Db::i()->select( '*', 'nexus_product_options', array( 'opt_package=?', $this->id ) ) as $option )
|
||||
foreach ( \IPS\Db::i()->select( '*', 'nexus_product_options', array( 'opt_package=?', $packageId ) ) as $option )
|
||||
{
|
||||
$basePriceAdjustments = json_decode( $option['opt_base_price'], TRUE );
|
||||
if ( isset( $basePriceAdjustments[ $currency ] ) and ( $basePriceAdjustments[ $currency ] or $basePriceAdjustments[ $currency ] === '0' ) )
|
||||
@@ -472,7 +538,6 @@ class _Package extends \IPS\Node\Model
|
||||
$lowest = $base;
|
||||
|
||||
/* It's possible the base price is $0, but all the renewal options add, so let's look at that */
|
||||
$renewOptions = $this->renew_options ? json_decode( $this->renew_options, TRUE ) : array();
|
||||
if ( !empty( $renewOptions ) )
|
||||
{
|
||||
$lowestNoDiscounts = NULL;
|
||||
@@ -506,21 +571,14 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/* Add tax? */
|
||||
if ( \IPS\Settings::i()->nexus_show_tax and $this->tax )
|
||||
if ( \IPS\Settings::i()->nexus_show_tax and $tax )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Load tax rate */
|
||||
$tax = \IPS\nexus\Tax::load( $this->tax );
|
||||
|
||||
/* What is the rate? */
|
||||
$taxRate = new \IPS\Math\Number( $tax->rate( $customer->estimatedLocation() ) );
|
||||
|
||||
/* Add it on */
|
||||
$lowest = $lowest->add( $lowest->multiply( $taxRate ) );
|
||||
$lowestNoDiscounts = $lowestNoDiscounts->add( $lowestNoDiscounts->multiply( $taxRate ) );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
/* What is the rate? */
|
||||
$taxRate = new \IPS\Math\Number( $tax->rate( $customer->estimatedLocation() ) );
|
||||
|
||||
/* Add it on */
|
||||
$lowest = $lowest->add( $lowest->multiply( $taxRate ) );
|
||||
$lowestNoDiscounts = $lowestNoDiscounts->add( $lowestNoDiscounts->multiply( $taxRate ) );
|
||||
}
|
||||
|
||||
/* Return */
|
||||
@@ -708,6 +766,15 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$buttons = array_merge( $buttons, parent::getButtons( $url, $subnode ) );
|
||||
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'subscriptions', 'subscriptions_manage' ) and \IPS\nexus\Subscription\Package::canConvert( $this ) )
|
||||
{
|
||||
$buttons['convert_subs'] = array(
|
||||
'icon' => 'certificate',
|
||||
'title' => 'nexus_subs_convert',
|
||||
'link' => \IPS\Http\Url::internal( 'app=nexus&module=subscriptions&controller=subscriptions&do=convertToSubscription&id=' . $this->_id )
|
||||
);
|
||||
}
|
||||
|
||||
return $buttons;
|
||||
}
|
||||
@@ -721,13 +788,18 @@ class _Package extends \IPS\Node\Model
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
{
|
||||
$groups = array();
|
||||
foreach ( \IPS\Member\Group::groups() as $group )
|
||||
{
|
||||
$groups[ $group->g_id ] = $group->name;
|
||||
}
|
||||
|
||||
$groupsExcludingGuestsAndAdmins = array();
|
||||
foreach ( \IPS\Member\Group::groups( FALSE, FALSE ) as $group )
|
||||
{
|
||||
$groupsExcludingGuestsAndAdmins[ $group->g_id ] = $group->name;
|
||||
}
|
||||
|
||||
$types = array();
|
||||
$typeFields = array();
|
||||
$typeFieldToggles = array();
|
||||
@@ -856,15 +928,19 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
$form->addTab( 'package_pricing' );
|
||||
$form->addHeader( 'package_purchase_price' );
|
||||
|
||||
$form->add( new \IPS\nexus\Form\Money( 'p_base_price', $this->base_price, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'p_tax', (int) $this->tax, FALSE, array( 'class' => 'IPS\nexus\Tax', 'zeroVal' => 'do_not_tax' ) ) );
|
||||
$form->addHeader('package_discounts');
|
||||
$discounts = json_decode( $this->discounts, TRUE );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_usergroup_discounts', isset( $discounts['usergroup'] ) ? $discounts['usergroup'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountUsergroup' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_loyalty_discounts', isset( $discounts['loyalty'] ) ? $discounts['loyalty'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountLoyalty' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_bulk_discounts', isset( $discounts['bulk'] ) ? $discounts['bulk'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountBulk' ) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_set_discounts', ( is_array( $discounts ) AND count( $discounts ) ), FALSE, array( 'togglesOn' => array( 'p_usergroup_discounts', 'p_loyalty_discounts', 'p_bulk_discounts' ) ), NULL, NULL, NULL, 'p_set_discounts' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_usergroup_discounts', isset( $discounts['usergroup'] ) ? $discounts['usergroup'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountUsergroup' ), NULL, NULL, NULL, 'p_usergroup_discounts' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_loyalty_discounts', isset( $discounts['loyalty'] ) ? $discounts['loyalty'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountLoyalty' ), NULL, NULL, NULL, 'p_loyalty_discounts' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_bulk_discounts', isset( $discounts['bulk'] ) ? $discounts['bulk'] : array(), FALSE, array( 'stackFieldType' => 'IPS\nexus\Form\DiscountBulk' ), NULL, NULL, NULL, 'p_bulk_discounts' ) );
|
||||
|
||||
$form->addHeader( 'package_renewals' );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_renews', !empty( $renewOptions ), FALSE, array( 'togglesOn' => array( 'p_renew_options', 'p_grace_period', 'p_renew', ( $this->id ? "form_{$this->id}" : 'form_new' ) . '_header_package_client_area_renewals' ) ), NULL, NULL, NULL, 'p_renews' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_renews', !empty( $renewOptions ), FALSE, array( 'togglesOn' => array( 'p_renew_options', 'p_grace_period', 'p_renew', ( $this->id ? "form_{$this->id}" : 'form_new' ) . '_header_package_client_area_renewals', 'p_email_expire_soon_type', 'p_email_expire_type' ) ), NULL, NULL, NULL, 'p_renews' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_renew_options', $renewOptions, FALSE, array(
|
||||
'stackFieldType' => 'IPS\nexus\Form\RenewalTerm',
|
||||
'allCurrencies' => TRUE,
|
||||
@@ -903,6 +979,7 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
), NULL, NULL, NULL, 'p_dynamic_stock' ) );
|
||||
|
||||
|
||||
$form->addTab('package_store');
|
||||
$form->addHeader('package_store_permissions');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_store', $this->store, FALSE, array( 'togglesOn' => array( 'p_member_groups', 'p_featured', 'p_desc_editor', 'p_images', 'p_reviewable', "{$form->id}_header_package_store_display", "{$form->id}_header_package_reviews" ) ) ) );
|
||||
@@ -928,21 +1005,20 @@ class _Package extends \IPS\Node\Model
|
||||
$form->addHeader('package_reviews');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_reviewable', $this->reviewable, FALSE, array( 'togglesOn' => array( 'p_review_moderate' ) ), NULL, NULL, NULL, 'p_reviewable' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_review_moderate', $this->review_moderate ?: FALSE, FALSE, array(), NULL, NULL, NULL, 'p_review_moderate' ) );
|
||||
$form->addHeader( 'package_notify' );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_notify', $this->notify ? explode( ',', $this->notify ) : array(), FALSE, array( 'stackFieldType' => 'Email' ) ) );
|
||||
|
||||
$form->addHeader( 'package_benefits' );
|
||||
unset( $groups[ \IPS\Settings::i()->guest_group ] );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'p_primary_group', $this->primary_group ?: '*', FALSE, array( 'options' => $groups, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_primary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->addTab( 'package_benefits' );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'p_primary_group', $this->primary_group ?: '*', FALSE, array( 'options' => $groupsExcludingGuestsAndAdmins, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_primary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_return_primary', $this->return_primary, FALSE, array(), NULL, NULL, NULL, 'p_return_primary' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'p_secondary_group', $this->secondary_group ? explode( ',', $this->secondary_group ) : '*', FALSE, array( 'options' => $groups, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_secondary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'p_secondary_group', $this->secondary_group ? explode( ',', $this->secondary_group ) : '*', FALSE, array( 'options' => $groupsExcludingGuestsAndAdmins, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_secondary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_return_secondary', $this->return_secondary, FALSE, array(), NULL, NULL, NULL, 'p_return_secondary' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'p_support_severity', $this->support_severity ?: 0, FALSE, array( 'class' => 'IPS\nexus\Support\Severity', 'zeroVal' => 'none' ), NULL, NULL, NULL, 'p_support_severity' ) );
|
||||
foreach ( $typeFields['package_benefits'] as $field )
|
||||
{
|
||||
$form->add( $field );
|
||||
}
|
||||
$form->addHeader( 'package_notify' );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'p_notify', $this->notify ? explode( ',', $this->notify ) : array(), FALSE, array( 'stackFieldType' => 'Email' ) ) );
|
||||
|
||||
|
||||
$form->addTab('package_client_area');
|
||||
$form->addHeader('package_client_area_display');
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'p_page', NULL, FALSE, array(
|
||||
@@ -975,6 +1051,41 @@ class _Package extends \IPS\Node\Model
|
||||
1 => 'p_downgrade_refund_full',
|
||||
2 => 'p_downgrade_refund_prorate'
|
||||
)), NULL, NULL, NULL, 'p_downgrade_refund' ) );
|
||||
|
||||
$form->addTab('package_custom_emails');
|
||||
$form->addMessage('package_custom_emails_blurb');
|
||||
foreach ( \IPS\Settings::i()->cm_invoice_warning ? array( 'purchase', 'expire_soon', 'expire' ) : array( 'purchase', 'expire' ) as $k )
|
||||
{
|
||||
$typeKey = "email_{$k}_type";
|
||||
$valueKey = "email_{$k}";
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( "p_email_{$k}_type", $this->$typeKey, FALSE, array(
|
||||
'options' => array(
|
||||
'' => 'p_email_none',
|
||||
'wysiwyg' => 'p_email_wysiwyg',
|
||||
'html_template' => 'p_email_html_template',
|
||||
'html_raw' => 'p_email_html_raw',
|
||||
),
|
||||
'toggles' => array(
|
||||
'wysiwyg' => array( "p_email_{$k}_subject", "p_email_{$k}_wysiwyg" ),
|
||||
'html_template' => array( "p_email_{$k}_subject", "p_email_{$k}_html" ),
|
||||
'html_raw' => array( "p_email_{$k}_subject", "p_email_{$k}_html" )
|
||||
)
|
||||
), NULL, NULL, NULL, "p_email_{$k}_type" ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( "p_email_{$k}_subject", NULL, FALSE, array( 'app' => 'nexus', 'key' => $this->id ? "nexus_package_{$this->id}_email_{$k}_subject" : NULL ), NULL, NULL, NULL, "p_email_{$k}_subject" ) );
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( "p_email_{$k}_html", $this->$valueKey, FALSE, array(
|
||||
'preview' => \IPS\Http\Url::internal('app=nexus&module=store&controller=packages&do=emailPreview'),
|
||||
'tags' => array(
|
||||
'{$purchase->name}' => \IPS\Member::loggedIn()->language()->addToStack('ps_name'),
|
||||
'{$purchase->expires}' => \IPS\Member::loggedIn()->language()->addToStack('p_email_tag_expire'),
|
||||
'{$purchase->renewals}' => \IPS\Member::loggedIn()->language()->addToStack('ps_renewals'),
|
||||
'{$purchase->custom_fields[1]}' => \IPS\Member::loggedIn()->language()->addToStack('p_email_tag_field'),
|
||||
'{$purchase->url()}' => \IPS\Member::loggedIn()->language()->addToStack('p_email_tag_url'),
|
||||
'{$purchase->licenseKey()->key}'=> \IPS\Member::loggedIn()->language()->addToStack('license_key'),
|
||||
)
|
||||
), NULL, NULL, NULL, "p_email_{$k}_html" ) );
|
||||
$form->add( new \IPS\Helpers\Form\Editor( "p_email_{$k}_wysiwyg", $this->$valueKey, FALSE, array( 'app' => 'nexus', 'key' => 'Admin', 'autoSaveKey' => $this->id ? "nexus-pkg-{$this->id}-{$k}-email" : "nexus-new-pkg-{$k}-email" ), NULL, NULL, NULL, "p_email_{$k}_wysiwyg" ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -995,7 +1106,7 @@ class _Package extends \IPS\Node\Model
|
||||
\IPS\File::claimAttachments( 'nexus-new-pkg-assoc', $this->id, NULL, 'pkg-assoc', TRUE );
|
||||
\IPS\File::claimAttachments( 'nexus-new-pkg-pg', $this->id, NULL, 'pkg-pg', TRUE );
|
||||
|
||||
$class = 'IPS\nexus\Package\\' . ucfirst( $values['p_type'] );
|
||||
$class = 'IPS\nexus\Package\\' . mb_ucfirst( $values['p_type'] );
|
||||
|
||||
if ( isset( $class::$packageDatabaseTable ) )
|
||||
{
|
||||
@@ -1006,7 +1117,14 @@ class _Package extends \IPS\Node\Model
|
||||
return $obj->saveForm( $obj->formatFormValues( $values ) );
|
||||
}
|
||||
|
||||
return parent::saveForm( $values );
|
||||
$return = parent::saveForm( $values );
|
||||
|
||||
if ( !$this->custom )
|
||||
{
|
||||
\IPS\Content\Search\Index::i()->index( $this->item() );
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1022,8 +1140,30 @@ class _Package extends \IPS\Node\Model
|
||||
return $values;
|
||||
}
|
||||
|
||||
/* Email content */
|
||||
foreach ( array( 'purchase', 'expire_soon', 'expire' ) as $k )
|
||||
{
|
||||
if ( isset( $values["p_email_{$k}_type"] ) )
|
||||
{
|
||||
if ( $values["p_email_{$k}_type"] == 'wysiwyg' )
|
||||
{
|
||||
$values["p_email_{$k}"] = $values["p_email_{$k}_wysiwyg"];
|
||||
}
|
||||
elseif ( in_array( $values["p_email_{$k}_type"], array( 'html_template', 'html_raw' ) ) )
|
||||
{
|
||||
$values["p_email_{$k}"] = $values["p_email_{$k}_html"];
|
||||
}
|
||||
else
|
||||
{
|
||||
$values["p_email_{$k}"] = NULL;
|
||||
}
|
||||
}
|
||||
unset( $values["p_email_{$k}_wysiwyg"] );
|
||||
unset( $values["p_email_{$k}_html"] );
|
||||
}
|
||||
|
||||
/* Translatables */
|
||||
foreach ( array( 'name' => '', 'desc' => '_desc', 'assoc_error' => '_assoc', 'page' => '_page' ) as $key => $suffix )
|
||||
foreach ( array( 'name' => '', 'desc' => '_desc', 'assoc_error' => '_assoc', 'page' => '_page', 'email_purchase_subject' => '_email_purchase_subject', 'email_expire_soon_subject' => '_email_expire_soon_subject', 'email_expire_subject' => '_email_expire_subject' ) as $key => $suffix )
|
||||
{
|
||||
if ( isset( $values[ 'p_' . $key ] ) )
|
||||
{
|
||||
@@ -1472,7 +1612,7 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'primary_group', $changes ) or array_key_exists( 'secondary_group', $changes ) )
|
||||
if ( array_key_exists( 'primary_group', $changes ) or array_key_exists( 'secondary_group', $changes ) AND ( !$purchase->expire OR $purchase->expire->getTimestamp() > time() ) )
|
||||
{
|
||||
$this->_removeUsergroups( $purchase, $changes );
|
||||
$this->_addUsergroups( $purchase );
|
||||
@@ -1632,9 +1772,8 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Item ibject
|
||||
* Create Item Object
|
||||
*
|
||||
* @param \IPS\nexus\Package $package The package
|
||||
* @param \IPS\nexus\Money $price Price
|
||||
* @return \IPS\nexus\extensions\nexus\Item\Package
|
||||
*/
|
||||
@@ -1855,7 +1994,14 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
if ( isset( $values[ 'nexus_pfield_' . $field->id ] ) )
|
||||
{
|
||||
$customFieldsSave[ $field->id ] = $class::stringValue( $values[ 'nexus_pfield_' . $field->id ] );
|
||||
if ( $class instanceof \IPS\Helpers\Form\Upload )
|
||||
{
|
||||
$customFieldsSave[ $field->id ] = (string) $values[ 'nexus_pfield_' . $field->id ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$customFieldsSave[ $field->id ] = $class::stringValue( $values[ 'nexus_pfield_' . $field->id ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1952,7 +2098,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
\IPS\Output::i()->redirect( $invoice->checkoutUrl() );
|
||||
}
|
||||
$purchase->member->log( 'purchase', array( 'type' => 'change', 'id' => $purchase->id, 'old' => $purchase->name, 'name' => $newPackage->_title, 'system' => FALSE ) );
|
||||
$purchase->member->log( 'purchase', array( 'type' => 'change', 'id' => $purchase->id, 'old' => $purchase->name, 'name' => $newPackage->titleForLog(), 'system' => FALSE ) );
|
||||
\IPS\Output::i()->redirect( $purchase->url() );
|
||||
}
|
||||
return (string) $form;
|
||||
@@ -1998,7 +2144,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$options[ $k ] = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $term['cost'][$currency]['amount'], $currency ), new \DateInterval( 'P' . $term['term'] . mb_strtoupper( $term['unit'] ) ) );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'ps_renewals', NULL, TRUE, array( 'options' => $options, 'parse' => 'none' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'ps_renewals', NULL, TRUE, array( 'options' => $options, 'parse' => 'normal' ) ) );
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$term = $renewalOptions[ $values['ps_renewals'] ];
|
||||
@@ -2148,8 +2294,15 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$class = 'IPS\Helpers\Form\\' . $field->type;
|
||||
}
|
||||
|
||||
$customFields[ $field->id ] = $class::stringValue( $values[ 'nexus_pfield_' . $field->id ] );
|
||||
|
||||
if ( $class instanceof \IPS\Helpers\Form\Upload )
|
||||
{
|
||||
$customFields[ $field->id ] = (string) $values[ 'nexus_pfield_' . $field->id ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$customFields[ $field->id ] = $class::stringValue( $values[ 'nexus_pfield_' . $field->id ] );
|
||||
}
|
||||
}
|
||||
|
||||
$purchase->custom_fields = $customFields;
|
||||
@@ -2292,6 +2445,8 @@ class _Package extends \IPS\Node\Model
|
||||
$email->send( $to );
|
||||
}
|
||||
}
|
||||
|
||||
$this->sendCustomEmail( 'purchase', $purchase );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2317,6 +2472,18 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On expire soon
|
||||
* If returns TRUE, the normal expire warning email will not be sent
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @return void
|
||||
*/
|
||||
public function onExpireWarning( \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
return $this->sendCustomEmail( 'expire_soon', $purchase );
|
||||
}
|
||||
|
||||
/**
|
||||
* On Purchase Expired
|
||||
*
|
||||
@@ -2326,6 +2493,8 @@ class _Package extends \IPS\Node\Model
|
||||
public function onExpire( \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
$this->_removeUsergroups( $purchase );
|
||||
|
||||
$this->sendCustomEmail( 'expire', $purchase );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2390,6 +2559,39 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a custom email
|
||||
*
|
||||
* @param string $type Which custom email
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @return bool
|
||||
*/
|
||||
public function sendCustomEmail( $type, \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
$typeKey = "email_{$type}_type";
|
||||
$valueKey = "email_{$type}";
|
||||
|
||||
if ( $this->$typeKey )
|
||||
{
|
||||
if ( $this->$typeKey == 'wysiwyg' )
|
||||
{
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), $this->$valueKey, NULL, \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
}
|
||||
else
|
||||
{
|
||||
$functionName = 'nexus_customPurchaseEmail_' . $this->id;
|
||||
\IPS\Theme::makeProcessFunction( $this->$valueKey, $functionName, '$purchase' );
|
||||
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), call_user_func( 'IPS\\Theme\\'.$functionName, $purchase ), NULL, \IPS\Email::TYPE_TRANSACTIONAL, $this->$typeKey == 'html_template' );
|
||||
}
|
||||
$email->send( $purchase->member );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* !Upgrading/Downgrading */
|
||||
|
||||
/**
|
||||
@@ -2403,14 +2605,15 @@ class _Package extends \IPS\Node\Model
|
||||
*/
|
||||
protected function upgradeDowngradeOptions( \IPS\nexus\Purchase $purchase, $overrideRestrictions = FALSE, &$includesUpgrades=FALSE, &$includesDowngrades=FALSE )
|
||||
{
|
||||
/* Cannot upgrade/downgrade custom or inactive packages */
|
||||
if ( $this->custom or !$purchase->active )
|
||||
/* Cannot upgrade/downgrade custom or cancelled packages */
|
||||
if ( $this->custom or $purchase->cancelled )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/* Cannot upgrade/downgrade without permission unless $overrideRestrictions is set */
|
||||
if ( !$this->allow_upgrading and !$overrideRestrictions )
|
||||
if ( ( !$this->allow_upgrading AND !$this->allow_downgrading ) AND !$overrideRestrictions )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
@@ -2570,7 +2773,7 @@ class _Package extends \IPS\Node\Model
|
||||
$desciptions[ $package->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'downgrade_refund_and_renew', FALSE, array( 'sprintf' => array( $costToUpgrade, $renewalOptionsString ) ) );
|
||||
}
|
||||
|
||||
$renewFields[] = new \IPS\Helpers\Form\Radio( 'renew_option_' . $package->id, NULL, NULL, array( 'options' => $renewalOptions, 'descriptions' => $renewFieldsDescriptions, 'parse' => 'none' ), NULL, NULL, NULL, 'renew_option_' . $package->id );
|
||||
$renewFields[] = new \IPS\Helpers\Form\Radio( 'renew_option_' . $package->id, NULL, NULL, array( 'options' => $renewalOptions, 'descriptions' => $renewFieldsDescriptions, 'parse' => 'normal' ), NULL, NULL, NULL, 'renew_option_' . $package->id );
|
||||
\IPS\Member::loggedIn()->language()->words['renew_option_' . $package->id] = \IPS\Member::loggedIn()->language()->addToStack( 'renewal_term', FALSE );
|
||||
$toggles[ $package->id ] = array( 'renew_option_' . $package->id );
|
||||
}
|
||||
@@ -2578,7 +2781,7 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'change_package_to', NULL, TRUE, array( 'options' => $options, 'descriptions' => $desciptions, 'toggles' => $toggles, 'parse' => 'none' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'change_package_to', NULL, TRUE, array( 'options' => $options, 'descriptions' => $desciptions, 'toggles' => $toggles, 'parse' => 'normal' ) ) );
|
||||
foreach ( $renewFields as $field )
|
||||
{
|
||||
$form->add( $field );
|
||||
@@ -2675,6 +2878,7 @@ class _Package extends \IPS\Node\Model
|
||||
$renewalOptionsInDays[ $term->days() ] = $term;
|
||||
}
|
||||
$closestRenewalOption = null;
|
||||
$numberOfDaysInCurrentRenewalTerm = $purchase->renewals->days();
|
||||
foreach ( $renewalOptionsInDays as $days => $term )
|
||||
{
|
||||
if ( $closestRenewalOption === null or abs( $numberOfDaysInCurrentRenewalTerm - $closestRenewalOption ) > abs( $days - $numberOfDaysInCurrentRenewalTerm ) )
|
||||
@@ -2688,7 +2892,7 @@ class _Package extends \IPS\Node\Model
|
||||
$diff = $purchase->renewals->diff( $renewalTermToUse );
|
||||
|
||||
/* Multiply that by how many days are left */
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( $purchase->renewals->days() );
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( $numberOfDaysInCurrentRenewalTerm );
|
||||
$daysLeftUntilExpiry = new \IPS\Math\Number( (string) ceil( ( $purchase->expire->getTimestamp() - time() ) / 86400 ) );
|
||||
if ( $numberOfDaysInCurrentRenewalTerm->compare( $daysLeftUntilExpiry ) === 0 )
|
||||
{
|
||||
@@ -2725,6 +2929,11 @@ class _Package extends \IPS\Node\Model
|
||||
$item->tax = $newPackage->tax ? \IPS\nexus\Tax::load( $newPackage->tax ) : NULL;
|
||||
$item->id = $purchase->id;
|
||||
$item->extra = array( 'newPackage' => $newPackage->id, 'oldPackage' => $this->id, 'renewalOption' => $chosenRenewalOption, 'previousRenewalTerms' => $purchase->renewals ? array( 'cost' => $purchase->renewals->cost->amount, 'currency' => $purchase->renewals->cost->currency, 'term' => $purchase->renewals->getTerm() ) : NULL );
|
||||
|
||||
if ( $newPackage->methods and $newPackage->methods != '*' )
|
||||
{
|
||||
$item->paymentMethodIds = explode( ',', $newPackage->methods );
|
||||
}
|
||||
|
||||
$invoice = new \IPS\nexus\Invoice;
|
||||
$invoice->member = $purchase->member;
|
||||
@@ -2985,11 +3194,12 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->member->member_group_id = $this->primary_group;
|
||||
$purchase->member->members_bitoptions['ignore_promotions'] = true;
|
||||
$purchase->member->save();
|
||||
$purchase->member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'purchase', 'action' => 'add', 'id' => $purchase->id, 'name' => $purchase->name, 'old' => $extra['old_primary_group'], 'new' => $purchase->member->member_group_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Secondary Groups */
|
||||
$secondary = array_filter( explode( ',', $this->secondary_group ), create_function( '$v', 'return (bool) $v;' ) );
|
||||
$secondary = array_filter( explode( ',', $this->secondary_group ), function( $v ){ return (bool) $v; } );
|
||||
$current_secondary = $purchase->member->mgroup_others ? explode( ',', $purchase->member->mgroup_others ) : array();
|
||||
$new_secondary = $current_secondary;
|
||||
if ( !empty( $secondary ) )
|
||||
@@ -3010,6 +3220,7 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
$purchase->member->mgroup_others = ',' . implode( ',', $new_secondary ) . ',';
|
||||
$purchase->member->save();
|
||||
$purchase->member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'purchase', 'action' => 'add', 'id' => $purchase->id, 'name' => $purchase->name, 'old' => $current_secondary, 'new' => $new_secondary ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3031,23 +3242,37 @@ class _Package extends \IPS\Node\Model
|
||||
/* We only want to move them back if they haven't been moved again since */
|
||||
if ( $purchase->member->member_group_id == $basis['primary_group'] )
|
||||
{
|
||||
$oldGroup = $purchase->member->member_group_id;
|
||||
|
||||
/* Have we made other purchases that have changed their primary group? */
|
||||
try
|
||||
{
|
||||
$purchase->member->member_group_id = \IPS\Db::i()->select( 'p_primary_group', 'nexus_purchases', array( 'ps_member=? AND ps_app=? AND ps_type=? AND ps_active=1 AND p_primary_group<>0 AND ps_id<>?', $purchase->member->member_id, 'nexus', 'package', $purchase->id ) )
|
||||
$next = \IPS\Db::i()->select( array( 'ps_id', 'ps_name', 'p_primary_group' ), 'nexus_purchases', array( 'ps_member=? AND ps_app=? AND ps_type=? AND ps_active=1 AND p_primary_group<>0 AND ps_id<>?', $purchase->member->member_id, 'nexus', 'package', $purchase->id ) )
|
||||
->join( 'nexus_packages', 'p_id=ps_item_id' )
|
||||
->first();
|
||||
|
||||
/* Make sure this group exists */
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $next['p_primary_group'] );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
$purchase->member->member_group_id = $next['p_primary_group'];
|
||||
$purchase->member->save();
|
||||
$purchase->member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'purchase', 'action' => 'change', 'remove_id' => $purchase->id, 'remove_name' => $purchase->name, 'id' => $next['ps_id'], 'name' => $next['ps_name'], 'old' => $oldGroup, 'new' => $purchase->member->member_group_id ) );
|
||||
}
|
||||
/* No, move them to their original group */
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$purchase->member->member_group_id = $purchase->member->cm_return_group;
|
||||
|
||||
/* Does this group exist? */
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $purchase->member->member_group_id );
|
||||
\IPS\Member\Group::load( $purchase->member->cm_return_group );
|
||||
$purchase->member->member_group_id = $purchase->member->cm_return_group;
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
@@ -3058,6 +3283,7 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->member->cm_return_group = 0;
|
||||
$purchase->member->members_bitoptions['ignore_promotions'] = false;
|
||||
$purchase->member->save();
|
||||
$purchase->member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'purchase', 'action' => 'remove', 'id' => $purchase->id, 'name' => $purchase->name, 'old' => $oldGroup, 'new' => $purchase->member->member_group_id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3067,6 +3293,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
/* Work some stuff out */
|
||||
$membersSecondaryGroups = $purchase->member->mgroup_others ? array_unique( array_filter( explode( ',', $purchase->member->mgroup_others ) ) ) : array();
|
||||
$currentSecondaryGroups = $membersSecondaryGroups;
|
||||
$membersPreviousSecondaryGroupsBeforeThisPurchase = array_unique( array_filter( explode( ',', $extra['old_secondary_groups'] ) ) );
|
||||
$secondaryGroupsAwardedByThisPurchase = array_unique( array_filter( explode( ',', $basis['secondary_group'] ) ) );
|
||||
|
||||
@@ -3096,10 +3323,24 @@ class _Package extends \IPS\Node\Model
|
||||
/* If we're still here, remove it */
|
||||
unset( $membersSecondaryGroups[ array_search( $groupId, $membersSecondaryGroups ) ] );
|
||||
}
|
||||
|
||||
/* And make sure only valid groups are saved */
|
||||
$membersSecondaryGroups = array_filter( $membersSecondaryGroups, function( $group ){
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $group );
|
||||
return TRUE;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
});
|
||||
|
||||
/* Save */
|
||||
$purchase->member->mgroup_others = implode( ',', $membersSecondaryGroups );
|
||||
$purchase->member->save();
|
||||
$purchase->member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'purchase', 'action' => 'remove', 'id' => $purchase->id, 'name' => $purchase->name, 'old' => $currentSecondaryGroups, 'new' => $membersSecondaryGroups ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class _Product extends \IPS\nexus\Package
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . ucfirst( $this->lkey );
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . mb_ucfirst( $this->lkey );
|
||||
$licenseKey = new $class;
|
||||
$licenseKey->identifier = $this->lkey_identifier;
|
||||
$licenseKey->purchase = $purchase;
|
||||
@@ -359,7 +359,7 @@ class _Product extends \IPS\nexus\Package
|
||||
/* Invalidate License Key Cache so old data is not loaded */
|
||||
$purchase->licenseKey = NULL;
|
||||
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . ucfirst( $this->lkey );
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . mb_ucfirst( $this->lkey );
|
||||
$licenseKey = new $class;
|
||||
$licenseKey->identifier = $this->lkey_identifier;
|
||||
$licenseKey->purchase = $purchase;
|
||||
@@ -385,7 +385,7 @@ class _Product extends \IPS\nexus\Package
|
||||
{
|
||||
if ( $this->lkey )
|
||||
{
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . ucfirst( $this->lkey );
|
||||
$class = 'IPS\nexus\Purchase\LicenseKey\\' . mb_ucfirst( $this->lkey );
|
||||
$licenseKey = new $class;
|
||||
$licenseKey->identifier = $this->lkey_identifier;
|
||||
$licenseKey->purchase = $purchase;
|
||||
|
||||
@@ -21,8 +21,10 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
/**
|
||||
* Package Review
|
||||
*/
|
||||
class _Review extends \IPS\Content\Review implements \IPS\Content\ReportCenter, \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Embeddable
|
||||
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Embeddable
|
||||
{
|
||||
use \IPS\Content\Reportable;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
@@ -73,6 +75,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\ReportCenter,
|
||||
'votes_total' => 'votes',
|
||||
'votes_helpful' => 'useful',
|
||||
'votes_data' => 'vote_data',
|
||||
'author_response' => 'author_response',
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -322,24 +322,25 @@ abstract class _Payout extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* 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 status Status: 'done' = Payment sent; 'pend' = Pending; 'canc' = Canceled
|
||||
* @apiresponse \IPS\nexus\Money amount Amount
|
||||
* @apiresponse string gateway The gateway that will process the withdrawal
|
||||
* @apiresponse string data The data provided by the member for the process. For example, if the gateway is PayPal, this will be their PayPal email address
|
||||
* @apiresponse datetime requestedDate Date withdrawal was requested
|
||||
* @apiresponse datetime completedDate Date withdrawal was completed
|
||||
* @apiresponse string gatewayId Any ID number provided by the gateway to identify the transaction on their end
|
||||
* @apiresponse \IPS\nexus\Customer customer Customer
|
||||
* @apiresponse int id ID number
|
||||
* @apiresponse string status Status: 'done' = Payment sent; 'pend' = Pending; 'canc' = Canceled
|
||||
* @apiresponse \IPS\nexus\Money amount Amount
|
||||
* @apiresponse string gateway The gateway that will process the withdrawal
|
||||
* @apiresponse string data The data provided by the member for the process. For example, if the gateway is PayPal, this will be their PayPal email address
|
||||
* @apiresponse datetime requestedDate Date withdrawal was requested
|
||||
* @apiresponse datetime completedDate Date withdrawal was completed
|
||||
* @clientapiresponse string gatewayId Any ID number provided by the gateway to identify the transaction on their end
|
||||
* @apiresponse \IPS\nexus\Customer customer Customer
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'status' => $this->status,
|
||||
'amount' => $this->amount->apiOutput(),
|
||||
'member' => $this->member->apiOutput(),
|
||||
'amount' => $this->amount->apiOutput( $authorizedMember ),
|
||||
'member' => $this->member->apiOutput( $authorizedMember ),
|
||||
'gateway' => $this->gateway,
|
||||
'data' => $this->data,
|
||||
'requestedDate' => $this->date->rfc3339(),
|
||||
|
||||
@@ -62,7 +62,7 @@ abstract class _LicenseKey extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
$classname = 'IPS\nexus\Purchase\LicenseKey\\' . ucfirst( $data['lkey_type'] );
|
||||
$classname = 'IPS\nexus\Purchase\LicenseKey\\' . mb_ucfirst( $data['lkey_type'] );
|
||||
|
||||
/* Initiate an object */
|
||||
$obj = new $classname;
|
||||
|
||||
@@ -30,6 +30,6 @@ class _Mdfive extends \IPS\nexus\Purchase\LicenseKey
|
||||
*/
|
||||
public function generate()
|
||||
{
|
||||
return md5( uniqid() );
|
||||
return md5( mt_rand() );
|
||||
}
|
||||
}
|
||||
@@ -105,11 +105,29 @@ class _Purchase extends \IPS\Node\Model
|
||||
public function treeRow( $url, $ref )
|
||||
{
|
||||
$childCount = $this->childrenCount( NULL, NULL, TRUE, array( array( 'ps_show=1' ) ) );
|
||||
$hasCustomFields = count( $this->custom_fields );
|
||||
|
||||
$hasCustomFields = FALSE;
|
||||
foreach ( $this->custom_fields as $k => $v )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( trim( \IPS\nexus\Package\CustomField::load( $k )->displayValue( $v, TRUE ) ) )
|
||||
{
|
||||
$hasCustomFields = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch( \OutOfRangeException $e ) {}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$description = call_user_func( array( $this->extension(), 'getPurchaseNameInfo' ), $this );
|
||||
|
||||
if( empty( $description ) )
|
||||
{
|
||||
$description[] = \IPS\Member::loggedIn()->language()->addToStack( 'purchase_number', FALSE, array( 'sprintf' => array( $this->id ) ) );
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
@@ -128,8 +146,9 @@ class _Purchase extends \IPS\Node\Model
|
||||
}
|
||||
$description[] = $renewals;
|
||||
}
|
||||
|
||||
$description = implode( ' · ', $description );
|
||||
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'trees', 'core' )->row(
|
||||
$url,
|
||||
$this->id,
|
||||
@@ -541,6 +560,34 @@ class _Purchase extends \IPS\Node\Model
|
||||
$this->_data['pay_to'] = $member->member_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fee to commission on renewal charges
|
||||
*
|
||||
* @return \IPS\nexus\Money|NULL
|
||||
*/
|
||||
public function get_fee()
|
||||
{
|
||||
if ( $this->_data['fee'] and $this->renewals )
|
||||
{
|
||||
try
|
||||
{
|
||||
return new \IPS\nexus\Money( $this->_data['fee'], $this->renewals->cost->currency );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fee to commission on renewal charges
|
||||
*
|
||||
* @param \IPS\nexus\Money|NULL $fee The fee
|
||||
* @return void
|
||||
*/
|
||||
public function set_fee( \IPS\nexus\Money $fee = NULL )
|
||||
{
|
||||
$this->_data['fee'] = $fee ? $fee->amount : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original invoice
|
||||
@@ -895,6 +942,7 @@ class _Purchase extends \IPS\Node\Model
|
||||
/**
|
||||
* 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 name Name
|
||||
@@ -914,14 +962,14 @@ class _Purchase extends \IPS\Node\Model
|
||||
* @apiresponse string image If the item has a relevant image (for exmaple, product image, Downloads file screenshot), the URL to it
|
||||
* @apiresponse string url The URL for the customer to view this purchase in the client area
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
$parent = NULL;
|
||||
if ( $this->parent )
|
||||
{
|
||||
try
|
||||
{
|
||||
$parent = static::load( $this->parent )->apiOutput();
|
||||
$parent = static::load( $this->parent )->apiOutput( $authorizedMember );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
}
|
||||
@@ -932,12 +980,12 @@ class _Purchase extends \IPS\Node\Model
|
||||
'itemApp' => $this->app,
|
||||
'itemType' => $this->type,
|
||||
'itemId' => $this->item_id,
|
||||
'customer' => $this->member->apiOutput(),
|
||||
'customer' => $this->member->apiOutput( $authorizedMember ),
|
||||
'purchased' => $this->start->rfc3339(),
|
||||
'expires' => $this->expire ? $this->expire->rfc3339() : null,
|
||||
'active' => (bool) $this->active,
|
||||
'canceled' => (bool) $this->cancelled,
|
||||
'renewalTerm' => $this->renewals ? $this->renewals->apiOutput() : null,
|
||||
'renewalTerm' => $this->renewals ? $this->renewals->apiOutput( $authorizedMember ) : null,
|
||||
'customFields' => $this->custom_fields,
|
||||
'parent' => $parent,
|
||||
'show' => (bool) $this->show,
|
||||
@@ -1090,6 +1138,12 @@ class _Purchase extends \IPS\Node\Model
|
||||
*/
|
||||
public function canRenewUntil( \IPS\Member $member = NULL, $inCycles = FALSE, $admin = FALSE )
|
||||
{
|
||||
/* Can this purchase be renewed at all? */
|
||||
if ( !$this->canBeRenewed() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if ( !$admin and $this->member->member_id !== $member->member_id and !array_key_exists( $member->member_id, iterator_to_array( $this->member->alternativeContacts( array( \IPS\Db::i()->findInSet( 'purchases', array( $this->id ) ) . ' AND billing=1' ) ) ) ) )
|
||||
@@ -1131,6 +1185,23 @@ class _Purchase extends \IPS\Node\Model
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase can be renewed?
|
||||
*
|
||||
* @return boolean|NULL
|
||||
*/
|
||||
public function canBeRenewed()
|
||||
{
|
||||
try
|
||||
{
|
||||
return call_user_func( array( $this->extension(), 'canBeRenewed' ), $this );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can Cancel
|
||||
@@ -1163,7 +1234,7 @@ class _Purchase extends \IPS\Node\Model
|
||||
{
|
||||
if( $this->_url === NULL )
|
||||
{
|
||||
$this->_url = \IPS\Http\Url::internal( "app=nexus&module=clients&controller=purchases&do=view&id={$this->id}", 'front', 'clientspurchase', array( \IPS\Http\Url\Friendly::seoTitle( $this->name ) ), \IPS\Settings::i()->nexus_https );
|
||||
$this->_url = \IPS\Http\Url::internal( "app=nexus&module=clients&controller=purchases&do=view&id={$this->id}", 'front', 'clientspurchase', array( \IPS\Http\Url\Friendly::seoTitle( $this->name ) ) );
|
||||
}
|
||||
|
||||
return $this->_url;
|
||||
|
||||
@@ -268,15 +268,15 @@ class _RenewalTerm
|
||||
$otherTermDetails = $otherTerm->getTerm();
|
||||
foreach ( array( 'thisTermDetails', 'otherTermDetails' ) as $t )
|
||||
{
|
||||
if ( $$t['unit'] === 'y' )
|
||||
if ( ${$t}['unit'] === 'y' )
|
||||
{
|
||||
$$t['unit'] = 'm';
|
||||
$$t['term'] *= 12;
|
||||
${$t}['unit'] = 'm';
|
||||
${$t}['term'] *= 12;
|
||||
}
|
||||
if ( $$t['unit'] === 'w' )
|
||||
if ( ${$t}['unit'] === 'w' )
|
||||
{
|
||||
$$t['unit'] = 'd';
|
||||
$$t['term'] *= 7;
|
||||
${$t}['unit'] = 'd';
|
||||
${$t}['term'] *= 7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,20 +347,21 @@ class _RenewalTerm
|
||||
/**
|
||||
* 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 string term 'd' for days; 'w' for weeks; 'm' for months; 'y' for years
|
||||
* @apiresponse int unit The number for term. For example, if the renewal term is every 6 months, term will be 'm' and unit will be 6
|
||||
* @apiresponse \IPS\nexus\Money price The renewal price
|
||||
* @apiresponse \IPS\nexus\Tax taxClass If the renewal price is taxed, the tax class that applies
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
$term = $this->getTerm();
|
||||
return array(
|
||||
'term' => $term['term'],
|
||||
'unit' => $term['unit'],
|
||||
'price' => $this->cost->apiOutput(),
|
||||
'taxClass' => $this->tax ? $this->tax->apiOutput() : null,
|
||||
'price' => $this->cost->apiOutput( $authorizedMember ),
|
||||
'taxClass' => $this->tax ? $this->tax->apiOutput( $authorizedMember ) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -544,11 +544,12 @@ class _FlatRate extends \IPS\Node\Model implements \IPS\nexus\Shipping\Rate
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
|
||||
@@ -347,6 +347,7 @@ class _Order extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* 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 status Status: 'done' = Shipped; 'pend' = Waiting to be shipped; 'canc' = Canceled
|
||||
@@ -358,7 +359,7 @@ class _Order extends \IPS\Patterns\ActiveRecord
|
||||
* @apiresponse \IPS\GeoLocation address The delivery address
|
||||
* @apiresponse string trackingUrl The URL to view tracking information, if available
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
$items = array();
|
||||
foreach ( $this->items as $item )
|
||||
@@ -377,11 +378,11 @@ class _Order extends \IPS\Patterns\ActiveRecord
|
||||
'id' => $this->id,
|
||||
'status' => $this->status,
|
||||
'invoiceId' => $this->invoice->id,
|
||||
'method' => $this->method ? $this->method->apiOutput() : null,
|
||||
'method' => $this->method ? $this->method->apiOutput( $authorizedMember ) : null,
|
||||
'items' => $items,
|
||||
'requestDate' => $this->date->rfc3339(),
|
||||
'shipDate' => $this->shipped_date ? $this->shipped_date->rfc3339() : null,
|
||||
'address' => $this->address()->apiOutput(),
|
||||
'address' => $this->address()->apiOutput( $authorizedMember ),
|
||||
'trackingUrl' => $this->trackingUrl() ? ( (string) $this->trackingUrl() ) : null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,993 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member subscription Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 9 Feb 2018
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Subscription;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Subscription Model
|
||||
*/
|
||||
class _Package extends \IPS\Node\Model
|
||||
{
|
||||
/* !ActiveRecord */
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_member_subscription_packages';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'sp_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Order Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'position';
|
||||
|
||||
/**
|
||||
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
|
||||
*/
|
||||
public static $titleLangPrefix = 'nexus_subs_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
|
||||
*/
|
||||
public static $descriptionLangSuffix = '_desc';
|
||||
|
||||
/* !Node */
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'menu__nexus_subscriptions_subscriptions';
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
* @code
|
||||
array(
|
||||
'app' => 'core', // The application key which holds the restrictrions
|
||||
'module' => 'foo', // The module key which holds the restrictions
|
||||
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'delete' => 'foo_delete'
|
||||
),
|
||||
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
'app' => 'nexus',
|
||||
'module' => 'subscriptions',
|
||||
'prefix' => 'subscriptions_'
|
||||
);
|
||||
|
||||
protected static $urlBase = 'app=nexus&module=subscriptions&controller=subscriptions&id=';
|
||||
|
||||
protected static $urlTemplate = 'nexus_subscription';
|
||||
|
||||
protected static $seoTitleColumn = '';
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Duplicate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ( $this->skipCloneDuplication === TRUE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$old = $this;
|
||||
|
||||
parent::__clone();
|
||||
|
||||
/* Copy across images */
|
||||
try
|
||||
{
|
||||
$file = \IPS\File::get( 'nexus_Products', $old->image );
|
||||
$this->image = (string) \IPS\File::create( 'nexus_Products', $file->originalFilename, $file->contents() );
|
||||
$this->save();
|
||||
}
|
||||
catch( \Exception $e ) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get buttons to display in tree
|
||||
* Example code explains return value
|
||||
*
|
||||
* @code
|
||||
array(
|
||||
array(
|
||||
'icon' => 'plus-circle', // Name of FontAwesome icon to use
|
||||
'title' => 'foo', // Language key to use for button's title parameter
|
||||
'link' => \IPS\Http\Url::internal( 'app=foo...' ) // URI to link to
|
||||
'class' => 'modalLink' // CSS Class to use on link (Optional)
|
||||
),
|
||||
... // Additional buttons
|
||||
);
|
||||
* @endcode
|
||||
* @param string $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
*/
|
||||
public function getButtons( $url, $subnode=FALSE )
|
||||
{
|
||||
$buttons = array();
|
||||
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'subscriptions', 'subscriptions_manage' ) )
|
||||
{
|
||||
$buttons['add_member'] = array(
|
||||
'icon' => 'plus',
|
||||
'title' => 'nexus_subs_add_member',
|
||||
'link' => $url->setQueryString( array( 'do' => 'addMember', 'id' => $this->id ) ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('nexus_subs_add_member') )
|
||||
);
|
||||
}
|
||||
|
||||
$buttons = array_merge( $buttons, parent::getButtons( $url, $subnode ) );
|
||||
|
||||
return $buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the cover image uRL
|
||||
*
|
||||
* @return \IPS\File
|
||||
*/
|
||||
public function get__image()
|
||||
{
|
||||
return \IPS\File::get( 'nexus_Products', $this->image );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
$groups = array();
|
||||
foreach ( \IPS\Member\Group::groups() as $group )
|
||||
{
|
||||
$groups[ $group->g_id ] = $group->name;
|
||||
}
|
||||
$groupsExcludingGuestsAndAdmins = array();
|
||||
foreach ( \IPS\Member\Group::groups( FALSE, FALSE ) as $group )
|
||||
{
|
||||
$groupsExcludingGuestsAndAdmins[ $group->g_id ] = $group->name;
|
||||
}
|
||||
|
||||
$renewOptions = array();
|
||||
if ( $this->renew_options and $_renewOptions = json_decode( $this->renew_options, TRUE ) and is_array( $_renewOptions ) )
|
||||
{
|
||||
$costs = array();
|
||||
foreach ( $_renewOptions['cost'] as $cost )
|
||||
{
|
||||
$costs[ $cost['currency'] ] = new \IPS\nexus\Money( $cost['amount'], $cost['currency'] );
|
||||
}
|
||||
|
||||
/* Catch any invalid renewal terms, these can occasionally appear from legacy IP.Subscriptions */
|
||||
try
|
||||
{
|
||||
$renewOptions = new \IPS\nexus\Purchase\RenewalTerm( $costs, new \DateInterval( "P{$_renewOptions['term']}" . mb_strtoupper( $_renewOptions['unit'] ) ), NULL );
|
||||
}
|
||||
catch( \Exception $ex) { }
|
||||
|
||||
}
|
||||
|
||||
$form->addHeader('subscription_basic_settings');
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'sp_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => $this->id ? "nexus_subs_{$this->id}" : NULL ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'sp_enabled', $this->id ? $this->enabled : TRUE, FALSE ) );
|
||||
|
||||
$form->addHeader( 'nexus_subs_cost' );
|
||||
$form->add( new \IPS\nexus\Form\Money( 'sp_price', $this->price, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'sp_renews', !empty( $renewOptions ), FALSE, array( 'togglesOn' => array( 'sp_renew_options' ) ), NULL, NULL, NULL, 'sp_renews' ) );
|
||||
$form->add( new \IPS\nexus\Form\RenewalTerm( 'sp_renew_options', $renewOptions, NULL, array( 'allCurrencies' => TRUE ), NULL, NULL, NULL, 'sp_renew_options' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'sp_tax', (int) $this->tax, FALSE, array( 'class' => 'IPS\nexus\Tax', 'zeroVal' => 'do_not_tax' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'sp_gateways', ( !$this->gateways or $this->gateways === '*' ) ? 0 : explode( ',', $this->gateways ), FALSE, array( 'class' => 'IPS\nexus\Gateway', 'multiple' => TRUE, 'zeroVal' => 'any' ) ) );
|
||||
|
||||
|
||||
$form->addHeader( 'nexus_subs_groups' );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'sp_primary_group', $this->primary_group ?: '*', FALSE, array( 'options' => $groupsExcludingGuestsAndAdmins, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_primary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'sp_secondary_group', $this->secondary_group ? explode( ',', $this->secondary_group ) : '*', FALSE, array( 'options' => $groupsExcludingGuestsAndAdmins, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'do_not_change', 'unlimitedToggles' => array( 'p_return_secondary' ), 'unlimitedToggleOn' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'sp_return_primary', $this->return_primary, FALSE, array(), NULL, NULL, NULL, 'sp_return_primary' ) );
|
||||
|
||||
|
||||
$form->addHeader('nexus_subs_display');
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'sp_image', ( ( $this->id AND $this->image ) ? \IPS\File::get( 'nexus_Products', $this->image ) : NULL ), FALSE, array( 'storageExtension' => 'nexus_Products', 'image' => TRUE ), NULL, NULL, NULL, 'sp_image' ) );
|
||||
/*$form->add( new \IPS\Helpers\Form\YesNo( 'sp_featured', $this->featured, FALSE, array(), NULL, NULL, NULL, 'sp_featured' ) );*/
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'sp_desc', NULL, FALSE, array(
|
||||
'app' => 'nexus',
|
||||
'key' => $this->id ? "nexus_subs_{$this->id}_desc" : NULL,
|
||||
'editor' => array(
|
||||
'app' => 'nexus',
|
||||
'key' => 'Admin',
|
||||
'autoSaveKey' => ( $this->id ? "nexus-sub-{$this->id}" : "nexus-new-sub" ),
|
||||
'attachIds' => $this->id ? array( $this->id, NULL, 'sub' ) : NULL, 'minimize' => 'p_desc_placeholder'
|
||||
)
|
||||
), NULL, NULL, NULL, 'p_desc_editor' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Save Add/Edit Form
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return void
|
||||
*/
|
||||
public function saveForm( $values )
|
||||
{
|
||||
if ( !$this->id )
|
||||
{
|
||||
$this->save();
|
||||
unset( static::$multitons[ $this->id ] );
|
||||
|
||||
\IPS\File::claimAttachments( 'nexus-new-sub', $this->id, NULL, 'sub', TRUE );
|
||||
|
||||
$obj = static::load( $this->id );
|
||||
return $obj->saveForm( $obj->formatFormValues( $values ) );
|
||||
}
|
||||
|
||||
$return = parent::saveForm( $values );
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
{
|
||||
if ( !$this->id )
|
||||
{
|
||||
return $values;
|
||||
}
|
||||
|
||||
/* Translatables */
|
||||
foreach ( array( 'name' => '', 'desc' => '_desc' ) as $key => $suffix )
|
||||
{
|
||||
if ( isset( $values[ 'sp_' . $key ] ) )
|
||||
{
|
||||
\IPS\Lang::saveCustom( 'nexus', "nexus_subs_{$this->id}{$suffix}", $values[ 'sp_' . $key ] );
|
||||
}
|
||||
unset( $values[ 'sp_' . $key ] );
|
||||
}
|
||||
|
||||
/* Normalise */
|
||||
if( isset( $values['sp_price'] ) )
|
||||
{
|
||||
$values['sp_price'] = json_encode( $values['sp_price'] );
|
||||
}
|
||||
|
||||
if( isset( $values['sp_primary_group'] ) )
|
||||
{
|
||||
$values['sp_primary_group'] = $values['sp_primary_group'] == '*' ? 0 : $values['sp_primary_group'];
|
||||
}
|
||||
|
||||
if( isset( $values['sp_secondary_group'] ) )
|
||||
{
|
||||
$values['sp_secondary_group'] = $values['sp_secondary_group'] == '*' ? '' : implode( ',', $values['sp_secondary_group'] );
|
||||
}
|
||||
|
||||
if( isset( $values['sp_tax'] ) )
|
||||
{
|
||||
$values['sp_tax'] = $values['sp_tax'] ? $values['sp_tax']->id : 0;
|
||||
}
|
||||
|
||||
if( isset( $values['sp_gateways'] ) )
|
||||
{
|
||||
$values['sp_gateways'] = ( isset( $values['sp_gateways'] ) and is_array( $values['sp_gateways'] ) ) ? implode( ',', array_keys( $values['sp_gateways'] ) ) : '*';
|
||||
}
|
||||
|
||||
/* Renewal options */
|
||||
if( isset( $values['sp_renews'] ) )
|
||||
{
|
||||
if ( $values['sp_renews'] )
|
||||
{
|
||||
$renewOptions = array();
|
||||
$option = $values['sp_renew_options'];
|
||||
$term = $option->getTerm();
|
||||
|
||||
$values['sp_renew_options'] = json_encode( array(
|
||||
'cost' => $option->cost,
|
||||
'term' => $term['term'],
|
||||
'unit' => $term['unit']
|
||||
) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['sp_renew_options'] = '';
|
||||
}
|
||||
|
||||
unset( $values['sp_renews'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['sp_image'] ) )
|
||||
{
|
||||
$values['sp_image'] = (string) $values['sp_image'];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Price
|
||||
*
|
||||
* @param string|NULL $currency Desired currency, or NULL to choose based on member's chosen currency
|
||||
* @return \IPS\nexus\Money|NULL
|
||||
*/
|
||||
public function price( $currency = NULL )
|
||||
{
|
||||
if ( !$currency )
|
||||
{
|
||||
$currency = ( isset( $_SESSION['currency'] ) and in_array( $_SESSION['currency'], \IPS\nexus\Money::currencies() ) ) ? $_SESSION['currency'] : \IPS\nexus\Customer::loggedIn()->defaultCurrency();
|
||||
}
|
||||
|
||||
$costs = json_decode( $this->price, TRUE );
|
||||
|
||||
if ( is_array( $costs ) and isset( $costs[ $currency ]['amount'] ) )
|
||||
{
|
||||
return new \IPS\nexus\Money( $costs[ $currency ]['amount'], $currency );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joining fee
|
||||
*
|
||||
* @param string|NULL $currency Desired currency, or NULL to choose based on member's chosen currency
|
||||
* @return \IPS\nexus\Money|NULL
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
public function renewalTerm( $currency = NULL )
|
||||
{
|
||||
if ( $this->renew_options and $renewal = json_decode( $this->renew_options, TRUE ) )
|
||||
{
|
||||
$renewalPrices = $renewal['cost'];
|
||||
if ( !$currency )
|
||||
{
|
||||
$currency = ( isset( $_SESSION['currency'] ) and in_array( $_SESSION['currency'], \IPS\nexus\Money::currencies() ) ) ? $_SESSION['currency'] : \IPS\nexus\Customer::loggedIn()->defaultCurrency();
|
||||
}
|
||||
|
||||
if ( isset( $renewalPrices[ $currency ] ) )
|
||||
{
|
||||
$grace = NULL;
|
||||
if ( \IPS\Settings::i()->nexus_subs_invoice_grace )
|
||||
{
|
||||
$grace = new \DateInterval( 'P' . \IPS\Settings::i()->nexus_subs_invoice_grace . 'D' );
|
||||
}
|
||||
|
||||
$tax = NULL;
|
||||
if ( $this->tax )
|
||||
{
|
||||
try
|
||||
{
|
||||
$tax = \IPS\nexus\Tax::load( $this->tax );
|
||||
}
|
||||
catch( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalPrices[ $currency ]['amount'], $currency ), new \DateInterval( 'P' . $renewal['term'] . mb_strtoupper( $renewal['unit'] ) ), $tax, FALSE, $grace );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Price Blurb
|
||||
*
|
||||
* @return string|NULL
|
||||
*/
|
||||
public function priceBlurb()
|
||||
{
|
||||
if ( $price = $this->price() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$renewalTerm = $this->renewalTerm( $price->currency );
|
||||
|
||||
if ( $renewalTerm and $renewalTerm->cost->amount == $price->amount )
|
||||
{
|
||||
return $renewalTerm;
|
||||
}
|
||||
else if ( $renewalTerm )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_sub_cost_plus_renewal', FALSE, array( 'sprintf' => array( $price, $renewalTerm ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $price;
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('nexus_sub_cost_unavailable');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('nexus_sub_cost_unavailable');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cost to upgrade to this package (may return negative value for refund)
|
||||
*
|
||||
* @param \IPS\nexus\Subscription\Package $package The currently subscribed package
|
||||
* @param \IPS\Member $member The member.
|
||||
* @return \IPS\nexus\Money|NULL
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function costToUpgrade( \IPS\nexus\Subscription\Package $package, \IPS\Member $member )
|
||||
{
|
||||
/* Fetch purchase */
|
||||
$purchase = NULL;
|
||||
foreach ( \IPS\nexus\extensions\nexus\Item\Subscription::getPurchases( $member, $package->id ) as $row )
|
||||
{
|
||||
if ( $row->active and ! $row->cancelled )
|
||||
{
|
||||
$purchase = $row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $purchase === NULL )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$currency = $purchase->original_invoice->currency;
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$currency = $purchase->member->defaultCurrency();
|
||||
}
|
||||
|
||||
$priceOfExistingPackage = json_decode( $package->price, TRUE );
|
||||
$priceOfExistingPackage = $priceOfExistingPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnOldPackage = json_decode( $package->renew_options, TRUE );
|
||||
|
||||
$priceOfThisPackage = json_decode( $this->price, TRUE );
|
||||
$priceOfThisPackage = $priceOfThisPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnNewPackage = json_decode( $this->renew_options, TRUE );
|
||||
|
||||
if ( $priceOfThisPackage >= $priceOfExistingPackage )
|
||||
{
|
||||
$type = \IPS\Settings::i()->nexus_subs_upgrade;
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = \IPS\Settings::i()->nexus_subs_downgrade;
|
||||
}
|
||||
|
||||
switch ( $type )
|
||||
{
|
||||
case -1:
|
||||
return NULL; /* nope */
|
||||
|
||||
case 0:
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
|
||||
case 1:
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage - $priceOfExistingPackage, $currency );
|
||||
|
||||
case 2:
|
||||
|
||||
if ( !$purchase->renewals )
|
||||
{
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
}
|
||||
if ( !$renewalOptionsOnNewPackage )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
/* What is the closest renewal option on the new package? We'll use that one */
|
||||
$renewalOptionsInDays = array();
|
||||
$term = ( new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'], $currency ), new \DateInterval( 'P' . $renewalOptionsOnNewPackage['term'] . mb_strtoupper( $renewalOptionsOnNewPackage['unit'] ) ), $purchase->renewals->tax ) );
|
||||
$renewalOptionsInDays[ $term->days() ] = $term;
|
||||
|
||||
$closestRenewalOption = null;
|
||||
$numberOfDaysInCurrentRenewalTerm = $purchase->renewals->days();
|
||||
foreach ( $renewalOptionsInDays as $days => $term )
|
||||
{
|
||||
if ( $closestRenewalOption === null or abs( $numberOfDaysInCurrentRenewalTerm - $closestRenewalOption ) > abs( $days - $numberOfDaysInCurrentRenewalTerm ) )
|
||||
{
|
||||
$closestRenewalOption = $days;
|
||||
}
|
||||
}
|
||||
$renewalTermToUse = $renewalOptionsInDays[ $closestRenewalOption ];
|
||||
|
||||
/* What is the difference between our current renewal term and the renewal term we're moving to? */
|
||||
$diff = $purchase->renewals->diff( $renewalTermToUse );
|
||||
|
||||
/* Multiply that by how many days are left */
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( $numberOfDaysInCurrentRenewalTerm );
|
||||
$daysLeftUntilExpiry = new \IPS\Math\Number( (string) ceil( ( $purchase->expire->getTimestamp() - time() ) / 86400 ) );
|
||||
if ( $numberOfDaysInCurrentRenewalTerm->compare( $daysLeftUntilExpiry ) === 0 )
|
||||
{
|
||||
return $diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \IPS\nexus\Money( $diff->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysLeftUntilExpiry ), $currency );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the upgrade/downgrade invoice or refund
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase
|
||||
* @param \IPS\nexus\Subscription\Package $newPackage
|
||||
* @param bool $skipCharge If TRUE, an upgrade charges and downgrade refunds will not be issued
|
||||
* @return \IPS\nexus\Invoice|void An invoice if an upgrade charge has to be paid, or void if not
|
||||
*/
|
||||
public function upgradeDowngrade( \IPS\nexus\Purchase $purchase, \IPS\nexus\Subscription\Package $newPackage, $skipCharge = FALSE )
|
||||
{
|
||||
/* Right, that's all the "I'll tamper with the URLs for a laugh" stuff out of the way... */
|
||||
$costToUpgrade = $newPackage->costToUpgrade( \IPS\nexus\Subscription\Package::load( $purchase->item_id ), $purchase->member );
|
||||
|
||||
/* Charge / Refund */
|
||||
if ( !$skipCharge )
|
||||
{
|
||||
/* Upgrade Charge */
|
||||
if ( $costToUpgrade->amount->isGreaterThanZero() )
|
||||
{
|
||||
$item = new \IPS\nexus\extensions\nexus\Item\SubscriptionUpgrade( sprintf( $purchase->member->language()->get( 'upgrade_charge_item' ), $purchase->member->language()->get( "nexus_subs_{$this->id}" ), $purchase->member->language()->get( "nexus_subs_{$newPackage->id}" ) ), $costToUpgrade );
|
||||
$item->tax = $newPackage->tax ? \IPS\nexus\Tax::load( $newPackage->tax ) : NULL;
|
||||
$item->id = $purchase->id;
|
||||
$item->extra = array( 'newPackage' => $newPackage->id, 'oldPackage' => $this->id );
|
||||
|
||||
if ( $newPackage->gateways and $newPackage->gateways != '*' )
|
||||
{
|
||||
$item->paymentMethodIds = explode( ',', $newPackage->methods );
|
||||
}
|
||||
|
||||
$invoice = new \IPS\nexus\Invoice;
|
||||
$invoice->member = $purchase->member;
|
||||
$invoice->currency = $costToUpgrade->currency;
|
||||
$invoice->addItem( $item );
|
||||
$invoice->return_uri = "app=nexus&module=subscriptons&controller=subscriptons";
|
||||
$invoice->renewal_ids = array( $purchase->id );
|
||||
$invoice->save();
|
||||
return $invoice;
|
||||
}
|
||||
elseif ( !$costToUpgrade->amount->isPositive() )
|
||||
{
|
||||
$credits = $purchase->member->cm_credits;
|
||||
$credits[ $costToUpgrade->currency ]->amount = $credits[ $costToUpgrade->currency ]->amount->add( $costToUpgrade->amount->multiply( new \IPS\Math\Number( '-1' ) ) );
|
||||
$purchase->member->cm_credits = $credits;
|
||||
$purchase->member->save();
|
||||
}
|
||||
}
|
||||
|
||||
/* Work out the new renewal term */
|
||||
$term = NULL;
|
||||
$renewalOptions = json_decode( $newPackage->renew_options, TRUE );
|
||||
$term = $renewalOptions;
|
||||
|
||||
if ( $term )
|
||||
{
|
||||
try
|
||||
{
|
||||
$currency = $purchase->original_invoice->currency;
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
$currency = $purchase->member->defaultCurrency();
|
||||
}
|
||||
$term = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $term['cost'][$currency]['amount'], $currency ), new \DateInterval( 'P' . $term['term'] . mb_strtoupper( $term['unit'] ) ) );
|
||||
}
|
||||
|
||||
/* Remove usergroups */
|
||||
$this->_removeUsergroups( $purchase->member );
|
||||
|
||||
/* If we didn't have an expiry date before, but the new package has a renewal term, set an expiry date */
|
||||
if ( !$purchase->expire and $term )
|
||||
{
|
||||
$purchase->expire = \IPS\DateTime::create()->add( $term->interval );
|
||||
}
|
||||
/* OR if we did have an expiry date, but the new package does not have a renewal term, remove it */
|
||||
elseif ( !$term )
|
||||
{
|
||||
$purchase->expire = NULL;
|
||||
}
|
||||
|
||||
/* Update Purchase */
|
||||
$purchase->name = \IPS\Member::loggedIn()->language()->get( "nexus_subs_{$newPackage->id}" );
|
||||
$purchase->item_id = $newPackage->id;
|
||||
$purchase->renewals = $term;
|
||||
$purchase->save();
|
||||
|
||||
/* Re-add usergroups */
|
||||
$newPackage->_addUsergroups( $purchase->member );
|
||||
|
||||
/* Cancel any pending invoices */
|
||||
if ( $pendingInvoice = $purchase->invoice_pending )
|
||||
{
|
||||
$pendingInvoice->status = \IPS\nexus\invoice::STATUS_CANCELED;
|
||||
$pendingInvoice->save();
|
||||
$purchase->invoice_pending = NULL;
|
||||
$purchase->save();
|
||||
}
|
||||
|
||||
/* Change the subscription itself */
|
||||
try
|
||||
{
|
||||
\IPS\nexus\Subscription::loadActiveByMember( $purchase->member )->changePackage( $newPackage, $purchase->expire );
|
||||
}
|
||||
catch( \Exception $ex )
|
||||
{
|
||||
\IPS\Log::log( "Change Package error (" . $e->getCode() . ") " . $e->getMessage(), 'subscriptions' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a member to the subscription package
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return \IPS\nexus\Subscription The new subscription object added
|
||||
*/
|
||||
public function addMember( $member )
|
||||
{
|
||||
/* Make any previous subscriptions inactive */
|
||||
\IPS\nexus\Subscription::markInactiveByUser( $member );
|
||||
|
||||
$expires = 0;
|
||||
$renews = 0;
|
||||
if ( $this->renew_options and $renewal = json_decode( $this->renew_options, TRUE ) )
|
||||
{
|
||||
$start = \IPS\DateTime::ts( time(), TRUE );
|
||||
$start->add( new \DateInterval( 'P' . $renewal['term'] . mb_strtoupper( $renewal['unit'] ) ) );
|
||||
$expires = $start->getTimeStamp();
|
||||
$renews = 1;
|
||||
}
|
||||
|
||||
/* Create a new one */
|
||||
$sub = new \IPS\nexus\Subscription;
|
||||
$sub->package_id = $this->id;
|
||||
$sub->member_id = $member->member_id;
|
||||
$sub->active = 1;
|
||||
$sub->cancelled = 0;
|
||||
$sub->start = time();
|
||||
$sub->expire = $expires;
|
||||
$sub->renews = $renews;
|
||||
$sub->save();
|
||||
|
||||
$this->_addUsergroups( $member );
|
||||
|
||||
return $sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expires a member
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return void
|
||||
*/
|
||||
public function expireMember( $member )
|
||||
{
|
||||
/* Run before marking it inactive or it won't find the row in _removeUsergroups */
|
||||
$this->_removeUsergroups( $member );
|
||||
|
||||
/* Make any previous subscriptions inactive */
|
||||
\IPS\nexus\Subscription::markInactiveByUser( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a member
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return void
|
||||
*/
|
||||
public function cancelMember( $member)
|
||||
{
|
||||
/* Run before marking it inactive or it won't find the row in _removeUsergroups */
|
||||
$this->_removeUsergroups( $member );
|
||||
|
||||
/* Make any previous subscriptions inactive */
|
||||
\IPS\nexus\Subscription::markInactiveByUser( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a member
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return void
|
||||
*/
|
||||
public function removeMember( $member)
|
||||
{
|
||||
/* Run before marking it inactive or it won't find the row in _removeUsergroups */
|
||||
$this->_removeUsergroups( $member );
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\nexus\Subscription::loadByMemberAndPackage( $member, $this, FALSE )->delete();
|
||||
}
|
||||
catch( \OutOfRangeException $ex ) {}
|
||||
}
|
||||
|
||||
/* !Usergroups */
|
||||
|
||||
/**
|
||||
* Add user groups
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The customer
|
||||
* @return void
|
||||
*/
|
||||
public function _addUsergroups( $member )
|
||||
{
|
||||
$previousGroup = 0;
|
||||
$previousSecondary = '';
|
||||
$newSecondary = '';
|
||||
|
||||
/* Primary Group */
|
||||
if ( $this->primary_group and $this->primary_group != $member->member_group_id and !in_array( $member->member_group_id, explode( ',', \IPS\Settings::i()->nexus_subs_exclude_groups ) ) )
|
||||
{
|
||||
/* Hang on, are we about to boot someone out the ACP? */
|
||||
if ( ! ( $member->isAdmin() and !in_array( $this->primary_group, array_keys( \IPS\Member::administrators()['g'] ) ) ) )
|
||||
{
|
||||
/* Save the current group */
|
||||
$previousGroup = $member->member_group_id;
|
||||
|
||||
/* And update to the new group */
|
||||
$member->member_group_id = $this->primary_group;
|
||||
$member->members_bitoptions['ignore_promotions'] = true;
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousGroup, 'new' => $member->member_group_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Secondary Groups */
|
||||
$secondary = array_filter( explode( ',', $this->secondary_group ), create_function( '$v', 'return (bool) $v;' ) );
|
||||
|
||||
$current_secondary = $member->mgroup_others ? explode( ',', $member->mgroup_others ) : array();
|
||||
$newSecondary = $current_secondary;
|
||||
if ( !empty( $secondary ) )
|
||||
{
|
||||
foreach ( $secondary as $gid )
|
||||
{
|
||||
if ( !in_array( $gid, $newSecondary ) )
|
||||
{
|
||||
$newSecondary[] = $gid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $current_secondary != $newSecondary )
|
||||
{
|
||||
$previousSecondary = $member->mgroup_others;
|
||||
$member->mgroup_others = ',' . implode( ',', $newSecondary ) . ',';
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousSecondary, 'new' => $newSecondary ) );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', array( 'sub_previous_group' => $previousGroup, 'sub_previous_secondary_groups' => $previousSecondary ), array( 'sub_active=1 and sub_package_id=? and sub_member_id=?', $this->id, $member->member_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user groups
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The customer
|
||||
* @return void
|
||||
*/
|
||||
public function _removeUsergroups( $member )
|
||||
{
|
||||
if ( ! $this->return_primary )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fetch purchase */
|
||||
$purchase = NULL;
|
||||
foreach ( \IPS\nexus\extensions\nexus\Item\Subscription::getPurchases( $member, $this->id ) as $row )
|
||||
{
|
||||
/* Don't check for cancelled here, as the purchase will be cancelled before we get here */
|
||||
if ( $row->active )
|
||||
{
|
||||
$purchase = $row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $purchase === NULL )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$sub = \IPS\Db::i()->select( '*', 'nexus_member_subscriptions', array( 'sub_active=1 and sub_package_id=? and sub_member_id=?', $this->id, $member->member_id ) )->first();
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We only want to move them back if they haven't been moved again since */
|
||||
if ( $member->member_group_id == $this->primary_group )
|
||||
{
|
||||
$oldGroup = $member->member_group_id;
|
||||
|
||||
/* Have we made other purchases that have changed their primary group? */
|
||||
try
|
||||
{
|
||||
$next = \IPS\Db::i()->select( array( 'ps_id', 'ps_name', 'p_primary_group' ), 'nexus_purchases', array( 'ps_member=? AND ps_app=? AND ps_type=? AND ps_active=1 AND p_primary_group<>0 AND ps_id<>?', $member->member_id, 'nexus', 'package', $purchase->id ) )
|
||||
->join( 'nexus_packages', 'p_id=ps_item_id' )
|
||||
->first();
|
||||
|
||||
/* Make sure this group exists */
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $next['p_primary_group'] );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
$member->member_group_id = $next['p_primary_group'];
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'purchase', 'action' => 'change', 'remove_id' => $next['ps_id'], 'ps_name' => $next['ps_id'], 'id' => $next['ps_id'], 'name' => $next['ps_name'], 'old' => $oldGroup, 'new' => $member->member_group_id ) );
|
||||
}
|
||||
/* No, move them to their original group */
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
/* Does this group exist? */
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $sub['sub_previous_group'] );
|
||||
$member->member_group_id = $sub['sub_previous_group'];
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
$member->member_group_id = \IPS\Settings::i()->member_group;
|
||||
}
|
||||
|
||||
/* Save */
|
||||
$member->members_bitoptions['ignore_promotions'] = false;
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'subscription', 'action' => 'remove', 'id' => $this->id, 'old' => $oldGroup, 'new' => $member->member_group_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary groups
|
||||
$secondaryGroupsAwardedByThisPurchase = array_unique( array_filter( explode( ',', $this->secondary_group ) ) );
|
||||
$membersSecondaryGroups = $member->mgroup_others ? array_unique( array_filter( explode( ',', $member->mgroup_others ) ) ) : array();
|
||||
if ( isset( $sub['sub_previous_secondary_groups'] ) and $sub['sub_previous_secondary_groups'] !== NULL )
|
||||
{
|
||||
/* Work some stuff out */
|
||||
$currentSecondaryGroups = $membersSecondaryGroups;
|
||||
$membersPreviousSecondaryGroupsBeforeThisPurchase = array_unique( array_filter( explode( ',', $sub['sub_previous_secondary_groups'] ) ) );
|
||||
|
||||
/* Have we made other purchases that have added secondary groups? */
|
||||
$secondaryGroupsAwardedByOtherPurchases = array();
|
||||
foreach ( \IPS\Db::i()->select( 'p_secondary_group', 'nexus_purchases', array( 'ps_member=? AND ps_app=? AND ps_type=? AND ps_active=1 AND p_secondary_group IS NOT NULL AND p_secondary_group<>? AND ps_id<>?', $member->member_id, 'nexus', 'package', '', $purchase->id ) )->join( 'nexus_packages', 'p_id=ps_item_id' ) as $secondaryGroups )
|
||||
{
|
||||
$secondaryGroupsAwardedByOtherPurchases = array_merge( $secondaryGroupsAwardedByOtherPurchases, array_filter( explode( ',', $secondaryGroups ) ) );
|
||||
}
|
||||
$secondaryGroupsAwardedByOtherPurchases = array_unique( $secondaryGroupsAwardedByOtherPurchases );
|
||||
|
||||
/* Loop through */
|
||||
foreach ( $secondaryGroupsAwardedByThisPurchase as $groupId )
|
||||
{
|
||||
/* If we had this group before we made this purchase, we're going to keep it */
|
||||
if ( in_array( $groupId, $membersPreviousSecondaryGroupsBeforeThisPurchase ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we are being awarded this group by a different purchase, we're also going to keep it */
|
||||
if ( in_array( $groupId, $secondaryGroupsAwardedByOtherPurchases ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we're still here, remove it */
|
||||
unset( $membersSecondaryGroups[ array_search( $groupId, $membersSecondaryGroups ) ] );
|
||||
}
|
||||
|
||||
/* And make sure only valid groups are saved */
|
||||
$membersSecondaryGroups = array_filter( $membersSecondaryGroups, function( $group ){
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $group );
|
||||
return TRUE;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
});
|
||||
|
||||
/* Save */
|
||||
$member->mgroup_others = implode( ',', $membersSecondaryGroups );
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'subscription', 'action' => 'remove', 'id' => $this->id, 'old' => $currentSecondaryGroups, 'new' => $membersSecondaryGroups ) );
|
||||
}
|
||||
else if ( $secondaryGroupsAwardedByThisPurchase )
|
||||
{
|
||||
foreach( $membersSecondaryGroups as $group )
|
||||
{
|
||||
if ( in_array( $group, $secondaryGroupsAwardedByThisPurchase ) )
|
||||
{
|
||||
unset( $membersSecondaryGroups[ array_search( $groupId, $membersSecondaryGroups ) ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* And make sure only valid groups are saved */
|
||||
$membersSecondaryGroups = array_filter( $membersSecondaryGroups, function( $group ){
|
||||
try
|
||||
{
|
||||
\IPS\Member\Group::load( $group );
|
||||
return TRUE;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
});
|
||||
|
||||
$member->mgroup_others = implode( ',', $membersSecondaryGroups );
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'subscription', 'action' => 'remove', 'id' => $this->id, 'old' => $currentSecondaryGroups, 'new' => $membersSecondaryGroups ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this package can be converted or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function canConvert( \IPS\nexus\Package $package )
|
||||
{
|
||||
if ( ! $package->physical and ! $package->lkey )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief User subscription model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 12 Feb 2018
|
||||
*/
|
||||
|
||||
namespace IPS\nexus;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* User subscription model
|
||||
*/
|
||||
class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_member_subscriptions';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'sub_';
|
||||
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* Get the DateTime object for when this little subscription doth run out
|
||||
*
|
||||
* @return \IPS\DateTime|NULL
|
||||
*/
|
||||
public function get__expire()
|
||||
{
|
||||
if ( $this->expire )
|
||||
{
|
||||
return \IPS\DateTime::ts( $this->expire );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package (ooh, very mysterious, sounds like something from Narcos)
|
||||
*
|
||||
* @return \IPS\nexus\Subscription\Package or NULL, I don't really care at this point.
|
||||
*/
|
||||
public function get_package()
|
||||
{
|
||||
try
|
||||
{
|
||||
return \IPS\nexus\Subscription\Package::load( $this->package_id );
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the purchase
|
||||
*
|
||||
* @return \IPS\nexus\Subscription\Package or NULL, I don't really care at this point.
|
||||
*/
|
||||
public function get_purchase()
|
||||
{
|
||||
try
|
||||
{
|
||||
return \IPS\nexus\Purchase::load( $this->purchase_id );
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the subscription package
|
||||
*
|
||||
* @param \IPS\nexus\Subscription\Package $package The new package innit
|
||||
* @param \IPS\DateTime|NULL $expires The new expiration date
|
||||
* @return void
|
||||
*/
|
||||
public function changePackage( \IPS\nexus\Subscription\Package $package, $expires=NULL )
|
||||
{
|
||||
$this->package_id = $package->id;
|
||||
$this->expire = ( $expires === NULL ) ? 0 : $expires->getTimeStamp();
|
||||
$this->renews = ! empty( $package->renew_options ) ? 1 : 0;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a nice blurb explaining about the current subscription. If you want, up to you.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function currentBlurb()
|
||||
{
|
||||
if ( $this->expire and $this->renews and ! $this->manually_added )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed_with_expire', NULL, array( 'sprintf' => array( $this->_expire->dayAndMonth() . ' ' . $this->_expire->format('Y') ) ) );
|
||||
}
|
||||
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the package this person is currently subscribed to, or NULL
|
||||
*
|
||||
* @param \IPS\Member|NULL $member Pass a member in if you like, no pressure really up to you
|
||||
* @return \IPS\nexus\Subscription or NULL
|
||||
*/
|
||||
public static function loadActiveByMember( $member=NULL )
|
||||
{
|
||||
$member = $member ? $member : \IPS\Member::loggedIn();
|
||||
|
||||
try
|
||||
{
|
||||
return static::constructFromData( \IPS\Db::i()->select( '*', 'nexus_member_subscriptions', array( 'sub_active=1 and sub_member_id=?', $member->member_id ) )->first() );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a subscription by member and package
|
||||
*
|
||||
* @param \IPS\Member $member Take a guess
|
||||
* @param \IPS\nexus\Subscription\Package $package I mean it's really writing itself
|
||||
* @param boolean $active Only get active packages
|
||||
* @return \IPS\nexus\Subscription
|
||||
* @throws OutOfRangeException
|
||||
*/
|
||||
public static function loadByMemberAndPackage( \IPS\Member $member, \IPS\nexus\Subscription\Package $package, $activeOnly = TRUE )
|
||||
{
|
||||
try
|
||||
{
|
||||
$where = array( array( 'sub_package_id=? and sub_member_id=?', $package->id, $member->member_id ) );
|
||||
|
||||
if ( $activeOnly === TRUE )
|
||||
{
|
||||
$where[] = array( 'sub_active=1' );
|
||||
}
|
||||
|
||||
return static::constructFromData( \IPS\Db::i()->select( '*', 'nexus_member_subscriptions', $where )->first() );
|
||||
}
|
||||
catch( \Exception $ex )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all subscriptions by this member as inactive
|
||||
*
|
||||
* @param \IPS\Member $member I dunno, take a guess
|
||||
* @return void
|
||||
*/
|
||||
public static function markInactiveByUser( \IPS\Member $member )
|
||||
{
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', array( 'sub_active' => 0 ), array( 'sub_member_id=?', $member->member_id ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Subscriptions table
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 09 Feb 2018
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Subscription;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriptions table
|
||||
*/
|
||||
class _Table extends \IPS\Helpers\Table\Table
|
||||
{
|
||||
/**
|
||||
* @brief Sort options
|
||||
*/
|
||||
public $sortOptions = array( 'sp_position' );
|
||||
|
||||
/**
|
||||
* @brief Rows
|
||||
*/
|
||||
protected static $rows = null;
|
||||
|
||||
/**
|
||||
* @brief WHERE clause
|
||||
*/
|
||||
protected $where = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \IPS\Http\Url Base URL
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( \IPS\Http\Url $url=NULL )
|
||||
{
|
||||
/* Init */
|
||||
parent::__construct( $url );
|
||||
|
||||
$this->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'subscription', 'nexus', 'front' ), 'table' );
|
||||
$this->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'subscription', 'nexus', 'front' ), 'rows' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rows
|
||||
*
|
||||
* @param array $advancedSearchValues Values from the advanced search form
|
||||
* @return array
|
||||
*/
|
||||
public function getRows( $advancedSearchValues=NULL )
|
||||
{
|
||||
if ( static::$rows === NULL )
|
||||
{
|
||||
/* Check sortBy */
|
||||
$this->sortBy = in_array( $this->sortBy, $this->sortOptions ) ? $this->sortBy : 'sp_position';
|
||||
|
||||
/* What are we sorting by? */
|
||||
$sortBy = $this->sortBy . ' ' . ( mb_strtolower( $this->sortDirection ) == 'desc' ? 'desc' : 'asc' );
|
||||
|
||||
/* Specify filter in where clause */
|
||||
$where = isset( $this->where ) ? is_array( $this->where ) ? $this->where : array( $this->where ) : array();
|
||||
|
||||
if ( $this->filter and isset( $this->filters[ $this->filter ] ) )
|
||||
{
|
||||
$where[] = is_array( $this->filters[ $this->filter ] ) ? $this->filters[ $this->filter ] : array( $this->filters[ $this->filter ] );
|
||||
}
|
||||
|
||||
$where[] = array( 'sp_enabled=1' );
|
||||
|
||||
/* Get Count */
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*) as cnt', 'nexus_member_subscription_packages', $where )->first();
|
||||
$this->pages = ceil( $count / $this->limit );
|
||||
|
||||
/* Get results */
|
||||
$it = \IPS\Db::i()->select( '*', 'nexus_member_subscription_packages', $where, $sortBy, array( ( $this->limit * ( $this->page - 1 ) ), $this->limit ) );
|
||||
$rows = iterator_to_array( $it );
|
||||
|
||||
foreach( $rows as $index => $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
static::$rows[ $index ] = \IPS\nexus\Subscription\Package::constructFromData( $row );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return static::$rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table headers
|
||||
*
|
||||
* @param array|NULL $advancedSearchValues Advanced search values
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders( $advancedSearchValues )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,20 @@ class _Department extends \IPS\Node\Model
|
||||
{
|
||||
return !( $node instanceof \IPS\nexus\Package\Group );
|
||||
} ) ) );
|
||||
|
||||
if ( \IPS\Settings::i()->nexus_subs_enabled )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'dpt_subscriptions', $this->subscriptions ? array_filter( array_map( function ( $val ) {
|
||||
try
|
||||
{
|
||||
return \IPS\nexus\Subscription\Package::load( $val );
|
||||
} catch ( \OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}, explode( ',', $this->subscriptions ) ) ) : 0, FALSE, array('zeroVal' => 'do_not_associate_sub_requests', 'multiple' => TRUE, 'class' => 'IPS\nexus\Subscription\Package', 'zeroValTogglesOff' => array('dpt_require_package') ) ) );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'dpt_require_package', $this->require_package, FALSE, array(), NULL, NULL, NULL, 'dpt_require_package' ) );
|
||||
|
||||
$form->addHeader( 'department_staff' );
|
||||
@@ -203,7 +217,7 @@ class _Department extends \IPS\Node\Model
|
||||
$admins[ 'dpt_staff_member' ][ 'm' . $row['row_id'] ] = \IPS\Member::load( $row['row_id'] )->name;
|
||||
}
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'dpt_staff', ( $this->id and $this->staff !== '*' ) ? explode( ',', $this->staff ) : '*', TRUE, array( 'options' => $admins, 'multiple' => TRUE, 'unlimited' => '*' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'dpt_staff', ( $this->id and $this->staff !== '*' ) ? explode( ',', $this->staff ) : '*', TRUE, array( 'options' => $admins, 'multiple' => TRUE, 'unlimited' => '*', 'parse' => 'normal' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,6 +269,15 @@ class _Department extends \IPS\Node\Model
|
||||
}, array_keys( $values['dpt_packages'] ) ) ) : 0;
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'dpt_subscriptions', $values ) )
|
||||
{
|
||||
$values['dpt_subscriptions'] = is_array( $values['dpt_subscriptions'] ) ? implode( ',', array_keys( $values['dpt_subscriptions'] ) ) : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['dpt_subscriptions'] = 0;
|
||||
}
|
||||
|
||||
if ( ( array_key_exists( 'dpt_require_package', $values ) AND is_null( $values['dpt_require_package'] ) ) or $values['dpt_packages'] == 0 )
|
||||
{
|
||||
$values['dpt_require_package'] = 0;
|
||||
@@ -393,11 +416,12 @@ class _Department extends \IPS\Node\Model
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->_id,
|
||||
|
||||
@@ -253,6 +253,69 @@ class _Reply extends \IPS\Content\Comment implements \IPS\Content\Hideable
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments based on some arbitrary parameters
|
||||
*
|
||||
* @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 mixed $includeHiddenComments Include hidden comments? NULL to detect if currently logged in member has permission, -1 to return public content only, TRUE to return unapproved content and FALSE to only return unapproved content the viewing member submitted
|
||||
* @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
|
||||
* @return array|NULL|\IPS\Content\Comment If $limit is 1, will return \IPS\Content\Comment or NULL for no results. For any other number, will return an array.
|
||||
*/
|
||||
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenComments=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=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 );
|
||||
}
|
||||
$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, $includeHiddenComments, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send if it's pending
|
||||
*
|
||||
@@ -263,6 +326,7 @@ class _Reply extends \IPS\Content\Comment implements \IPS\Content\Hideable
|
||||
if ( $this->type === static::REPLY_PENDING )
|
||||
{
|
||||
$this->type = static::REPLY_STAFF;
|
||||
$this->hidden = 0;
|
||||
$this->save();
|
||||
|
||||
$defaultRecipients = $this->item()->getDefaultRecipients();
|
||||
|
||||
@@ -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 )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -237,11 +237,12 @@ class _Severity extends \IPS\Node\Model
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->_id,
|
||||
|
||||
@@ -326,11 +326,12 @@ class _Status extends \IPS\Node\Model
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->_id,
|
||||
|
||||
@@ -182,7 +182,7 @@ class _StockAction extends \IPS\Node\Model
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'action_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => ( $this->id ? "nexus_stockaction_{$this->id}" : NULL ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_status', $this->status ?: 0, FALSE, array( 'class' => 'IPS\nexus\Support\Status', 'zeroVal' => 'do_not_change' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_department', $this->department ?: 0, FALSE, array( 'class' => 'IPS\nexus\Support\Department', 'zeroVal' => 'do_not_move' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'action_staff', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'options' => array_merge( array( 0 => 'do_not_change' ), \IPS\nexus\Support\Request::staff() ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'action_staff', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'parse' => 'normal', 'options' => array_merge( array( 0 => 'do_not_change' ), \IPS\nexus\Support\Request::staff() ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'action_message_on', $this->message, FALSE, array( 'togglesOn' => array( 'action_message_editor' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'action_message', $this->message, FALSE, array( 'app' => 'nexus', 'key' => 'Support', 'autoSaveKey' => md5( 'stockaction-' . ( $this->id ?: 'new' ) ) ), NULL, NULL, NULL, 'action_message_editor' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_show_in', ( !$this->show_in or $this->show_in === '*' ) ? 0 : explode( ',', $this->show_in ), FALSE, array( 'class' => 'IPS\nexus\Support\Department', 'zeroVal' => 'all', 'multiple' => TRUE ) ) );
|
||||
|
||||
@@ -160,7 +160,7 @@ class _Stream extends \IPS\Patterns\ActiveRecord
|
||||
public static function customer( \IPS\nexus\Customer $customer )
|
||||
{
|
||||
$return = new static;
|
||||
$return->title = \IPS\Member::loggedIn()->language()->addToStack( 'members_support_requests', FALSE, array( 'sprintf' => array( $customer->cm_name ) ) );
|
||||
$return->title = sprintf( \IPS\Member::loggedIn()->language()->get( 'members_support_requests' ), $customer->cm_name );
|
||||
$return->departments = NULL;
|
||||
$return->statuses = NULL;
|
||||
$return->severities = NULL;
|
||||
@@ -406,7 +406,8 @@ class _Stream extends \IPS\Patterns\ActiveRecord
|
||||
/* Search term */
|
||||
if ( $this->search_term )
|
||||
{
|
||||
$where[] = array( 'r_id IN(?)', \IPS\Db::i()->select( 'reply_request', 'nexus_support_replies', array( "MATCH(reply_post) AGAINST (? IN BOOLEAN MODE)", $this->search_term ) ) );
|
||||
$subQuery= \IPS\Content\Search\Mysql\Query::matchClause('reply_post', $this->search_term);
|
||||
$where[] = array( 'r_id IN(?)', \IPS\Db::i()->select( 'reply_request', 'nexus_support_replies', $subQuery ) );
|
||||
}
|
||||
|
||||
/* Tracked? */
|
||||
@@ -451,15 +452,8 @@ class _Stream extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( mb_strpos( $order, 'dpt_position' ) !== FALSE )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->select( 'staff_id', 'nexus_support_staff_dpt_order', array( 'staff_id=?', $member->member_id ), NULL, 1 )->first();
|
||||
$query->join( 'nexus_support_staff_dpt_order', 'nexus_support_staff_dpt_order.department_id=r_department AND nexus_support_staff_dpt_order.staff_id=' . $member->member_id );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$query->join( 'nexus_support_departments', 'dpt_id=r_department' );
|
||||
}
|
||||
$query->join( 'nexus_support_staff_dpt_order', 'nexus_support_staff_dpt_order.department_id=r_department AND nexus_support_staff_dpt_order.staff_id=' . $member->member_id );
|
||||
$query->join( 'nexus_support_departments', 'dpt_id=r_department' );
|
||||
}
|
||||
if ( mb_strpos( $order, 'sev_position' ) !== FALSE )
|
||||
{
|
||||
|
||||
@@ -223,11 +223,12 @@ class _Tax extends \IPS\Node\Model
|
||||
/**
|
||||
* 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 name Name
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
|
||||
@@ -32,6 +32,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
const STATUS_REFUNDED = 'rfnd'; // Transaction has been refunded in full
|
||||
const STATUS_PART_REFUNDED = 'prfd'; // Transaction has been partially refunded
|
||||
const STATUS_GATEWAY_PENDING= 'gwpd'; // The gateway is processing the transaction
|
||||
const STATUS_DISPUTED = 'dspd'; // The customer disputed the transaction with their bank (filed a chargeback)
|
||||
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
@@ -89,7 +90,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
* Get transaction table
|
||||
*
|
||||
* @param array $where Where clause
|
||||
* @param string $ref Referer
|
||||
* @param string $ref Referrer
|
||||
* @return \IPS\Helpers\Table\Db
|
||||
*/
|
||||
public static function table( $where = array(), \IPS\Http\Url $url, $ref = 't' )
|
||||
@@ -99,6 +100,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
$table->sortBy = $table->sortBy ?: 't_date';
|
||||
|
||||
/* Format Columns */
|
||||
$table->rowClasses = array( 't_invoice' => array( 'ipsTable_wrap' ) );
|
||||
$table->include = array( 't_status', 't_id', 't_method', 't_member', 't_amount', 't_invoice', 't_date' );
|
||||
$table->parsers = array(
|
||||
't_status' => function( $val )
|
||||
@@ -398,7 +400,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function set_partial_refund( \IPS\nexus\Money $amount )
|
||||
{
|
||||
$this->_data['partial_refund'] = $amount->amount;
|
||||
$this->_data['partial_refund'] = (string) $amount->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -496,8 +498,8 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? No fraud rule matches so we can proceed with the transaction */
|
||||
return static::STATUS_PAID;
|
||||
/* Check gateway */
|
||||
return $this->method->fraudCheck( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +606,10 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
* @return \IPS\Member|NULL If the invoice belonged to a guest, a member will be created by approving and returned here
|
||||
*/
|
||||
public function approve( $by = NULL )
|
||||
{
|
||||
{
|
||||
/* Get the amount to pay before storing this as a paid transaction */
|
||||
$amountToPayOnInvoice = $this->invoice->amountToPay();
|
||||
|
||||
/* Set the transaction as paid */
|
||||
$this->status = static::STATUS_PAID;
|
||||
$extra = $this->extra;
|
||||
@@ -620,7 +625,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
$this->save();
|
||||
|
||||
/* Mark the invoice paid if necessary */
|
||||
if ( !$this->invoice->amountToPay()->amount->isGreaterThanZero() )
|
||||
if ( !$amountToPayOnInvoice->amount->subtract( $this->amount->amount )->isGreaterThanZero() )
|
||||
{
|
||||
return $this->invoice->markPaid();
|
||||
}
|
||||
@@ -661,10 +666,11 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @param string $refundMethod "gateway", "credit", or "none"
|
||||
* @param float|NULL $amount Amount (NULL for full amount)
|
||||
* @param string $reason Reason for refund, if applicable (provided by gateway's refundReasons())
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function refund( $refundMethod='gateway', $amount=NULL )
|
||||
public function refund( $refundMethod='gateway', $amount=NULL, $reason=NULL )
|
||||
{
|
||||
/* What's the amount? */
|
||||
if ( $amount )
|
||||
@@ -683,7 +689,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
$refundReference = NULL;
|
||||
if ( $refundMethod === 'gateway' and method_exists( $this->method, 'refund' ) )
|
||||
{
|
||||
$refundReference = $this->method->refund( $this, $amount );
|
||||
$refundReference = $this->method->refund( $this, $amount, $reason );
|
||||
}
|
||||
elseif ( $refundMethod === 'credit' )
|
||||
{
|
||||
@@ -728,7 +734,8 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->partial_refund->amount = $this->partial_refund->amount->add( $amount );
|
||||
$this->partial_refund = new \IPS\nexus\Money( $this->partial_refund->amount->add( $amount ), $this->currency );
|
||||
|
||||
$extra['history'][] = array( 's' => static::STATUS_PART_REFUNDED, 'by' => \IPS\Member::loggedIn()->member_id, 'on' => time(), 'to' => $refundMethod, 'amount' => $amount, 'ref' => $refundReference );
|
||||
if ( $amount >= $this->amount->amount )
|
||||
{
|
||||
@@ -830,7 +837,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if( $this->_url === NULL )
|
||||
{
|
||||
$this->_url = \IPS\Http\Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id={$this->invoice->id}&t={$this->id}", 'front', 'nexus_checkout', \IPS\Settings::i()->nexus_https );
|
||||
$this->_url = \IPS\Http\Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id={$this->invoice->id}&t={$this->id}", 'front', 'nexus_checkout' );
|
||||
}
|
||||
|
||||
return $this->_url;
|
||||
@@ -858,7 +865,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
$return = array();
|
||||
|
||||
/* Approve button */
|
||||
if ( in_array( $this->status, array( static::STATUS_PENDING, static::STATUS_WAITING, static::STATUS_HELD, static::STATUS_REVIEW, static::STATUS_GATEWAY_PENDING ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
if ( in_array( $this->status, array( static::STATUS_WAITING, static::STATUS_HELD, static::STATUS_REVIEW ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
{
|
||||
$return['approve'] = array(
|
||||
'title' => $this->auth ? 'transaction_capture' : 'transaction_approve',
|
||||
@@ -869,7 +876,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/* Review button */
|
||||
if ( in_array( $this->status, array( static::STATUS_PENDING, static::STATUS_WAITING, static::STATUS_HELD ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
if ( in_array( $this->status, array( static::STATUS_WAITING, static::STATUS_HELD ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
{
|
||||
$return['review'] = array(
|
||||
'title' => 'transaction_flag_review',
|
||||
@@ -879,7 +886,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/* Void button */
|
||||
if ( ( in_array( $this->status, array( static::STATUS_PENDING, static::STATUS_GATEWAY_PENDING ) ) or ( $this->auth and in_array( $this->status, array( static::STATUS_HELD, static::STATUS_REVIEW ) ) ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
if ( $this->auth and in_array( $this->status, array( static::STATUS_HELD, static::STATUS_REVIEW ) ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
{
|
||||
$return['void'] = array(
|
||||
'title' => 'transaction_void',
|
||||
@@ -889,7 +896,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
);
|
||||
}
|
||||
|
||||
/* Cancal button for manual */
|
||||
/* Cancel button for manual */
|
||||
elseif ( $this->status === static::STATUS_WAITING and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_edit' ) )
|
||||
{
|
||||
$return['void'] = array(
|
||||
@@ -957,29 +964,30 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* 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 status Status: 'okay' = Paid; 'pend' = Pending, waiting for gateway; 'wait' = Pending, manual approval required; 'hold' = Held for manual approval; 'revw' = Flagged for review; 'fail' = Failed; 'rfnd' = Refunded; 'prfd' = Partially refunded
|
||||
* @apiresponse int invoiceId Invoice ID Number
|
||||
* @apiresponse \IPS\nexus\Money amount Amount
|
||||
* @apiresponse \IPS\nexus\Money refundAmount If partially refunded, the amount that has been refunded
|
||||
* @apiresponse \IPS\nexus\Gateway gateway The gateway
|
||||
* @apiresponse string gatewayId Any ID number provided by the gateway to identify the transaction on their end
|
||||
* @apiresponse datetime date Date
|
||||
* @apiresponse \IPS\nexus\Customer customer Customer
|
||||
* @apiresponse int id ID number
|
||||
* @apiresponse string status Status: 'okay' = Paid; 'pend' = Pending, waiting for gateway; 'wait' = Pending, manual approval required; 'hold' = Held for manual approval; 'revw' = Flagged for review; 'fail' = Failed; 'rfnd' = Refunded; 'prfd' = Partially refunded
|
||||
* @apiresponse int invoiceId Invoice ID Number
|
||||
* @apiresponse \IPS\nexus\Money amount Amount
|
||||
* @apiresponse \IPS\nexus\Money refundAmount If partially refunded, the amount that has been refunded
|
||||
* @apiresponse \IPS\nexus\Gateway gateway The gateway
|
||||
* @clientapiresponse string gatewayId Any ID number provided by the gateway to identify the transaction on their end
|
||||
* @apiresponse datetime date Date
|
||||
* @apiresponse \IPS\nexus\Customer customer Customer
|
||||
*/
|
||||
public function apiOutput()
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'status' => $this->status,
|
||||
'invoiceId' => $this->invoice->id,
|
||||
'amount' => $this->amount->apiOutput(),
|
||||
'refundAmount' => $this->partial_refund ? $this->partial_refund->apiOutput() : null,
|
||||
'gateway' => $this->method->apiOutput(),
|
||||
'amount' => $this->amount->apiOutput( $authorizedMember ),
|
||||
'refundAmount' => $this->partial_refund ? $this->partial_refund->apiOutput( $authorizedMember ) : null,
|
||||
'gateway' => $this->method ? $this->method->apiOutput( $authorizedMember ) : null,
|
||||
'gatewayId' => $this->gw_id,
|
||||
'date' => $this->date->rfc3339(),
|
||||
'customer' => $this->member->apiOutput()
|
||||
'customer' => $this->member->apiOutput( $authorizedMember )
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user