Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

+97 -139
View File
@@ -12,70 +12,71 @@
namespace IPS\nexus\Package;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use IPS\Db;
use IPS\Dispatcher;
use IPS\Helpers\Form\Number;
use IPS\Helpers\Form\Radio;
use IPS\Helpers\Form\Select;
use IPS\Helpers\Form\YesNo;
use IPS\IPS;
use IPS\Member;
use IPS\nexus\Invoice;
use IPS\nexus\Purchase;
use IPS\nexus\Purchase\LicenseKey;
use IPS\nexus\Package as NexusPackage;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use OutOfRangeException;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Product Package
*/
class _Product extends \IPS\nexus\Package
class Product extends NexusPackage
{
/**
* @brief Database Table
*/
protected static $packageDatabaseTable = 'nexus_packages_products';
protected static string $packageDatabaseTable = 'nexus_packages_products';
/**
* @brief Which columns belong to the local table
*/
protected static $packageDatabaseColumns = array( 'p_physical', 'p_subscription', 'p_shipping', 'p_weight', 'p_lkey', 'p_lkey_identifier', 'p_lkey_uses', 'p_show', 'p_length', 'p_width', 'p_height' );
protected static array $packageDatabaseColumns = array( 'p_subscription', 'p_lkey', 'p_lkey_identifier', 'p_lkey_uses', 'p_show' );
/**
* ACP Fields
*
* @param \IPS\nexus\Package $package The package
* @param bool $custom If TRUE, is for a custom package
* @param bool $customEdit If TRUE, is editing a custom package
* @return array
* @param NexusPackage $package The package
* @param bool $custom If TRUE, is for a custom package
* @param bool $customEdit If TRUE, is editing a custom package
* @return array
*/
public static function acpFormFields( \IPS\nexus\Package $package, $custom=FALSE, $customEdit=FALSE )
public static function acpFormFields(NexusPackage $package, bool $custom=FALSE, bool $customEdit=FALSE ): array
{
$return = array();
$formId = $package->id ? "form_{$package->id}" : 'form_new';
if ( !$customEdit ) // After the package has been created, these are unimportant
{
$return['package_settings']['physical'] = new \IPS\Helpers\Form\YesNo( 'p_physical', $package->type === 'product' ? $package->physical : FALSE, FALSE, array( 'disableCopy' => TRUE, 'togglesOn' => array( 'p_weight', 'p_length', 'p_width', 'p_height', 'p_shipping' ), 'togglesOff' => array( 'elProductTaxWarningContainer' ) ) );
$return['package_settings']['weight'] = new \IPS\nexus\Form\Weight( 'p_weight', $package->type === 'product' ? new \IPS\nexus\Shipping\Weight( $package->weight ) : NULL, FALSE, array( 'disableCopy' => TRUE ), NULL, NULL, NULL, 'p_weight' );
$return['package_settings']['length'] = new \IPS\nexus\Form\Length( 'p_length', $package->type === 'product' ? new \IPS\nexus\Shipping\Length( $package->length ) : NULL, FALSE, array( 'disableCopy' => TRUE ), NULL, NULL, NULL, 'p_length' );
$return['package_settings']['width'] = new \IPS\nexus\Form\Length( 'p_width', $package->type === 'product' ? new \IPS\nexus\Shipping\Length( $package->width ) : NULL, FALSE, array( 'disableCopy' => TRUE ), NULL, NULL, NULL, 'p_width' );
$return['package_settings']['height'] = new \IPS\nexus\Form\Length( 'p_height', $package->type === 'product' ? new \IPS\nexus\Shipping\Length( $package->height ) : NULL, FALSE, array( 'disableCopy' => TRUE ), NULL, NULL, NULL, 'p_height' );
$availableShippingMethods = array();
foreach ( \IPS\nexus\Shipping\FlatRate::roots() as $rate )
{
$availableShippingMethods[ $rate->_id ] = $rate->_title;
}
if ( \IPS\Settings::i()->easypost_api_key and \IPS\Settings::i()->easypost_show_rates )
{
$availableShippingMethods['easypost'] = 'enhancements__nexus_EasyPost';
}
$return['package_settings']['shipping'] = new \IPS\Helpers\Form\CheckboxSet( 'p_shipping', ( $package->type === 'product' and $package->shipping !== '*' ) ? explode( ',', $package->shipping ) : '*', FALSE, array( 'options' => $availableShippingMethods, 'multiple' => TRUE, 'unlimited' => '*', 'impliedUnlimited' => TRUE ), NULL, NULL, NULL, 'p_shipping' );
}
$return['package_settings']['show'] = new \IPS\Helpers\Form\YesNo( 'p_show', $package->type === 'product' ? $package->show : TRUE, FALSE, array( 'togglesOn' => array( "{$formId}_tab_package_client_area", "{$formId}_header_package_associations", "{$formId}_header_package_associations_desc", 'p_associate', "{$formId}_header_package_renewals", 'p_renews', 'p_support_severity', 'p_lkey' ) ) );
$return['package_settings']['show'] = new YesNo( 'p_show', $package->type === 'product' ? $package->show : TRUE, FALSE, array( 'togglesOn' => array( "{$formId}_tab_package_client_area", "{$formId}_header_package_associations", "{$formId}_header_package_associations_desc", 'p_associate', "{$formId}_header_package_renewals", 'p_renews', 'p_lkey' ) ) );
if ( !$custom )
{
$return['store_permissions']['subscription'] = new \IPS\Helpers\Form\YesNo( 'p_subscription', $package->type === 'product' ? !$package->subscription : TRUE );
$return['store_permissions']['subscription'] = new YesNo( 'p_subscription', $package->type === 'product' ? !$package->subscription : TRUE );
}
$licenseKeyOptions = array();
$licenseKeyToggles = array();
foreach ( \IPS\nexus\Purchase\LicenseKey::licenseKeyTypes() as $key => $class )
foreach ( LicenseKey::licenseKeyTypes() as $key => $class )
{
$licenseKeyOptions[ mb_strtolower( $key ) ] = 'lkey_' . $key;
$licenseKeyToggles[ mb_strtolower( $key ) ] = array( 'p_lkey_identifier', 'p_lkey_uses' );
@@ -83,10 +84,10 @@ class _Product extends \IPS\nexus\Package
if ( !empty( $licenseKeyOptions ) )
{
array_unshift( $licenseKeyOptions, 'lkey_none' );
$return['package_benefits']['lkey'] = new \IPS\Helpers\Form\Radio( 'p_lkey', $package->type === 'product' ? $package->lkey : 0, FALSE, array( 'options' => $licenseKeyOptions, 'toggles' => $licenseKeyToggles ), NULL, NULL, NULL, 'p_lkey' );
$return['package_benefits']['lkey'] = new Radio( 'p_lkey', $package->type === 'product' ? $package->lkey : 0, FALSE, array( 'options' => $licenseKeyOptions, 'toggles' => $licenseKeyToggles ), NULL, NULL, NULL, 'p_lkey' );
}
$return['package_benefits']['lkey_uses'] = new \IPS\Helpers\Form\Number( 'p_lkey_uses', $package->type === 'product' ? $package->lkey_uses : -1, FALSE, array( 'unlimited' => -1 ) );
$return['package_benefits']['lkey_uses'] = new Number( 'p_lkey_uses', $package->type === 'product' ? $package->lkey_uses : -1, FALSE, array( 'unlimited' => -1 ) );
$identifierOptions = array(
'name' => 'lkey_identifier_name',
@@ -95,13 +96,13 @@ class _Product extends \IPS\nexus\Package
);
if ( $package->id )
{
foreach ( \IPS\nexus\Package\CustomField::roots( NULL, NULL, array( array( \IPS\Db::i()->findInSet( 'cf_packages', array( $package->id ) ) ) ) ) as $field )
foreach (CustomField::roots( NULL, NULL, array( array( Db::i()->findInSet( 'cf_packages', array( $package->id ) ) ) ) ) as $field )
{
$identifierOptions[ $field->id ] = $field->_title;
}
}
$return['package_benefits']['lkey_identifier'] = new \IPS\Helpers\Form\Select( 'p_lkey_identifier', $package->type === 'product' ? $package->lkey_identifier : '0', FALSE, array( 'options' => $identifierOptions, 'unlimited' => '0', 'unlimitedLang' => 'lkey_identifier_none' ), NULL, NULL, NULL, 'p_lkey_identifier' );
$return['package_benefits']['lkey_identifier'] = new Select( 'p_lkey_identifier', $package->type === 'product' ? $package->lkey_identifier : '0', FALSE, array( 'options' => $identifierOptions, 'unlimited' => '0', 'unlimitedLang' => 'lkey_identifier_none' ), NULL, NULL, NULL, 'p_lkey_identifier' );
return $return;
}
@@ -112,53 +113,9 @@ class _Product extends \IPS\nexus\Package
* @param array $values Values from the form
* @return array
*/
public function formatFormValues( $values )
{
if( isset( $values['p_subscription'] ) )
{
$values['p_subscription'] = isset( $values['p_subscription'] ) ? !$values['p_subscription'] : FALSE;
}
if( isset( $values['p_weight'] ) )
{
$values['p_weight'] = \is_object( $values['p_weight'] ) ? $values['p_weight']->kilograms : 0;
}
else
{
$values['p_weight'] = 0;
}
if( isset( $values['p_length'] ) )
{
$values['p_length'] = \is_object( $values['p_length'] ) ? $values['p_length']->metres : 0;
}
else
{
$values['p_length'] = 0;
}
if( isset( $values['p_width'] ) )
{
$values['p_width'] = \is_object( $values['p_width'] ) ? $values['p_width']->metres : 0;
}
else
{
$values['p_width'] = 0;
}
if( isset( $values['p_height'] ) )
{
$values['p_height'] = \is_object( $values['p_height'] ) ? $values['p_height']->metres : 0;
}
else
{
$values['p_height'] = 0;
}
if( isset( $values['p_shipping'] ) )
{
$values['p_shipping'] = \is_array( $values['p_shipping'] ) ? implode( ',', $values['p_shipping'] ) : '*';
}
public function formatFormValues( array $values ): array
{
$values['p_subscription'] = isset( $values['p_subscription'] ) ? !$values['p_subscription'] : FALSE;
return parent::formatFormValues( $values );
}
@@ -168,7 +125,7 @@ class _Product extends \IPS\nexus\Package
*
* @return array
*/
public static function updateableFields()
public static function updateableFields() : array
{
return array_merge( parent::updateableFields(), array(
'lkey',
@@ -181,17 +138,15 @@ class _Product extends \IPS\nexus\Package
/**
* Update existing purchases
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param array $changes The old values
* @param bool $cancelBillingAgreementIfNecessary If making changes to renewal terms, TRUE will cancel associated billing agreements. FALSE will skip that change
* @return void
* @param Purchase $purchase The purchase
* @param array $changes The old values
* @param bool $cancelBillingAgreementIfNecessary If making changes to renewal terms, TRUE will cancel associated billing agreements. FALSE will skip that change
* @return void
*/
public function updatePurchase( \IPS\nexus\Purchase $purchase, $changes, $cancelBillingAgreementIfNecessary=FALSE )
public function updatePurchase(Purchase $purchase, array $changes, bool $cancelBillingAgreementIfNecessary=FALSE ): void
{
if ( array_key_exists( 'lkey', $changes ) )
{
$oldKey = NULL;
$lKey = $purchase->licenseKey();
if ( $lKey )
@@ -199,11 +154,11 @@ class _Product extends \IPS\nexus\Package
$lKey->delete();
}
$licenseTypes = \IPS\nexus\Purchase\LicenseKey::licenseKeyTypes();
$licenseTypes = LicenseKey::licenseKeyTypes();
if( class_exists( $licenseTypes[ mb_ucfirst( $this->lkey ) ]) )
if( class_exists( $licenseTypes[ IPS::mb_ucfirst( $this->lkey ) ]) )
{
$class = $licenseTypes[ mb_ucfirst( $this->lkey ) ];
$class = $licenseTypes[ IPS::mb_ucfirst( $this->lkey ) ];
$licenseKey = new $class;
$licenseKey->identifier = $this->lkey_identifier;
$licenseKey->purchase = $purchase;
@@ -230,7 +185,7 @@ class _Product extends \IPS\nexus\Package
$purchase->save();
}
return parent::updatePurchase( $purchase, $changes, $cancelBillingAgreementIfNecessary );
parent::updatePurchase( $purchase, $changes, $cancelBillingAgreementIfNecessary );
}
/* !Actions */
@@ -238,23 +193,23 @@ class _Product extends \IPS\nexus\Package
/**
* Add To Cart
*
* @param \IPS\nexus\extensions\nexus\Item\Package $item The item
* @param \IPS\nexus\Invoice\Item $item The item
* @param array $values Values from form
* @param string $memberCurrency The currency being used
* @return array Additional items to add
* @param string $memberCurrency The currency being used
* @return array Additional items to add
*/
public function addToCart( \IPS\nexus\extensions\nexus\Item\Package $item, array $values, $memberCurrency )
public function addToCart(\IPS\nexus\Invoice\Item $item, array $values, string $memberCurrency ): array
{
if ( $this->subscription )
{
if ( $item->quantity > 1 )
{
\IPS\Output::i()->error( 'err_subscription_qty', '1X247/2', 403, '' );
Output::i()->error( 'err_subscription_qty', '1X247/2', 403, '' );
}
if ( $this->_memberHasPurchasedSubscription( \IPS\Member::loggedIn() ) )
if ( $this->_memberHasPurchasedSubscription( Member::loggedIn() ) )
{
\IPS\Output::i()->error( 'err_subscription_bought', '1X247/1', 403, '' );
Output::i()->error( 'err_subscription_bought', '1X247/1', 403, '' );
}
if ( isset( $_SESSION['cart'] ) )
@@ -263,7 +218,7 @@ class _Product extends \IPS\nexus\Package
{
if ( $_item->id === $this->id )
{
\IPS\Output::i()->error( 'err_subscription_in_cart', '1X247/3', 403, '' );
Output::i()->error( 'err_subscription_in_cart', '1X247/3', 403, '' );
}
}
}
@@ -278,31 +233,31 @@ class _Product extends \IPS\nexus\Package
* is ran to check the items they are purchasing can be bought.
* Is expected to throw a DomainException with an error message to display to the user if not valid
*
* @param \IPS\Member $member The new member
* @return void
* @throws \DomainException
* @param Member $member The new member
* @return void
* @throws DomainException
*/
public function memberCanPurchase( \IPS\Member $member )
public function memberCanPurchase( Member $member ): void
{
if ( $this->subscription and $this->_memberHasPurchasedSubscription( $member ) )
{
throw new \DomainException( $member->language()->addToStack( 'err_subscription_bought_login', FALSE, array( 'sprintf' => array( $member->language()->addToStack( "nexus_package_{$this->id}" ) ) ) ) );
throw new DomainException( $member->language()->addToStack( 'err_subscription_bought_login', FALSE, array( 'sprintf' => array( $member->language()->addToStack( "nexus_package_{$this->id}" ) ) ) ) );
}
if ( ! ( $this->member_groups == "*" or !empty( ( array_intersect( explode( ",", $this->member_groups ), $member->groups ) ) ) ) )
{
throw new \DomainException( $member->language()->addToStack( 'err_group_cant_purchase', FALSE, array( 'sprintf' => array( $member->language()->addToStack( "nexus_package_{$this->id}" ) ) ) ) );
throw new DomainException( $member->language()->addToStack( 'err_group_cant_purchase', FALSE, array( 'sprintf' => array( $member->language()->addToStack( "nexus_package_{$this->id}" ) ) ) ) );
}
}
/**
* Check if a member has purchased this subscription product
*
* @param \IPS\Member $member The new member
* @param Member $member The new member
* @return bool
*/
protected function _memberHasPurchasedSubscription( \IPS\Member $member )
protected function _memberHasPurchasedSubscription( Member $member ) : bool
{
return (bool) \IPS\Db::i()->select( 'COUNT(*)', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=? AND ps_cancelled=0 AND ps_member=?', 'nexus', 'package', $this->id, $member->member_id ) )->first();
return (bool) Db::i()->select( 'COUNT(*)', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=? AND ps_cancelled=0 AND ps_member=?', 'nexus', 'package', $this->id, $member->member_id ) )->first();
}
/**
@@ -310,7 +265,7 @@ class _Product extends \IPS\nexus\Package
*
* @return bool
*/
public function showPurchaseRecord()
public function showPurchaseRecord(): bool
{
return $this->show;
}
@@ -318,36 +273,39 @@ class _Product extends \IPS\nexus\Package
/**
* Get ACP Page HTML
*
* @return string
* @param Purchase $purchase
* @return string
*/
public function acpPage( \IPS\nexus\Purchase $purchase )
public function acpPage( Purchase $purchase ): string
{
if ( $this->lkey and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'customers', 'lkeys_view' ) )
if ( $this->lkey and Member::loggedIn()->hasAcpRestriction( 'nexus', 'customers', 'lkeys_view' ) )
{
if ( $lkey = $purchase->licenseKey() )
{
return \IPS\Theme::i()->getTemplate('purchases')->lkey( $lkey );
return Theme::i()->getTemplate('purchases')->lkey( $lkey );
}
else
{
return \IPS\Theme::i()->getTemplate('purchases')->noLkey( $purchase );
return Theme::i()->getTemplate('purchases')->noLkey( $purchase );
}
}
return '';
}
/**
* ACP Action
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param Purchase $purchase The purchase
* @return string|null
*/
public function acpAction( \IPS\nexus\Purchase $purchase )
public function acpAction( Purchase $purchase ): string|null
{
switch ( \IPS\Request::i()->act )
switch ( Request::i()->act )
{
case 'lkeyReset':
\IPS\Dispatcher::i()->checkAcpPermission( 'lkeys_reset' );
\IPS\Session::i()->csrfCheck();
Dispatcher::i()->checkAcpPermission( 'lkeys_reset' );
Session::i()->csrfCheck();
$oldKey = NULL;
try
@@ -358,13 +316,13 @@ class _Product extends \IPS\nexus\Package
$old->delete();
}
}
catch ( \OutOfRangeException $e ) { }
catch ( OutOfRangeException ) { }
/* Invalidate License Key Cache so old data is not loaded */
$purchase->licenseKey = NULL;
$licenseTypes = \IPS\nexus\Purchase\LicenseKey::licenseKeyTypes();
$class = $licenseTypes[ mb_ucfirst( $this->lkey ) ];
$licenseTypes = LicenseKey::licenseKeyTypes();
$class = $licenseTypes[ IPS::mb_ucfirst( $this->lkey ) ];
$licenseKey = new $class;
$licenseKey->identifier = $this->lkey_identifier;
$licenseKey->purchase = $purchase;
@@ -372,7 +330,7 @@ class _Product extends \IPS\nexus\Package
$licenseKey->save();
$purchase->member->log( 'lkey', array( 'type' => 'reset', 'key' => $oldKey, 'new' => $licenseKey->key, 'ps_id' => $purchase->id, 'ps_name' => $purchase->name ) );
break;
return null;
default:
return parent::acpAction( $purchase );
@@ -382,16 +340,16 @@ class _Product extends \IPS\nexus\Package
/**
* On Purchase Generated
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param \IPS\nexus\Invoice $invoice The invoice
* @return void
* @param Purchase $purchase The purchase
* @param Invoice $invoice The invoice
* @return void
*/
public function onPurchaseGenerated( \IPS\nexus\Purchase $purchase, \IPS\nexus\Invoice $invoice )
public function onPurchaseGenerated(Purchase $purchase, Invoice $invoice ): void
{
if ( $this->lkey )
{
$licenseTypes = \IPS\nexus\Purchase\LicenseKey::licenseKeyTypes();
$class = $licenseTypes[ mb_ucfirst( $this->lkey ) ];
$licenseTypes = LicenseKey::licenseKeyTypes();
$class = $licenseTypes[ IPS::mb_ucfirst( $this->lkey ) ];
$licenseKey = new $class;
$licenseKey->identifier = $this->lkey_identifier;
$licenseKey->purchase = $purchase;
@@ -405,10 +363,10 @@ class _Product extends \IPS\nexus\Package
/**
* Warning to display to admin when cancelling a purchase
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param Purchase $purchase The purchase
* @return string|null
*/
public function onCancelWarning( \IPS\nexus\Purchase $purchase )
public function onCancelWarning( Purchase $purchase ): string|null
{
return NULL;
}