Enh: Added ContentTag concept for creating content categories/filter on module level

This commit is contained in:
buddh4 2017-07-24 23:56:14 +02:00
parent 2872d2aa9d
commit 14009c6195
17 changed files with 1150 additions and 3 deletions

View File

@ -88,7 +88,7 @@ abstract class SocialActivity extends \yii\base\Object implements rendering\View
* Static initializer should be prefered over new initialization, since it makes use
* of Yii::createObject dependency injection/configuration.
*
* @return \humhub\components\SocialActivity
* @return static
*/
public static function instance($options = [])
{
@ -110,8 +110,8 @@ abstract class SocialActivity extends \yii\base\Object implements rendering\View
/**
* Builder function for the source.
* @param type $source
* @return \humhub\components\SocialActivity
* @param \yii\db\ActiveRecord $source
* @return $this
*/
public function about($source)
{

View File

@ -45,6 +45,7 @@ HumHub Change Log
- Fix: Reset modal dialog size + add `size` option
- Enh: Added `size` option `ui.modal.Modal.set()`
- Enh: Use `ContentActiveRecord::getUrl()` for content perma links (if given)
- Enh: Added `ContentTag` concept for creating content categories/filter on module level
1.2.1 (June 17, 2017)
- Fix: Invite error in french language

View File

@ -0,0 +1,63 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
use yii\db\Migration;
class m170723_133337_content_tag extends Migration
{
public function safeUp()
{
$this->createTable('content_tag', [
'id' => 'pk',
'name' => $this->string(100)->notNull(),
'module_id' => $this->string(100)->notNull(),
'contentcontainer_id' => $this->integer()->null(),
'type' => $this->string(100)->null(),
'parent_id' => $this->integer()->null(),
'color' => $this->string(7)->null()
]);
$this->createIndex('idx-content-tag', 'content_tag', [
'module_id', 'contentcontainer_id', 'name'
]);
$this->addForeignKey('fk-content-tag-container-id', 'content_tag', 'contentcontainer_id', 'contentcontainer', 'id', 'CASCADE');
$this->addForeignKey('fk-content-tag-parent-id', 'content_tag', 'parent_id', 'content_tag', 'id', 'SET NULL');
$this->createTable('content_tag_relation', [
'id' => 'pk',
'content_id' => $this->integer()->notNull(),
'tag_id' => $this->integer()->notNull(),
]);
$this->addForeignKey('fk-content-tag-rel-content-id', 'content_tag_relation', 'content_id', 'content', 'id', 'CASCADE');
$this->addForeignKey('fk-content-tag-rel-tag-id', 'content_tag_relation', 'tag_id', 'content_tag', 'id', 'CASCADE');
}
public function safeDown()
{
echo "m170723_133337_content_filter cannot be reverted.\n";
return false;
}
/*
// Use up()/down() to run migration code without a transaction.
public function up()
{
}
public function down()
{
echo "m170723_133337_content_filter cannot be reverted.\n";
return false;
}
*/
}

View File

@ -11,6 +11,7 @@ namespace humhub\modules\content\models;
use Yii;
use humhub\modules\user\components\PermissionManager;
use yii\base\Exception;
use yii\base\InvalidParamException;
use yii\helpers\Url;
use humhub\modules\user\models\User;
use humhub\modules\space\models\Space;
@ -419,6 +420,52 @@ class Content extends ContentDeprecated
return $this->hasOne(ContentContainer::className(), ['id' => 'contentcontainer_id']);
}
/**
* Returns the ContentTagRelation query.
*
* @since 1.2.2
* @return \yii\db\ActiveQuery
*/
public function getTagRelations()
{
return $this->hasMany(ContentTagRelation::className(), ['content_id' => 'id']);
}
/**
* Returns all content related tags ContentTags related to this content.
*
* @since 1.2.2
* @return \yii\db\ActiveQuery
*/
public function getTags()
{
return $this->hasMany(ContentTag::class, ['id' => 'tag_id'])->via('tagRelations');
}
/**
* Adds a new ContentTagRelation for this content and the given $tag instance.
*
* @since 1.2.2
* @throws InvalidParamException if the provided tag is part of another ContentContainer
* @return boolean true if tag relation could be saved or is already assigned otherwise false
*/
public function addTag(ContentTag $tag)
{
if(!empty($tag->contentcontainer_id) && $tag->contentcontainer_id != $this->contentcontainer_id) {
throw new InvalidParamException(Yii::t('ContentModule.base', 'Content Tag with invalid contentcontainer_id assigned.'));
}
if(ContentTagRelation::findBy($this, $tag)->count()) {
return true;
}
unset($this->tags);
$contentRelation = new ContentTagRelation($this, $tag);
return $contentRelation->save();
}
/**
* Checks if the given user can edit this content.
*

View File

@ -0,0 +1,421 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 23.07.2017
* Time: 15:47
*/
namespace humhub\modules\content\models;
use humhub\components\ActiveRecord;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentContainerActiveRecord;
use Yii;
use yii\db\ActiveQuery;
/**
* ContentTags are a concept to categorize content on module and ContentContainer level.
* By means of ContentTags a module is able to create own category concepts as for example event types (calendar) or
* task related tags, in which the module itself is responsible for managing and the usage of it's tags.
*
* Module implementing own an own category concept should extend the ContentTag class as follows:
*
* ```php
* class MyModuleCategory extends ContentTag
* {
* public $moduleId = 'myModule';
*
* public static getLabel()
* {
* return 'MyCategory'
* }
* ```
*
* All MyModuleCategory will be assigned with the given `$moduleId` as `$module_id` and the class name as `$type`.
* Calls to `MyModuleCategory::find()` will always include `$module_id` filters, the same goes for other query methods as `findByCotnainer` etc.
*
* If a subclass sets `$includeTypeQuery` to true queries will also include a $type filter. This is required if a module provides more than one
* tag type.
*
* If you require to select untyped tags use `ContentTag::find()` instead.
*
* If the contentcontainer_id of a given ContentTag is null, the ContentTag is meant to be module global and therefore not bound to a ContentContainer instance.
*
* In some cases a ContentTag has to be related with some further settigns, this can be achieved by extending the `ContentTagAddition` class and
* setting the ContentTags `$additioNClass`.
*
* An instance of this class will automatically be created by calling `$tag->addition`
*
* @property integer $id
* @property string name
* @property string module_id
* @property integer contentcontainer_id
* @property string type
* @property integer parent_id
*
* @package humhub\modules\content\models
* @since 1.2.2
* @author buddha
*/
class ContentTag extends ActiveRecord
{
public $moduleId;
public $additionClass;
protected $_container;
public $includeTypeQuery = false;
/**
* @inheritdoc
*/
public static function tableName()
{
return 'content_tag';
}
public function __construct($contentContainer = [], $name = null, $config = [])
{
if (is_array($contentContainer)) {
parent::__construct($contentContainer);
} else if ($contentContainer instanceof ContentContainerActiveRecord) {
$this->contentcontainer_id = $contentContainer->contentcontainer_id;
parent::__construct($config);
} else {
parent::__construct([]);
}
if(!empty($name)) {
$this->name = $name;
}
}
public static function getLabel()
{
return Yii::t('ContentModule.models_ContentTag', 'Tag');
}
/**
* Sets the module_id as and type.
* Subclasses overwriting this method have to make sure to call parent::init() at the end.
*/
public function init()
{
$this->module_id = $this->moduleId;
if (!$this->type) {
$this->type = static::class;
}
parent::init();
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['name', 'module_id'], 'required'],
[['name', 'module_id', 'type'], 'string', 'max' => '100'],
['color', 'string', 'max' => '7'],
[['parent_id'], 'integer'],
[['name'], 'validateUnique']
];
}
/**
* Validates
* @param $attribute
* @param $params
* @param $validator
*/
public function validateUnique($attribute, $params, $validator)
{
if (empty($this->contentcontainer_id)) {
$query = self::find()->andWhere('contentcontainer_id IS NULL');
} else {
$query = self::findByContainer($this->contentcontainer_id);
}
$query->andWhere(['name' => $this->name]);
if (!$this->isNewRecord) {
$query->andWhere(['<>', 'id', $this->id]);
}
if ($query->count() > 0) {
$this->addError('name', Yii::t('ContentModule.models_ContentTag', 'The given name is already in use.'));
}
}
/**
* @inheritdoc
*/
public function __get($name)
{
/**
* Ensure there is always a corresponding Content record
* @see ContentTagAddition
*/
if ($name == 'addition' && $this->additionClass) {
$addition = parent::__get('addition');
if (!$this->isRelationPopulated('addition') || $addition === null) {
$addition = Yii::createObject($this->additionClass);
$this->populateRelation('addition', $addition);
$addition->setTag($this);
}
return $addition;
}
return parent::__get($name);
}
/**
* @inheritdoc
*/
public function validate($attributeNames = null, $clearErrors = true)
{
$result = parent::validate($attributeNames, $clearErrors);
if($attributeNames === null || in_array('addition', $attributeNames)) {
// the addition will only be validated if $tag->addition has been called
if($this->hasAddition() && !$this->addition->validate()) {
return false;
}
}
return $result;
}
/**
* Returns the ContentContainer relation as ActiveQuery to this tag or null if this is a global tag.
*
* Note: In order to retrieve the actual Cotnainer (Space/User) use `getContainer()`.
* @return \yii\db\ActiveQuery
*/
public function getContentContainer()
{
if ($this->contentcontainer_id === null) {
return null;
}
return $this->hasOne(ContentContainer::className(), ['id' => 'contentcontainer_id']);
}
/**
* Returns the actual Container (Space/User) related to this tag or null if this is a global tag.
* This function will cache the container instance once loaded.
*
* @return null|ContentContainerActiveRecord
*/
public function getContainer()
{
if ($this->contentcontainer_id === null) {
return null;
}
if ($this->_container === null) {
$this->_container = $this->contentContainer->getPolymorphicRelation();
}
return $this->_container;
}
/**
* Returns the parent tag relation.
*
* @return \yii\db\ActiveQuery
*/
public function getParent()
{
if ($this->parent_id === null) {
return null;
}
return $this->hasOne(ContentTag::className(), ['id' => 'parent_id']);
}
/**
* @return ActiveRecord|\yii\db\ActiveQuery
*/
public function getAddition()
{
if (!$this->additionClass) {
return null;
}
return $this->hasOne($this->additionClass, ['tag_id' => 'id']);
}
public function hasAddition()
{
return $this->additionClass != null && $this->isRelationPopulated('addition');
}
public function setAddition(ContentTagAddition $addition)
{
$addition->setTag($this);
}
public function afterSave($insert, $changedAttributes)
{
$addition = $this->addition;
parent::afterSave($insert, $changedAttributes);
}
/**
* Checks if this content tag is of the given $type.
*
* @param ContentTag $tag
* @return bool
*/
public function is($type)
{
return $this->type === $type;
}
/**
* Finds instances and filters by module_id if a static module_id is given.
*
* @return \yii\db\ActiveQuery
*/
public static function find()
{
$query = parent::find();
self::moduleQuery($query);
return self::typeQuery($query);
}
/**
* Adds an type filter query to the given $query instance in case $includeTypeQuery of the subclass is set to true and the
* calling class is not ContentTag class itself.
*
* @param $query ActiveQuery
* @param bool $force
* @return mixed
*/
protected static function typeQuery($query)
{
$instance = new static;
if($instance->includeTypeQuery && static::class != ContentTag::class) {
$query->andWhere(['content_tag.type' => static::class]);
}
return $query;
}
/**
* Adds an module id filter query to the given $query instance in case a subclass has set the $moduleId.
*
* @param $query ActiveQuery
* @return mixed
*/
protected static function moduleQuery($query)
{
$instance = new static;
if (!empty($instance->moduleId)) {
$query->andWhere(['content_tag.module_id' => $instance->moduleId]);
}
return $query;
}
/**
*
*
* @param $moduleId
* @return \yii\db\ActiveQuery
*/
public static function findByModule($moduleId)
{
return parent::find()->where(['module_id' => $moduleId]);
}
/**
* Finds instances by given $type.
*
* @param string $type
* @return \yii\db\ActiveQuery
*/
public static function findByType($type)
{
$query = parent::find();
self::moduleQuery($query)->andWhere(['content_tag.type' => $type]);
return $query;
}
/**
* Returns Content related tags.
*
* @param Content $content
* @param null $type
* @return ActiveQuery
*/
public static function findByContent(Content $content)
{
$query = $content->getTags();
self::moduleQuery($query);
$instance = new static;
if (!empty($instance->moduleId)) {
$query->andWhere(['content_tag.module_id' => $instance->moduleId]);
}
return self::typeQuery($query);
}
/**
* Returns all tag relations of the given type for the given $content.
*
* @param Content $content
* @return ContentTagRelation[]
*/
public static function getTagContentRelations(Content $content)
{
$query = $content->getTagRelations()->innerJoin('content_tag', 'content_tag_relation.tag_id = content_tag.id');
$instance = new static;
if (!empty($instance->moduleId)) {
$query->andWhere(['content_tag.module_id' => $instance->moduleId]);
}
if (static::class != ContentTag::class) {
$query->andWhere(['content_tag.type' => static::class]);
}
return $query->all();
}
/**
* Deletes all tag relations of the given type for the given $content.
*
* @param Content $content
*/
public static function deleteContentRelations(Content $content)
{
$relations = self::getTagContentRelations($content);
foreach ($relations as $relation) {
$relation->delete();
}
unset($content->tags);
}
/**
* Finds instances by ContentContainerActiveRecord and optional type.
*
* @param ContentContainerActiveRecord|integer $record Container instance or contentcontainer_id
* @param null $type
* @return \yii\db\ActiveQuery
*/
public static function findByContainer($container)
{
$container_id = $container instanceof ContentContainerActiveRecord ? $container->contentcontainer_id : $container;
return self::find()->andWhere(['content_tag.contentcontainer_id' => $container_id]);
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 23.07.2017
* Time: 21:38
*/
namespace humhub\modules\content\models;
use yii\db\ActiveRecord;
/**
* Class ContentTagAddition
*
* @property integer $id
* @perperty integer $tag_id
*
* @since 1.2.2
* @author buddha
*/
class ContentTagAddition extends ActiveRecord
{
public function setTag(ContentTag $tag)
{
$this->tag_id = $tag->id;
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 23.07.2017
* Time: 22:06
*/
namespace humhub\modules\content\models;
use humhub\components\ActiveRecord;
use yii\base\InvalidParamException;
/**
* Class ContentTagRelation
*
* @property integer $id
* @property integer $content_id
* @property integer $tag_id
*
* @since 1.2.2
* @author buddha
*/
class ContentTagRelation extends ActiveRecord
{
public static function tableName()
{
return "content_tag_relation";
}
/**
* ContentTagRelation constructor.
* @param array $content
* @param ContentTag|null $tag
* @param array $config
*/
public function __construct($content = [], $tag = null, $config = [])
{
if(is_array($content)) {
parent::__construct($content);
} else if($content instanceof Content) {
$this->setContent($content);
if($tag !== null && $tag->isNewRecord) {
throw new InvalidParamException('ContentTag was not saved before creating ContentTagRelation');
}
if($tag !== null) {
$this->setTag($tag);
}
parent::__construct($config);
} else {
parent::__construct([]);
}
}
public static function findBy($contentId, $tagId) {
$contentId = ($contentId instanceof Content) ? $contentId->id : $contentId;
$tagId = ($tagId instanceof ContentTag) ? $tagId->id : $tagId;
return self::find()->where(['content_id' => $contentId])->andWhere(['tag_id' => $tagId]);
}
public function getTag()
{
return $this->hasOne(ContentTagAddition::class, ['id' => 'tag_id']);
}
public function getContent()
{
return $this->hasOne(Content::class, ['id' => 'content_id']);
}
public function setContent(Content $content)
{
$this->content_id = $content->id;
}
public function setTag(ContentTag $tag)
{
$this->tag_id = $tag->id;
}
}

View File

@ -18,6 +18,8 @@ class ContentFixture extends ActiveFixture
public $depends = [
'humhub\modules\content\tests\codeception\fixtures\ContentContainerFixture',
'humhub\modules\content\tests\codeception\fixtures\ContentTagFixture',
'humhub\modules\content\tests\codeception\fixtures\ContentTagRelationFixture',
'humhub\modules\post\tests\codeception\fixtures\PostFixture',
'humhub\modules\comment\tests\codeception\fixtures\CommentFixture',
'humhub\modules\like\tests\codeception\fixtures\LikeFixture'

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace humhub\modules\content\tests\codeception\fixtures;
use humhub\modules\content\models\ContentTag;
use yii\test\ActiveFixture;
class ContentTagFixture extends ActiveFixture
{
public $modelClass = ContentTag::class;
public $dataFile = '@modules/content/tests/codeception/fixtures/data/content_tag.php';
}

View File

@ -0,0 +1,20 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace humhub\modules\content\tests\codeception\fixtures;
use humhub\modules\content\models\ContentTag;
use humhub\modules\content\models\ContentTagRelation;
use yii\test\ActiveFixture;
class ContentTagRelationFixture extends ActiveFixture
{
public $modelClass = ContentTagRelation::class;
public $dataFile = '@modules/content/tests/codeception/fixtures/data/content_tag_relation.php';
}

View File

@ -0,0 +1,9 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
return [];

View File

@ -0,0 +1,9 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
return [];

View File

@ -0,0 +1,217 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 24.07.2017
* Time: 15:56
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\modules\content\models\Content;
use humhub\modules\content\models\ContentTag;
use humhub\modules\content\models\ContentTagRelation;
use humhub\modules\space\models\Space;
use tests\codeception\_support\HumHubDbTestCase;
use yii\base\InvalidParamException;
class ContentTagTest extends HumHubDbTestCase
{
public $space;
public function testCreation()
{
// Test Tag validation/constructor
$this->assertTrue($this->createTestTag('testTag1'));
// Test Tag db attributes
$tag = TestTag::findOne(1);
$this->assertEquals('testTag1', $tag->name);
$this->assertEquals($this->space->contentcontainer_id, $tag->contentcontainer_id);
$this->assertEquals('test', $tag->moduleId);
$this->assertEquals(TestTag::class, $tag->type);
// Test unique module/container/name
$this->assertFalse($this->createTestTag('testTag1'));
// Other types should be able to use the same name
$this->assertTrue($this->createOtherTestTag('testTag1'));
// Other modules should be able to use the same name
$this->assertTrue($this->createOtherModuleTestTag('testTag1'));
// Other contentcontainer should be able to use the same name
$space2 = Space::findOne(2);
$this->assertTrue($this->createTestTag('testTag1', $space2));
// Global tag should be able to use the same name
$tag = new TestTagOtherModule(null, 'testTag1');
$this->assertTrue($tag->save());
}
public function testFindByContainer()
{
$space2 = Space::findOne(2);
$this->assertTrue($this->createTestTag('testTag1'));
$this->assertTrue($this->createTestTag('testTag2'));
$this->assertTrue($this->createTestTag('testTag3', $space2));
$this->assertTrue($this->createOtherTestTag('testTagA'));
$this->assertTrue($this->createOtherTestTag('testTagB'));
$this->assertTrue($this->createOtherTestTag('testTagC', $space2));
$this->assertTrue($this->createOtherModuleTestTag('testTagX'));
$this->assertTrue($this->createOtherModuleTestTag('testTagY'));
$this->assertTrue($this->createOtherModuleTestTag('testTagZ', $space2));
$allSpace1Tags = ContentTag::findByContainer($this->space)->all();
$this->assertEquals(6, count($allSpace1Tags));
$allSpace2Tags = ContentTag::findByContainer($space2)->all();
$this->assertEquals(3, count($allSpace2Tags));
$typedSpace1Tags = TestTag::findByContainer($this->space)->all();
$this->assertEquals(2, count($typedSpace1Tags));
$typedSpace2Tags = TestTag::findByContainer($space2)->all();
$this->assertEquals(1, count($typedSpace2Tags));
$otherModuleTags = TestTagOtherModule::findByContainer($this->space)->all();
$this->assertEquals(2, count($otherModuleTags));
$otherModuleTagsS2 = TestTagOtherModule::findByContainer($space2)->all();
$this->assertEquals(1, count($otherModuleTagsS2));
}
public function testTagDeletion()
{
$content = Content::findOne(1);
$tag2 = new TestTagSameModule($content->getContainer(), 'test2');
$tag2->save();
$content->addTag($tag2);
$this->assertEquals(1, count($content->tagRelations));
$tag2->delete();
$content->refresh();
$this->assertEquals(0, count($content->tagRelations));
}
public function testContentDeletion()
{
$content = Content::findOne(1);
$tag2 = new TestTagSameModule($content->getContainer(), 'test2');
$tag2->save();
$content->addTag($tag2);
$this->assertEquals(1, ContentTagRelation::find()->count());
$content->delete();
$this->assertEquals(0, ContentTagRelation::find()->count());
}
public function testTagContentRelation()
{
$this->space = Space::findOne(['id' => 3]);
$tag = new TestTag($this->space, 'test');
$content = Content::findOne(1);
try {
$content->addTag($tag);
$this->assertTrue(false);
} catch(InvalidParamException $e) {
// Tag was not saved
$this->assertTrue(true);
}
$this->assertTrue($tag->save());
try {
$content->addTag($tag);
$this->assertTrue(false);
} catch(InvalidParamException $e) {
// Tag assigned with invalid container_id
$this->assertTrue(true);
}
$tag->contentcontainer_id = $content->contentcontainer_id;
$this->assertTrue($content->addTag($tag));
$this->assertEquals(1, count($content->tags));
$this->assertTrue($content->addTag($tag));
$this->assertEquals(1, count($content->tags));
$tag2 = new TestTagSameModule($content->getContainer(), 'test2');
$tag2->save();
$content->addTag($tag2);
$this->assertEquals(2, count($content->tags));
$tag3 = new TestTagOtherModule($content->getContainer(), 'test3');
$tag3->save();
$content->addTag($tag3);
$this->assertEquals(3, count($content->tags));
$sameModuleTags = TestTagSameModule::findByContent($content)->all();
$this->assertEquals(1, count($sameModuleTags));
$tags = TestTag::findByContent($content)->all();
$this->assertEquals(1, count($tags));
TestTagSameModule::deleteContentRelations($content);
$sameModuleTags = TestTagSameModule::findByContent($content)->all();
$this->assertEquals(0, count($sameModuleTags));
$this->assertEquals(2, count($content->tags));
ContentTag::deleteContentRelations($content);
$this->assertEquals(0, count($content->tags));
}
protected function createTestTag($name, $container = null)
{
$container = (!$container) ? $this->space : $container;
if(!$container) {
$container = $this->space = Space::findOne(['id' => 3]);
}
$tag = new TestTag($container, $name);
return $tag->save();
}
protected function createOtherTestTag($name, $container = null)
{
$container = (!$container) ? $this->space : $container;
if(!$container) {
$container = $this->space = Space::findOne(['id' => 3]);
}
$tag = new TestTagSameModule($container, $name);
return $tag->save();
}
protected function createOtherModuleTestTag($name, $container = null)
{
$container = (!$container) ? $this->space : $container;
if(!$container) {
$container = $this->space = Space::findOne(['id' => 3]);
}
$tag = new TestTagOtherModule($container, $name);
return $tag->save();
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 24.07.2017
* Time: 15:56
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\modules\content\models\ContentTag;
class TestTag extends ContentTag
{
public $moduleId = 'test';
public $includeTypeQuery = true;
public static function getLabel()
{
return 'testCategory';
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 24.07.2017
* Time: 15:56
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\modules\content\models\ContentTag;
class TestTagOtherModule extends ContentTag
{
public $moduleId = 'otherTest';
public static function getLabel()
{
return 'testCategory';
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 24.07.2017
* Time: 15:56
*/
namespace humhub\modules\content\tests\codeception\unit;
use humhub\modules\content\models\ContentTag;
class TestTagSameModule extends ContentTag
{
public $moduleId = 'test';
public $includeTypeQuery = true;
public static function getLabel()
{
return 'testCategory';
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
/**
* Created by PhpStorm.
* User: buddha
* Date: 23.07.2017
* Time: 17:36
*/
namespace humhub\widgets;
use humhub\libs\Html;
use humhub\modules\content\models\ContentTag;
class ContentTagDropDown extends InputWidget
{
/**
* @var string tagClass
*/
public $tagClass;
public $query;
public $contentContainer;
public $type = true;
public $prompt = false;
public $promptValue = 0;
public $items;
private $_itemOptions = [];
public function int() {
if(!$this->tagClass) {
$this->tagClass = ContentTag::class;
// Reset default behavior inf no specific tagClass is given
if($this->type === true) {
$this->type = null;
}
}
if(!$this->none && !$this->noneLabel) {
$this->noneLabel = Yii::t('ContentModule.widgets_ContentTagDropDown', 'None');
}
}
public function run()
{
$items = $this->getItems();
if(empty($items)) {
return;
}
$options = $this->getOptions();
unset($options['id']);
if($this->form && $this->hasModel()) {
return $this->form->field($this->model, $this->attribute)->dropDownList($items, $options);
} else if($this->hasModel()) {
return Html::activeDropDownList($this->model, $this->attribute, $items, $options);
} else {
return Html::dropDownList($this->name, $this->value, $items, $options);
}
}
public function getAttributes()
{
$result = [
'class' => 'form-control',
'options' => $this->_itemOptions
];
if($this->prompt) {
$result['prompt'] = $this->prompt;
}
return $result;
}
public function getItems()
{
if($this->items) {
return $this->items;
}
if(!$this->query) {
if($this->contentContainer) {
$this->query = call_user_func($this->tagClass .'::findByContainer', $this->contentContainer);
} elseif(!empty($this->type)){
$type = ($this->type === true) ? $this->tagClass : $this->type;
$this->query = call_user_func($this->tagClass .'::findByType', [$type]);
} else {
$this->query = call_user_func($this->tagClass .'::find');
}
}
$tags = $this->items = $this->query->all();
$result = [];
foreach ($tags as $tag) {
$result[$tag->id] = $tag->name;
$this->_itemOptions[$tag->id] = [
'data-type-color' => $tag->color
];
}
return $result;
}
}