Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
@@ -12,56 +12,100 @@
|
||||
namespace IPS\nexus\Package;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use BadMethodCallException;
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use IPS\Application;
|
||||
use IPS\Content\Embeddable;
|
||||
use IPS\Content\Featurable;
|
||||
use IPS\Content\Filter;
|
||||
use IPS\Content\Hideable;
|
||||
use IPS\Content\Item as ContentItem;
|
||||
use IPS\Content\MetaData;
|
||||
use IPS\Content\Shareable;
|
||||
use IPS\Db;
|
||||
use IPS\File;
|
||||
use IPS\File\Iterator;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Member;
|
||||
use IPS\nexus\Customer;
|
||||
use IPS\nexus\Money;
|
||||
use IPS\nexus\Package;
|
||||
use IPS\nexus\Purchase\RenewalTerm;
|
||||
use IPS\nexus\Tax;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Output;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Request;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package Item Model
|
||||
*/
|
||||
class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\Content\Shareable, \IPS\Content\Embeddable, \IPS\Content\MetaData, \IPS\Content\Searchable
|
||||
class Item extends ContentItem implements Embeddable
|
||||
{
|
||||
use MetaData,
|
||||
Shareable,
|
||||
Hideable,
|
||||
Featurable
|
||||
{
|
||||
Hideable::moderateNewReviews as public _moderateNewReviews;
|
||||
Hideable::hiddenStatus as public _hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'nexus';
|
||||
public static string $application = 'nexus';
|
||||
|
||||
/**
|
||||
* @brief Module
|
||||
*/
|
||||
public static $module = 'store';
|
||||
public static string $module = 'store';
|
||||
|
||||
/**
|
||||
* @brief Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_packages';
|
||||
public static ?string $databaseTable = 'nexus_packages';
|
||||
|
||||
/**
|
||||
* @brief Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'p_';
|
||||
public static string $databasePrefix = 'p_';
|
||||
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief Node Class
|
||||
*/
|
||||
public static $containerNodeClass = 'IPS\nexus\Package\Group';
|
||||
public static ?string $containerNodeClass = 'IPS\nexus\Package\Group';
|
||||
|
||||
/**
|
||||
* @brief Review Class
|
||||
*/
|
||||
public static $reviewClass = 'IPS\nexus\Package\Review';
|
||||
public static string $reviewClass = 'IPS\nexus\Package\Review';
|
||||
|
||||
/**
|
||||
* @brief Database Column Map
|
||||
*/
|
||||
public static $databaseColumnMap = array(
|
||||
public static array $databaseColumnMap = array(
|
||||
'title' => 'name',
|
||||
'container' => 'group',
|
||||
'featured' => 'featured',
|
||||
@@ -77,115 +121,138 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $title = 'product';
|
||||
public static string $title = 'product';
|
||||
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'archive';
|
||||
public static string $icon = 'box';
|
||||
|
||||
/**
|
||||
* @brief Include In Sitemap
|
||||
*/
|
||||
public static $includeInSitemap = FALSE;
|
||||
public static bool $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;
|
||||
public static bool $canBeModeratedFromFrontend = FALSE;
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title()
|
||||
public function get_title() : string
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}");
|
||||
return Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function content()
|
||||
public function content(): ?string
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->get("nexus_package_{$this->id}_desc"); // Has to be get() rather than addToStack() so we can reliably strips tags, etc.
|
||||
return Member::loggedIn()->language()->get("nexus_package_{$this->id}_desc"); // Has to be get() rather than addToStack() so we can reliably strips tags, etc.
|
||||
}
|
||||
|
||||
/**
|
||||
* Can view?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @param Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @return bool
|
||||
*/
|
||||
public function canView( $member=NULL )
|
||||
public function canView( ?Member $member=null ): bool
|
||||
{
|
||||
if ( !$this->store )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$member = $member ?: Member::loggedIn();
|
||||
return $this->member_groups === '*' or $member->inGroup( explode( ',', $this->member_groups ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Package Data
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
public function delete(): void
|
||||
{
|
||||
/* Delete Images */
|
||||
foreach( \IPS\Db::i()->select( '*', 'nexus_package_images', array( 'image_product=?', $this->id ) ) as $image )
|
||||
foreach( Db::i()->select( '*', 'nexus_package_images', array( 'image_product=?', $this->id ) ) as $image )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'nexus_Products', $image['image_location'] )->delete();
|
||||
File::get( 'nexus_Products', $image['image_location'] )->delete();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch ( Exception ) { }
|
||||
}
|
||||
\IPS\Db::i()->delete( 'nexus_package_images', array( 'image_product=?', $this->id ) );
|
||||
Db::i()->delete( 'nexus_package_images', array( 'image_product=?', $this->id ) );
|
||||
|
||||
/* Delete Product Options */
|
||||
\IPS\Db::i()->delete( 'nexus_product_options', array( 'opt_package=?', $this->id ) );
|
||||
Db::i()->delete( 'nexus_product_options', array( 'opt_package=?', $this->id ) );
|
||||
|
||||
/* Delete Base Prices */
|
||||
\IPS\Db::i()->delete( 'nexus_package_base_prices', array( 'id=?', $this->id ) );
|
||||
Db::i()->delete( 'nexus_package_base_prices', array( 'id=?', $this->id ) );
|
||||
|
||||
parent::delete();
|
||||
|
||||
if( Application::appIsEnabled( 'downloads') )
|
||||
{
|
||||
/* Look for files linked to this product */
|
||||
foreach( Db::i()->select( '*', 'downloads_files', array( Db::i()->findInSet( 'file_nexus', array( $this->id ) ) ) ) as $file )
|
||||
{
|
||||
$packages = explode( ',', $file['file_nexus'] );
|
||||
$packages = array_filter( $packages, function( $packageId ){
|
||||
return $packageId != $this->id;
|
||||
} );
|
||||
|
||||
/* If the only package was the one we just deleted, set it as not purchasable but do not remove the file_nexus value or the product will become downloadable by anyone */
|
||||
if( !count( $packages ) )
|
||||
{
|
||||
Db::i()->update( 'downloads_files', array( 'file_purchasable' => 0 ), array( 'file_id=?', $file['file_id'] ) );
|
||||
}
|
||||
/* Otherwise if there are other products that can be purchased, remove this product from the purchasable list */
|
||||
else
|
||||
{
|
||||
Db::i()->update( 'downloads_files', array( 'file_nexus' => implode( ',', $packages ) ), array( 'file_id=?', $file['file_id'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items with permisison check
|
||||
*
|
||||
* @param array $where Where clause
|
||||
* @param string $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array $limit Limit clause
|
||||
* @param string|NULL $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index or NULL to ignore permissions
|
||||
* @param mixed $includeHiddenItems Include hidden items? 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
|
||||
* @param mixed $skipPermission If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip conatiner-based permission. You must still specify this in the $where clause
|
||||
* @param bool $joinTags If true, will join the tags table
|
||||
* @param bool $joinAuthor If true, will join the members table for the author
|
||||
* @param bool $joinLastCommenter If true, will join the members table for the last commenter
|
||||
* @param bool $showMovedLinks If true, moved item links are included in the results
|
||||
* @param array|null $location Array of item lat and long
|
||||
* @return \IPS\Patterns\ActiveRecordIterator|int
|
||||
* @param array $where Where clause
|
||||
* @param string|null $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array|null $limit Limit clause
|
||||
* @param string $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 int|bool|null $includeHiddenItems Include hidden items? 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 Member|null $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinComments If true, will join comment data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinReviews If true, will join review data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $countOnly If true will return the count
|
||||
* @param array|null $joins Additional arbitrary joins for the query
|
||||
* @param bool|Model $skipPermission If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip conatiner-based permission. You must still specify this in the $where clause
|
||||
* @param bool $joinTags If true, will join the tags table
|
||||
* @param bool $joinAuthor If true, will join the members table for the author
|
||||
* @param bool $joinLastCommenter If true, will join the members table for the last commenter
|
||||
* @param bool $showMovedLinks If true, moved item links are included in the results
|
||||
* @param array|null $location Array of item lat and long
|
||||
* @return ActiveRecordIterator|int
|
||||
*/
|
||||
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE, $showMovedLinks=FALSE, $location=NULL )
|
||||
public static function getItemsWithPermission( array $where=array(), string $order=NULL, int|array|null $limit=10, string $permissionKey='read', int|bool|null $includeHiddenItems= Filter::FILTER_AUTOMATIC, int $queryFlags=0, ?Member $member=null, bool $joinContainer=FALSE, bool $joinComments=FALSE, bool $joinReviews=FALSE, bool $countOnly=FALSE, array|null $joins=null, bool|Model $skipPermission=FALSE, bool $joinTags=TRUE, bool $joinAuthor=TRUE, bool $joinLastCommenter=TRUE, bool $showMovedLinks=FALSE, array|null $location=null ): ActiveRecordIterator|int
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$member = $member ?: Member::loggedIn();
|
||||
|
||||
$where[] = "( p_member_groups='*' OR " . \IPS\Db::i()->findInSet( 'p_member_groups', $member->groups ) . ' )';
|
||||
$where[] = "( p_member_groups='*' OR " . Db::i()->findInSet( 'p_member_groups', $member->groups ) . ' )';
|
||||
$where[] = array( 'p_store=?', 1 );
|
||||
|
||||
$return = parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter, $showMovedLinks );
|
||||
@@ -196,21 +263,21 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
/**
|
||||
* @brief Cached URLs
|
||||
*/
|
||||
protected $_url = array();
|
||||
protected mixed $_url = array();
|
||||
|
||||
/**
|
||||
* Get URL
|
||||
*
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
* @return Url
|
||||
*/
|
||||
public function url( $action=NULL )
|
||||
public function url( ?string $action=NULL ): Url
|
||||
{
|
||||
$_key = md5( $action );
|
||||
$_key = $action ? md5( $action ) : NULL;
|
||||
|
||||
if( !isset( $this->_url[ $_key ] ) )
|
||||
{
|
||||
$this->_url[ $_key ] = \IPS\Http\Url::internal( "app=nexus&module=store&controller=product&id={$this->id}", 'front', 'store_product', \IPS\Http\Url\Friendly::seoTitle( \IPS\Member::loggedIn()->language()->get( 'nexus_package_' . $this->id ) ) );
|
||||
$this->_url[ $_key ] = Url::internal( "app=nexus&module=store&controller=product&id={$this->id}", 'front', 'store_product', Friendly::seoTitle( Member::loggedIn()->language()->get( 'nexus_package_' . $this->id ) ) );
|
||||
|
||||
if ( $action )
|
||||
{
|
||||
@@ -220,61 +287,32 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
|
||||
return $this->_url[ $_key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Can review?
|
||||
*
|
||||
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
|
||||
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
|
||||
* @return bool
|
||||
*/
|
||||
public function canReview( $member=NULL, $considerPostBeforeRegistering = TRUE )
|
||||
{
|
||||
if ( !$this->reviewable )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if ( !parent::canReview( $member, $considerPostBeforeRegistering ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( !\IPS\Db::i()->select( 'COUNT(*)', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=? AND ps_member=?', 'nexus', 'package', $this->id, $member->member_id ) )->first() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should new reviews be moderated?
|
||||
*
|
||||
* @param \IPS\Member $member The member posting
|
||||
* @param Member $member The member posting
|
||||
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will check if a newly registered member would be moderated
|
||||
* @return bool
|
||||
*/
|
||||
public function moderateNewReviews( \IPS\Member $member, $considerPostBeforeRegistering = FALSE )
|
||||
public function moderateNewReviews( Member $member, bool $considerPostBeforeRegistering = FALSE ): bool
|
||||
{
|
||||
if ( $this->review_moderate )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return parent::moderateNewReviews( $member, $considerPostBeforeRegistering );
|
||||
|
||||
return $this->_moderateNewReviews( $member, $considerPostBeforeRegistering );
|
||||
}
|
||||
|
||||
/**
|
||||
* Images
|
||||
*
|
||||
* @return \IPS\File\Iterator
|
||||
* @return Iterator
|
||||
*/
|
||||
public function images()
|
||||
public function images() : Iterator
|
||||
{
|
||||
return new \IPS\File\Iterator( \IPS\Db::i()->select( 'image_location', 'nexus_package_images', array( 'image_product=?', $this->id ),'image_primary desc' ), 'nexus_Products', NULL, TRUE );
|
||||
return new Iterator( Db::i()->select( 'image_location', 'nexus_package_images', array( 'image_product=?', $this->id ),'image_primary desc' ), 'nexus_Products', NULL, TRUE );
|
||||
}
|
||||
|
||||
/* !Embeddable */
|
||||
@@ -285,46 +323,45 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
* @param array $params Additional parameters to add to URL
|
||||
* @return string
|
||||
*/
|
||||
public function embedContent( $params )
|
||||
public function embedContent( array $params ): string
|
||||
{
|
||||
$memberCurrency = ( ( isset( \IPS\Request::i()->cookie['currency'] ) and \in_array( \IPS\Request::i()->cookie['currency'], \IPS\nexus\Money::currencies() ) ) ? \IPS\Request::i()->cookie['currency'] : \IPS\nexus\Customer::loggedIn()->defaultCurrency() );
|
||||
$package = \IPS\nexus\Package::load( $this->id );
|
||||
$memberCurrency = ( ( isset( Request::i()->cookie['currency'] ) and in_array( Request::i()->cookie['currency'], Money::currencies() ) ) ? Request::i()->cookie['currency'] : Customer::loggedIn()->defaultCurrency() );
|
||||
$package = Package::load( $this->id );
|
||||
|
||||
/* Do we have renewal terms? */
|
||||
$renewalTerm = NULL;
|
||||
$renewOptions = $package->renew_options ? json_decode( $package->renew_options, TRUE ) : array();
|
||||
if ( \count( $renewOptions ) )
|
||||
if ( count( $renewOptions ) )
|
||||
{
|
||||
$renewalTerm = TRUE;
|
||||
if ( \count( $renewOptions ) === 1 )
|
||||
if ( count( $renewOptions ) === 1 )
|
||||
{
|
||||
$renewalTerm = array_pop( $renewOptions );
|
||||
$renewalTerm = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalTerm['cost'][ $memberCurrency ]['amount'], $memberCurrency ), new \DateInterval( 'P' . $renewalTerm['term'] . mb_strtoupper( $renewalTerm['unit'] ) ), $package->tax ? \IPS\nexus\Tax::load( $package->tax ) : NULL, $renewalTerm['add'] );
|
||||
$renewalTerm = new RenewalTerm( new Money( $renewalTerm['cost'][ $memberCurrency ]['amount'], $memberCurrency ), new DateInterval( 'P' . $renewalTerm['term'] . mb_strtoupper( $renewalTerm['unit'] ) ), $package->tax ? Tax::load( $package->tax ) : NULL, $renewalTerm['add'] );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'nexus', 'front' ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'nexus' )->embedProduct( $this, $renewalTerm, $this->url()->setQueryString( $params ), $this->embedImage() );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'embed.css', 'nexus', 'front' ) );
|
||||
return Theme::i()->getTemplate( 'global', 'nexus' )->embedProduct( $this, $renewalTerm, $this->url()->setQueryString( $params ), $this->embedImage() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image for embed
|
||||
*
|
||||
* @return \IPS\File|NULL
|
||||
* @return File|NULL
|
||||
*/
|
||||
public function embedImage()
|
||||
public function embedImage(): ?File
|
||||
{
|
||||
$product = \IPS\nexus\Package::load( $this->id );
|
||||
return $product->_data['image'] ? \IPS\File::get( 'nexus_Products', $product->_data['image'] ) : NULL;
|
||||
return $this->_data['image'] ? File::get( 'nexus_Products', $this->_data['image'] ) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mapped value
|
||||
*
|
||||
* @param string $key date,content,ip_address,first
|
||||
* @param string $key date,content,ip_address,first
|
||||
* @return mixed
|
||||
*/
|
||||
public function mapped( $key )
|
||||
public function mapped( string $key ): mixed
|
||||
{
|
||||
if ( $key === 'title' )
|
||||
{
|
||||
@@ -342,72 +379,19 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function supportedMetaDataTypes()
|
||||
public static function supportedMetaDataTypes(): array
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/* !Search */
|
||||
|
||||
/**
|
||||
* Title for search index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function searchIndexTitle()
|
||||
{
|
||||
$titles = array();
|
||||
foreach ( \IPS\Lang::languages() as $lang )
|
||||
{
|
||||
try
|
||||
{
|
||||
$titles[] = $lang->get( "nexus_package_{$this->id}" );
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
}
|
||||
}
|
||||
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()
|
||||
public static function basicDataColumns(): array
|
||||
{
|
||||
return array( 'p_id', 'p_base_price', 'p_reviews', 'p_discounts', 'p_renew_options', 'p_tax', 'p_stock', 'p_initial_term' );
|
||||
}
|
||||
@@ -418,34 +402,11 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
* @return Url
|
||||
*/
|
||||
public static function urlFromIndexData( $indexData, $itemData, $action = NULL )
|
||||
{
|
||||
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, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL, $reactions=array() )
|
||||
public static function urlFromIndexData( array $indexData, array $itemData, ?string $action = NULL ) : Url
|
||||
{
|
||||
$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 );
|
||||
return Url::internal( "app=nexus&module=store&controller=product&id={$indexData['index_item_id']}", 'front', 'store_product', Member::loggedIn()->language()->addToStack( 'nexus_package_' . $indexData['index_item_id'], FALSE, array( 'seotitle' => TRUE ) ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -455,12 +416,12 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
* @param array $items Item data (will be an array containing values from basicDataColumns())
|
||||
* @return array
|
||||
*/
|
||||
public static function searchResultExtraData( $items )
|
||||
public static function searchResultExtraData( array $items ): array
|
||||
{
|
||||
$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' ) );
|
||||
$images = iterator_to_array( Db::i()->select( array( 'image_product', 'image_location' ), 'nexus_package_images', array( array( 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 )
|
||||
foreach ( $items as $data )
|
||||
{
|
||||
if ( $data['p_tax'] )
|
||||
{
|
||||
@@ -471,58 +432,32 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
$taxData = array();
|
||||
if ( $taxIds )
|
||||
{
|
||||
$taxData = iterator_to_array( \IPS\Db::i()->select( '*', 'nexus_tax', \IPS\Db::i()->in( 't_id', $taxIds ) )->setKeyField('t_id') );
|
||||
$taxData = iterator_to_array( Db::i()->select( '*', 'nexus_tax', 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 ]['image'] = $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, array $reputationData, $reviewRating, $view )
|
||||
{
|
||||
$url = static::urlFromIndexData( $indexData, $itemData );
|
||||
|
||||
/* Work out the price to display */
|
||||
$customer = \IPS\nexus\Customer::loggedIn();
|
||||
$currency = ( isset( \IPS\Request::i()->cookie['currency'] ) and \in_array( \IPS\Request::i()->cookie['currency'], \IPS\nexus\Money::currencies() ) ) ? \IPS\Request::i()->cookie['currency'] : $customer->defaultCurrency();
|
||||
$renewOptions = $itemData['p_renew_options'] ? json_decode( $itemData['p_renew_options'], TRUE ) : array();
|
||||
$priceInfo = \IPS\nexus\Package::fullPriceInfoFromData( $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, $itemData['p_initial_term'] ? new \DateInterval("P{$itemData['p_initial_term']}") : NULL );
|
||||
|
||||
/* Display */
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'nexus', 'front' )->searchResultProductSnippet( $indexData, $itemData, isset( $itemData['extra']['image'] ) ? $itemData['extra']['image'] : NULL, $url, $priceInfo, $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
|
||||
* @param Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @param Model|NULL $container The container
|
||||
* @return bool
|
||||
*/
|
||||
public static function modPermission( $type, \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
|
||||
public static function modPermission( string $type, Member $member = NULL, Model $container = NULL ): bool
|
||||
{
|
||||
/* 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 )
|
||||
$member = $member ?: Member::loggedIn();
|
||||
if ( in_array( $type, array( 'hide', 'unhide', 'delete' ) ) and $container )
|
||||
{
|
||||
if ( $member->modPermission( "can_{$type}" ) )
|
||||
{
|
||||
@@ -536,77 +471,119 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
/**
|
||||
* Should posting this increment the poster's post count?
|
||||
*
|
||||
* @param \IPS\Node\Model|NULL $container Container
|
||||
* @return void
|
||||
* @param Model|NULL $container Container
|
||||
* @return bool
|
||||
*/
|
||||
public static function incrementPostCount( \IPS\Node\Model $container = NULL )
|
||||
public static function incrementPostCount( Model $container = NULL ): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can edit?
|
||||
* Check if a specific action is available for this Content.
|
||||
* Default to TRUE, but used for overrides in individual Item/Comment classes.
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
|
||||
* @return bool
|
||||
* @param string $action
|
||||
* @param Member|null $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canEdit( $member=NULL )
|
||||
public function actionEnabled( string $action, ?Member $member=null ) : bool
|
||||
{
|
||||
return FALSE;
|
||||
switch( $action )
|
||||
{
|
||||
case 'edit':
|
||||
case 'move':
|
||||
return FALSE;
|
||||
|
||||
case 'review':
|
||||
if ( !$this->reviewable )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$member = $member ?: Member::loggedIn();
|
||||
if ( !Db::i()->select( 'COUNT(*)', 'nexus_purchases', array( 'ps_app=? AND ps_type=? AND ps_item_id=? AND ps_member=?', 'nexus', 'package', $this->id, $member->member_id ) )->first() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::actionEnabled( $action, $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Moderator Action
|
||||
*
|
||||
* @param string $action The action
|
||||
* @param \IPS\Member|NULL $member The member doing the action (NULL for currently logged in member)
|
||||
* @param Member|NULL $member The member doing the action (NULL for currently logged in member)
|
||||
* @param string|NULL $reason Reason (for hides)
|
||||
* @param bool $immediately Delete immediately
|
||||
* @return void
|
||||
* @throws \OutOfRangeException|\InvalidArgumentException|\RuntimeException
|
||||
* @throws OutOfRangeException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function modAction( $action, \IPS\Member $member = NULL, $reason = NULL, $immediately = FALSE )
|
||||
public function modAction( string $action, Member $member = NULL, mixed $reason = NULL, bool $immediately=FALSE ): void
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can move?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
|
||||
* @return bool
|
||||
*/
|
||||
public function canMove( $member=NULL )
|
||||
{
|
||||
return FALSE;
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the meta description
|
||||
*
|
||||
* @param string|NULL $return Specific description to use (useful for paginated displays to prevent having to run extra queries)
|
||||
* @param string|null $return Specific description to use (useful for paginated displays to prevent having to run extra queries)
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function metaDescription( $return = NULL )
|
||||
public function metaDescription( string $return = NULL ): string
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}_desc", FALSE, array( 'striptags' => TRUE, 'removeNewlines' => TRUE ) );
|
||||
return Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}_desc", FALSE, array( 'striptags' => TRUE, 'removeNewlines' => TRUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container
|
||||
*
|
||||
* @return \IPS\Node\Model
|
||||
* @return Model
|
||||
* @note Certain functionality requires a valid container but some areas do not use this functionality (e.g. messenger)
|
||||
* @throws \OutOfRangeException|\BadMethodCallException
|
||||
* @throws OutOfRangeException|BadMethodCallException
|
||||
*/
|
||||
public function container()
|
||||
public function container(): Model
|
||||
{
|
||||
if( $this->custom > 0 )
|
||||
{
|
||||
throw new \BadMethodCallException;
|
||||
throw new BadMethodCallException;
|
||||
}
|
||||
|
||||
return parent::container();
|
||||
}
|
||||
|
||||
/**
|
||||
* Content is hidden?
|
||||
*
|
||||
* @return int
|
||||
* @li -3 is a post made by a guest using the "post before register" feature
|
||||
* @li -2 is pending deletion
|
||||
* @li -1 is hidden having been hidden by a moderator
|
||||
* @li 0 is unhidden
|
||||
* @li 1 is hidden needing approval
|
||||
* @note The actual column may also contain 2 which means the item is hidden because the parent is hidden, but it is not hidden in itself. This method will return -1 in that case.
|
||||
*
|
||||
* @note A piece of content (item and comment) can have an alias for hidden OR approved.
|
||||
* With hidden: 0=not hidden, 1=hidden (needs moderator approval), -1=hidden by moderator, 2=parent item is hidden, -2=pending deletion, -3=guest post before register
|
||||
* With approved: 1=not hidden, 0=hidden (needs moderator approval), -1=hidden by moderator, -2=pending deletion, -3=guest post before register
|
||||
*
|
||||
* User posting has moderator approval set: When adding an unapproved ITEM (approved=0, hidden=1) you should *not* increment container()->_comments but you should update container()->_unapprovedItems
|
||||
* User posting has moderator approval set: When adding an unapproved COMMENT (approved=0, hidden=1) you should *not* increment item()->num_comments in item or container()->_comments but you should update item()->unapproved_comments and container()->_unapprovedComments
|
||||
*
|
||||
* User post is hidden by moderator (approved=-1, hidden=0) you should decrement item()->num_comments and decrement container()->_comments but *not* increment item()->unapproved_comments or container()->_unapprovedComments
|
||||
* User item is hidden by a moderator (approved=-1, hidden=0) you should decrement container()->comments and subtract comment count from container()->_comments, but *not* increment container()->_unapprovedComments
|
||||
*
|
||||
* Moderator hides item (approved=-1, hidden=-1) you should substract num_comments from container()->_comments. Comments inside item are flagged as approved=-1, hidden=2 but item()->num_comments should not be substracted from
|
||||
*
|
||||
* Comments with a hidden value of 2 should increase item()->num_comments but not container()->_comments
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function hiddenStatus(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user