Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @since 10 May 2023 */ namespace IPS\Content; /* To prevent PHP errors (extending class does not exist) revealing path */ use IPS\core\Feature; use IPS\Events\Event; use IPS\File; use IPS\IPS; use IPS\Member; use IPS\Patterns\ActiveRecordIterator; use OutOfRangeException; use function get_called_class; if( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0') . ' 403 Forbidden' ); exit; } trait Featurable { /** * Can add this object be featured? * * @param Member|NULL $member The member to check for (NULL for currently logged in member) * @return boolean */ public function canFeature( ?Member $member=NULL ) : bool { /* Extensions go first */ if( $permCheck = Permissions::can( 'feature', $this, $member ) ) { return ( $permCheck === Permissions::PERM_ALLOW ); } if( !$this->actionEnabled( 'feature', $member ) ) { return false; } /* Skip for hidden content */ if( $this->hidden() !== 0 ) { return false; } /* Is this the first comment in an item that requires first comments? */ if ( $this instanceof Comment ) { $item = $this->item(); if ( $item::$firstCommentRequired and $this->isFirst() ) { /* Yeah, so do not allow it to be featured, as we want them to feature via the Item Actions menu */ return false; } } /* Do we have moderator permission to feature stuff in the container? */ if ( static::modPermission( 'feature', $member, ( $this instanceof Item ) ? $this->containerWrapper() : $this->item()->containerWrapper() ) ) { return true; } return false; } /** * Determines if the object is already featured * * @return bool */ public function isFeatured() : bool { if ( isset( static::$databaseColumnMap['featured'] ) ) { $featuredColumn = static::$databaseColumnMap['featured']; return (bool) $this->$featuredColumn; } else { try { $idColumn = static::$databaseColumnId; if ( Feature::loadByClassAndId( get_called_class(), $this->$idColumn ) ) { return true; } } catch ( OutOfRangeException ) { } } return FALSE; } /** * Set the featured state * * @param bool $featured * @return bool */ public function setFeatured( bool $featured ) : bool { if( isset( static::$databaseColumnMap['featured'] ) ) { $featuredColumn = static::$databaseColumnMap['featured']; $featured = (int) $featured; $this->$featuredColumn = $featured; $this->save(); /* Fire an event */ Event::fire( 'onStatusChange', $this, array( ( $featured ? 'feature' : 'unfeature' ) ) ); } return true; } /** * Get featured items * * @param int $limit Number to get * @param string $order MySQL ORDER BY clause * @param bool $thisClassOnly * @return ActiveRecordIterator */ public static function featured( $limit=10, $order='RAND()', $thisClassOnly=true ): ActiveRecordIterator { $where = array( array( static::$databaseTable . '.' . static::$databasePrefix . static::$databaseColumnMap['featured'] . '=?', 1 ) ); if( IPS::classUsesTrait( get_called_class(), FuturePublishing::class ) ) { $where[] = array( static::$databaseTable . '.' . static::$databasePrefix . static::$databaseColumnMap['is_future_entry'] . '=?', 0 ); } return static::getItemsWithPermission( $where, $order, $limit ); } /** * Return the featured image for this content * * @return File|null */ public function featuredImage() : ?File { $idColumn = static::$databaseColumnId; if( $promote = Feature::loadByClassAndId( get_class( $this ), $this->$idColumn ) ) { $images = $promote->imageObjects(); if( is_array( $images ) and count( $images ) ) { return $images[0]; } } return null; } }