%PDF- %PDF-
| Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/modules/stream/models/ |
| Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/modules/stream/models/StreamQuery.php |
<?php
namespace humhub\modules\stream\models;
use humhub\modules\stream\models\filters\BlockedUsersStreamFilter;
use humhub\modules\stream\models\filters\DateStreamFilter;
use humhub\modules\stream\models\filters\StreamQueryFilter;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\db\ActiveQuery;
use yii\helpers\ArrayHelper;
use humhub\modules\stream\actions\Stream;
use humhub\modules\stream\models\filters\ContentTypeStreamFilter;
use humhub\modules\stream\models\filters\DefaultStreamFilter;
use humhub\modules\stream\models\filters\OriginatorStreamFilter;
use humhub\modules\stream\models\filters\TopicStreamFilter;
use humhub\modules\content\models\Content;
use humhub\modules\user\models\User;
/**
* Description of StreamQuery
*
* @author buddha
* @since 1.2
*/
class StreamQuery extends Model
{
/**
* @event Event triggered before filterHandlers are applied, this can be used to add custom stream filters.
*/
const EVENT_BEFORE_FILTER = 'beforeFilter';
/**
* @event Event triggered after filterHandlers are applied.
*/
const EVENT_AFTER_FILTER = 'afterFilter';
/**
* Default channels
*/
const CHANNEL_DEFAULT = 'default';
const CHANNEL_ACTIVITY = 'activity';
/**
* Maximum wall entries per request
*/
const MAX_LIMIT = 20;
/**
* Can be set to filter specific content types.
*
* @var array Content type filter
*/
public $includes;
/**
* @var string stream channel to display
*/
public $channel = self::CHANNEL_DEFAULT;
/**
* Can be set to filter out specific content types.
*
* @var array Content type filter
*/
public $excludes;
/**
* The user which requested the stream. By default the current user identity.
* @var \humhub\modules\user\models\User
*/
public $user;
/**
* Can be set to filter content of a specific user
* @var \humhub\modules\user\models\User
*/
public $originator;
/**
* Can be set to request a single content instance.
* @var int
*/
public $contentId;
/**
* Start contentId used for stream "pagination".
* @var int
*/
public $from = 0;
/**
* Top contentId used for stream updates.
* @var int
* @since 1.5
*/
public $to;
/**
* Stream sorting default = Stream::SORT_CREATED_AT;
* @var string
*/
public $sort;
/**
* Result count limit.
* @var int
*/
public $limit;
/**
* Array of stream filters to apply to the query.
* There are the following filter available:
*
* - 'entry_files': Filters content with attached files
* - 'entry_mine': Filters only content created by the query $user
* - 'entry_userinvovled': Filter content the query $user is involved
* - 'visibility_private': Filter only private content
* - 'visibility_public': Filter only public content
*
* > Note: Since v1.3 those filters are forwarded to a [[DefaultStreamFilter]].
*
* @var array
*/
public $filters = [];
/**
* @var array additional query filter handler
* @see [[setupFilters()]]
* @since 1.3
*/
public $filterHandlers = [
DefaultStreamFilter::class,
TopicStreamFilter::class,
ContentTypeStreamFilter::class,
OriginatorStreamFilter::class,
BlockedUsersStreamFilter::class,
DateStreamFilter::class
];
/**
* The content query.
*
* @var ActiveQuery
*/
protected $_query;
/**
* @var boolean query built
*/
protected $_built = false;
/**
* @inheritdoc
*/
public function rules()
{
return [
[['limit', 'from', 'to', 'contentId'], 'number'],
[['sort'], 'safe'],
];
}
/**
* Static initializer.
*
* @param array|string|int $includes either an array of ContentActiveRecord class names or single class name or single contentId.
* @param array|string $excludes either an array of ContentActiveRecord class names or single class name to exclude from the query.
* @return static
*/
public static function find($includes = [], $excludes = [])
{
$instance = new static();
if (!is_int($includes)) {
//Allow single type
if (!is_array($includes)) {
$includes = [$includes];
}
if (!is_array($excludes)) {
$excludes = [$excludes];
}
} else {
$instance->contentId = $includes;
}
return $instance->includes($includes)->excludes($excludes);
}
/**
* @inheritDoc
*/
public function init()
{
$this->_query = Content::find();
// Set default user after initialization so it's avialable without assambling the query.
$this->checkUser();
}
/**
* Builder function for single content stream query.
*
* @param $contentId int
* @return static
*/
public function content($contentId)
{
if (!is_int($contentId)) {
$this->contentId = $contentId;
}
return $this;
}
/**
* Builder function used to set the user perspective of the stream.
*
* @param $user |null User if null the current user identity will be used
* @return static
* @see checkUser
*/
public function forUser($user = null)
{
$this->user = $user;
$this->checkUser();
return $this;
}
/**
* Builder function to overwrite the active filters.
*
* @param array|string $filters
* @return static
*/
public function filters($filters = [])
{
$this->filters = (is_string($filters)) ? [$filters] : $filters;
return $this;
}
/**
* Builder function to add a single or multiple filters to the current set of active filters.
*
* @param $filters
* @return static
*/
public function addFilter($filters)
{
if (!is_string($filters)) {
$this->filters[] = $filters;
} elseif (is_array($filters)) {
$this->filters = ArrayHelper::merge($this->filters, $filters);
}
return $this;
}
/**
* Builder function used to set the stream channel filter.
*
* @param string $channel
* @return static
*/
public function channel($channel)
{
$this->channel = $channel;
$this->_query->andWhere(['content.stream_channel' => $channel]);
return $this;
}
/**
* Builder function used to set the $includes array in order to only include specific content types.
*
* @param string[]|string $includes
* @return static
*/
public function includes($includes = [])
{
if (is_string($includes)) {
$this->includes = [$includes];
} elseif (is_array($includes)) {
$this->includes = $includes;
}
return $this;
}
public function excludes($types = [])
{
if (is_string($types)) {
$this->excludes = [$types];
} elseif (is_array($types)) {
$this->excludes = $types;
}
return $this;
}
/**
* Builder function for query $sort field.
*
* @param $sort string stream sorting either [[Stream::SORT_CREATED_AT]] or [[Stream::SORT_UPDATED_AT]]
* @return static
* @since 1.5
*/
public function sort($sort)
{
$this->sort = $sort;
return $this;
}
/**
* Builder function used to set the $originator filter.
*
* @param $user User
* @return static
*/
public function originator($user)
{
$this->originator = $user;
return $this;
}
/**
* Builder function used to set the $from filter. The result will only include older entries while respecting
* the $order setting. This function can be used for stream pagination (load more).
*
* > Note: the content entry with the id $from will not be included itself!
*
* @param $from int content id used for pagination
* @return static
*/
public function from($from = 0)
{
$this->from = $from;
return $this;
}
/**
* Builder function used to set the $to filter. The result will only include newer entries while respecting
* the $order setting. This function can be used for stream updates.
*
* > Note: the content entry with the id $to will not be included itself!
*
* @param $from int content id used for pagination
* @return static
*/
public function to($to = null)
{
$this->to = $to;
return $this;
}
/**
* Builder function used to set the result limit.
*
* @param int $limit
* @return static
*/
public function limit($limit = self::MAX_LIMIT)
{
$this->limit = $limit;
return $this;
}
/**
* Returns the underlying ActiveQuery object.
*
* @param bool $build weather or not the query should be build by means of the given filters
* @return ActiveQuery
*/
public function query($build = false)
{
if ($build && !$this->_built) {
$this->setupQuery();
}
return $this->_query;
}
/**
* Returns the query result.
*
* @return Content[]
*/
public function all()
{
return $this->query(!$this->_built)->all();
}
/**
* Builds up the query based on the active filters.
*/
protected function setupQuery()
{
$this->checkUser();
$this->checkSort();
$this->checkLimit();
$this->checkFrom();
$this->checkTo();
$this->setupCriteria();
$this->setupFilters();
if (!empty($this->channel)) {
$this->channel($this->channel);
}
$this->_built = true;
}
/**
* Sets the user identity as default user perspective in case no user was set manually.
*/
protected function checkUser()
{
if ($this->user === null && !Yii::$app->user->isGuest) {
$this->user = Yii::$app->user->getIdentity();
}
}
/**
* Sets the default stream sort order in case no or an invalid sort order has been set manually.
*/
protected function checkSort()
{
if (empty($this->sort) || !in_array($this->sort, [Stream::SORT_CREATED_AT, Stream::SORT_UPDATED_AT])) {
$this->sort = Yii::$app->getModule('stream')->settings->get('defaultSort', Stream::SORT_CREATED_AT);
}
}
/**
* Makes sure a valid $from contentId field is set.
*/
protected function checkFrom()
{
if (empty($this->from)) {
$this->from = null;
} else {
$this->from = (int)$this->from;
}
}
/**
* Makes sure a valid $to contentId field is set.
*/
protected function checkTo()
{
if (empty($this->to)) {
$this->to = null;
} else {
$this->to = (int)$this->to;
}
}
/**
* Sets the default limit in case no limit has been set manually.
*/
protected function checkLimit()
{
if (empty($this->limit)) {
$this->limit = self::MAX_LIMIT;
} else if (Yii::$app->request->isConsoleRequest) {
$this->limit = (int)$this->limit;
} else {
$this->limit = ($this->limit > self::MAX_LIMIT) ? self::MAX_LIMIT : (int)$this->limit;
}
}
/**
* Sets up the main query and stream order.
*/
protected function setupCriteria()
{
$this->_query->joinWith('createdBy');
$this->_query->joinWith('contentContainer');
$this->_query->limit($this->limit);
if (!Yii::$app->getModule('stream')->showDeactivatedUserContent) {
$this->_query->andWhere(['user.status' => User::STATUS_ENABLED]);
}
if ($this->contentId) {
$this->_query->andWhere(['content.id' => $this->contentId]);
return;
}
/**
* Setup Sorting
*/
if ($this->sort == Stream::SORT_UPDATED_AT) {
$this->_query->orderBy('content.stream_sort_date DESC');
if (!empty($this->from)) {
$this->_query->andWhere(
['or',
"content.stream_sort_date < (SELECT stream_sort_date FROM content wd WHERE wd.id=:from)",
['and',
"content.stream_sort_date = (SELECT stream_sort_date FROM content wd WHERE wd.id=:from)",
"content.id > :from"
],
], [':from' => $this->from]);
} elseif (!empty($this->to)) {
$this->_query->andWhere(
['or',
"content.stream_sort_date > (SELECT stream_sort_date FROM content wd WHERE wd.id=:to)",
['and',
"content.stream_sort_date = (SELECT stream_sort_date FROM content wd WHERE wd.id=:to)",
"content.id < :to"
],
], [':to' => $this->to]);
}
} else {
$this->_query->orderBy('content.id DESC');
if (!empty($this->from)) {
$this->_query->andWhere("content.id < :from", [':from' => $this->from]);
} elseif (!empty($this->to)) {
$this->_query->andWhere("content.id > :to", [':to' => $this->to]);
}
}
}
/**
* Sets up and apply filters.
*
* @throws \yii\base\InvalidConfigException
*/
protected function setupFilters()
{
$this->beforeApplyFilters();
foreach ($this->filterHandlers as $handler) {
$this->prepareHandler($handler)->apply();
}
$this->afterApplyFilters();
}
/**
* Is called right before applying query filters.
*
* Subclasses may use this function to add additional filters or modify existing filters.
*
* ```php
* protected function beforeApplyFilters()
* {
* parent::beforeApplyFilters();
* $this->addFilterHandler(MyStreamFilter::class);
* }
* ```
*
* @throws \yii\base\InvalidConfigException
* @since 1.6
*/
protected function beforeApplyFilters()
{
$this->trigger(static::EVENT_BEFORE_FILTER);
}
/**
* Is called right after applying query filters.
*
* @throws \yii\base\InvalidConfigException
* @since 1.6
*/
protected function afterApplyFilters()
{
$this->trigger(static::EVENT_AFTER_FILTER);
}
/**
* Adds a new filter handler to this query. This function accepts either a class name string or handler instance.
* Note, this function will automatically set the following filter properties:
*
* - streamQuery: this streamQuery instance
* - query: this activeQuery
* - formName: this formName usually StreamQuery
*
* Usage:
*
* ```php
* // Use this if you need to set some initial filter data
* $filter = $streamQuery->addFilterHandler(new MyFilterHandler(['container' => $this->container]))
*
* // or just
* $filter = $streamQuery->addFilterHandler(AnotherFilter::class)
* ```
*
* @param string|StreamQueryFilter $handler
* @param bool $overwrite whether or not to overwrite existing filters of the same class
* @return StreamQueryFilter initialized stream filter
* @throws InvalidConfigException
* @since 1.6
*/
public function addFilterHandler($handler, $overwrite = true)
{
if($overwrite) {
$this->removeFilterHandler($handler);
}
$handler = $this->prepareHandler($handler);
return $this->filterHandlers[] = $handler;
}
/**
* Can be used to add multiple filter handlers at once.
*
* @see self::addFilterHandler
* @param $handlers
* @param bool $overwrite
* @return string[]|StreamQueryFilter[]
* @throws InvalidConfigException
*/
public function addFilterHandlers($handlers, $overwrite = true)
{
$result = [];
foreach ($handlers as $handler) {
$result[] = $this->addFilterHandler($handler, $overwrite);
}
return $result;
}
/**
* Can be used to remove filters by filter class.
*
* @param $handler
* @return StreamQueryFilter
* @throws InvalidConfigException
* @since 1.6
*/
public function removeFilterHandler($handlerToRemove)
{
$result = [];
$handlerToRemoveClass = is_string($handlerToRemove) ? $handlerToRemove : get_class($handlerToRemove);
foreach ($this->filterHandlers as $handler) {
if(!is_a($handler, $handlerToRemoveClass, true)) {
$result[] = $handler;
}
}
$this->filterHandlers = $result;
}
/**
* Can be used to search for a filter handler by class.
*
* @param $handler
* @return StreamQueryFilter
* @throws InvalidConfigException
* @since 1.6
*/
public function getFilterHandler($handlerToRemove)
{
$handlerToRemoveClass = is_string($handlerToRemove) ? $handlerToRemove : get_class($handlerToRemove);
foreach ($this->filterHandlers as $handler) {
if(is_a($handler, $handlerToRemoveClass, true)) {
return $this->prepareHandler($handler);
}
}
return null;
}
/**
* @param StreamQueryFilter|string $handler
* @return StreamQueryFilter
* @throws InvalidConfigException
* @since 1.6
*/
private function prepareHandler($handler)
{
if (is_string($handler)) {
$handler = Yii::createObject([
'class' => $handler,
'streamQuery' => $this,
'query' => $this->_query,
'formName' => $this->formName()
]);
} elseif ($handler instanceof StreamQueryFilter) {
$handler->streamQuery = $this;
$handler->query = $this->_query;
$handler->formName = $this->formName();
} else {
throw new InvalidConfigException('Invalid stream filter class');
}
return $handler;
}
/**
* @return bool true of this query is used to query a single content entry
*/
public function isSingleContentQuery()
{
return $this->limit == 1 || $this->contentId != null;
}
/**
* Is inital stream requests (show first stream content)
*
* @return boolean Whether or not this query is considered as initial stream query.
*/
public function isInitialQuery()
{
return $this->from === null && $this->to === null && !$this->isSingleContentQuery();
}
}