Chg: Removed unnecessary ContentActiveRecord:initContent

This commit is contained in:
buddh4 2020-07-14 17:55:48 +02:00
parent 97f99e9adf
commit fd59272441
11 changed files with 534 additions and 56 deletions

View File

@ -107,13 +107,6 @@ class ContentActiveRecord extends ActiveRecord implements ContentOwner, Movable
*/
public $silentContentCreation = false;
/**
* @var Content used to cache the content relation in order to avoid the relation to be overwritten in the insert process
* @see https://github.com/humhub/humhub/issues/3110
* @since 1.3
*/
protected $initContent;
/**
* @var bool|string defines if the Movable behaviour of this ContentContainerActiveRecord type is active.
* @see Content::move()
@ -177,15 +170,12 @@ class ContentActiveRecord extends ActiveRecord implements ContentOwner, Movable
* Ensure there is always a corresponding Content record
* @see Content
*/
if ($name == 'content') {
$content = $this->initContent = (empty($this->initContent)) ? parent::__get('content') : $this->initContent;
if ($name === 'content') {
$content = parent::__get('content');
if(!$content) {
$content = $this->initContent = new Content();
$content = new Content();
$content->setPolymorphicRelation($this);
}
if(!$this->isRelationPopulated('content')) {
$this->populateRelation('content', $content);
}
@ -206,7 +196,7 @@ class ContentActiveRecord extends ActiveRecord implements ContentOwner, Movable
}
/**
* Can be used to define an icon for this content type e.g.: 'fa-calendar'.
* Can be used to define an icon for this content type e.g.: 'calendar'.
* @return string
*/
public function getIcon()
@ -392,7 +382,6 @@ class ContentActiveRecord extends ActiveRecord implements ContentOwner, Movable
// Set polymorphic relation
if ($insert) {
$this->populateRelation('content', $this->initContent);
$this->content->object_model = static::getObjectModel();
$this->content->object_id = $this->getPrimaryKey();
}

View File

@ -32,10 +32,24 @@ use yii\db\IntegrityException;
use yii\helpers\Url;
/**
* This is the model class for table "content".
* This is the model class for table "content". The content model serves as relation between a [[ContentContainer]] and
* [[ContentActiveRecord]] entries and contains shared content features as
*
* - read/write permission checks
* - move content
* - pin content
* - archive content
* - content author and update information
* - stream relation by `stream_channel` field
*
* The relation to [[ContentActiveRecord]] models is defined by a polymorphic relation
* `object_model` and `object_id` content table fields.
*
* The relation to [[ContentContainer]] is defined by `contentContainer_id` field.
*
* Note: Instances of this class are automatically created and saved by the [[ContentActiveRecord]] model and should not
* manually be created or deleted to maintain data integrity.
*
* The followings are the available columns in table 'content':
* @property integer $id
* @property string $guid
* @property string $object_model
@ -47,10 +61,10 @@ use yii\helpers\Url;
* @property integer $created_by
* @property string $updated_at
* @property integer $updated_by
* @property ContentContainer $contentContainer
* @property string $stream_sort_date
* @property string $stream_channel
* @property integer $contentcontainer_id;
* @property ContentContainer $contentContainer
* @property ContentContainerActiveRecord $container
* @mixin PolymorphicRelation
* @mixin GUID
@ -58,6 +72,11 @@ use yii\helpers\Url;
*/
class Content extends ActiveRecord implements Movable, ContentOwner
{
/**
* The default stream channel.
* @since 1.6
*/
const STREAM_CHANNEL_DEFAULT = 'default';
/**
* A array of user objects which should informed about this new content.
@ -132,20 +151,34 @@ class Content extends ActiveRecord implements Movable, ContentOwner
}
/**
* Returns a Content Object by given Class and ID
* Returns a [[ContentActiveRecord]] model by given polymorphic relation class and id.
* If there is no existing content relation with this model instance, null is returned.
*
* @param string $className Class Name of the Content
* @param int $id Primary Key
* @return ContentActiveRecord|null
* @throws IntegrityException
*/
public static function Get($className, $id)
{
$content = self::findOne(['object_model' => $className, 'object_id' => $id]);
if ($content != null) {
return $className::findOne(['id' => $id]);
if ($content) {
return $content->getModel();
}
return null;
}
/**
* @return ContentActiveRecord
* @throws IntegrityException
* @since 1.3
*/
public function getModel()
{
return $this->getPolymorphicRelation();
}
/**
* @inheritdoc
*/
@ -187,7 +220,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner
public function afterSave($insert, $changedAttributes)
{
/* @var $contentSource ContentActiveRecord */
$contentSource = $this->getPolymorphicRelation();
$contentSource = $this->getModel();
foreach ($this->notifyUsersOfNewContent as $user) {
$contentSource->follow($user->id);
@ -332,6 +365,8 @@ class Content extends ActiveRecord implements Movable, ContentOwner
* This is only allowed for workspace owner.
*
* @return boolean
* @throws Exception
* @throws \yii\base\InvalidConfigException
*/
public function canPin()
{
@ -356,6 +391,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner
* Checks if current content object is archived
*
* @return boolean
* @throws Exception
*/
public function isArchived()
{
@ -367,14 +403,11 @@ class Content extends ActiveRecord implements Movable, ContentOwner
* The content owner and the workspace admin can archive contents.
*
* @return boolean
* @throws Exception
* @throws \yii\base\InvalidConfigException
*/
public function canArchive()
{
// Disabled on user profiles, there is no stream filter available yet.
if ($this->getContainer() instanceof User) {
return false;
}
return $this->getContainer()->permissionManager->can(new ManageContent());
}
@ -496,6 +529,9 @@ class Content extends ActiveRecord implements Movable, ContentOwner
* - The current user is the owner of this content
* @param ContentContainerActiveRecord|null $container
* @return bool determines if the current user is generally permitted to move content on the given container (or the related container if no container was provided)
* @throws IntegrityException
* @throws \Throwable
* @throws \yii\base\InvalidConfigException
*/
public function checkMovePermission(ContentContainerActiveRecord $container = null)
{
@ -665,9 +701,13 @@ class Content extends ActiveRecord implements Movable, ContentOwner
* - The user is granted the managePermission set by the model record class
* - The user meets the additional condition implemented by the model records class own `canEdit()` function.
*
* @since 1.1
* @param User|integer $user user instance or user id
* @return bool can edit this content
* @throws Exception
* @throws IntegrityException
* @throws \Throwable
* @throws \yii\base\InvalidConfigException
* @since 1.1
*/
public function canEdit($user = null)
{
@ -707,16 +747,6 @@ class Content extends ActiveRecord implements Movable, ContentOwner
return false;
}
/**
* @return ContentActiveRecord
* @throws IntegrityException
* @since 1.3
*/
public function getModel()
{
return $this->getPolymorphicRelation();
}
/**
* Checks the given $permission of the current user in the contents content container.
* This is short for `$this->getContainer()->getPermissionManager()->can()`.

View File

@ -41,6 +41,7 @@ class ContentContainer extends ActiveRecord
{
return [
[['pk', 'owner_user_id'], 'integer'],
[['class', 'pk', 'guid'], 'required'],
[['guid', 'class'], 'string', 'max' => 255],
[['class', 'pk'], 'unique', 'targetAttribute' => ['class', 'pk'], 'message' => 'The combination of Class and Pk has already been taken.'],
[['guid'], 'unique']
@ -78,16 +79,14 @@ class ContentContainer extends ActiveRecord
/**
* @param $guid
* @return ContentContainerActiveRecord
* @return ContentContainerActiveRecord|null
* @throws \yii\db\IntegrityException
* @since 1.4
*/
public static function findRecord($guid)
{
$instance = static::findOne(['guid' => $guid]);
if($instance) {
return $instance->getPolymorphicRelation();
}
return $instance ? $instance->getPolymorphicRelation() : null;
}
}

View File

@ -8,6 +8,8 @@
namespace humhub\modules\content\models;
use yii\db\ActiveRecord;
/**
* This is the model class for table "contentcontainer_module".
*
@ -15,7 +17,7 @@ namespace humhub\modules\content\models;
* @property string $module_id
* @property integer $module_state
*/
class ContentContainerModuleState extends \yii\db\ActiveRecord
class ContentContainerModuleState extends ActiveRecord
{
const STATE_DISABLED = 0;

View File

@ -16,7 +16,19 @@ use yii\base\Model;
use yii\db\Query;
/**
* This class can be used to search for existing types of [[\humhub\modules\content\components\ContentActiveRecords]].
* The ContentType class serves as meta data class for content types and can be used to
* search for existing types of [[\humhub\modules\content\components\ContentActiveRecords]].
*
* ContentType models are usually used for Picker widgets e.g. ContentType filters in a stream. The [[getContentTypes()]]
* and [[getContentTypeSelection()]] function will search for content types with `stream_channel="default"` and an existing
* content entry related to a given container or
*
* ## Usage:
*
* ```php
* // Fetch content types
* $spaceContentTypes = ContentType::getContentTypes($space);
* ```
*
* @since 1.3
*/
@ -35,7 +47,7 @@ class ContentType extends Model
/**
* @var [] caches the result for contentContainer and global requests [$contentContainer->id|'' => ContentType[]]
*/
public static $cache = [];
private static $cache = [];
/**
* @inheritdoc
@ -46,6 +58,11 @@ class ContentType extends Model
$this->instance = Yii::createObject($this->typeClass);
}
public static function flush()
{
static::$cache = [];
}
/**
* @param ContentContainerActiveRecord|null $container
* @return static[] existing content types of the given container
@ -98,18 +115,9 @@ class ContentType extends Model
}
/**
* Returns a description of this particular content.
*
* This will be used to create a text preview of the content record. (e.g. in Activities or Notifications)
* You need to override this method in your content implementation.
*
* @return string description of this content
* Returns a content type related icon
* @return string
*/
public function getContentDescription()
{
return $this->instance->getContentDescription();
}
public function getIcon()
{
return $this->instance->getIcon();

View File

@ -0,0 +1,44 @@
<?php
namespace modules\content\tests\codeception\_support;
use humhub\modules\content\models\Content;
use humhub\modules\content\tests\codeception\unit\TestContent;
use humhub\modules\space\models\Space;
use tests\codeception\_support\HumHubDbTestCase;
class ContentModelTest extends HumHubDbTestCase
{
/**
* @var TestContent
*/
public $testModel;
/**
* @var Content
*/
public $testContent;
/**
* @var Space
*/
public $space;
public function _before()
{
parent::_before();
$this->becomeUser('User2');
$this->space = Space::findOne(['id' => 2]);
$this->testModel = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$this->assertTrue($this->testModel->save());
$this->testContent = $this->testModel->content;
}
}

View File

@ -29,4 +29,8 @@ class TestContent extends Post
$this->managePermission = $managePermission;
}
public function getContentName()
{
return 'TestContent';
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace tests\codeception\unit\modules\content;
use humhub\modules\content\models\Content;
use humhub\modules\content\models\ContentContainer;
use humhub\modules\user\models\User;
use modules\content\tests\codeception\_support\ContentModelTest;
class ContentContainerTest extends ContentModelTest
{
public function testUniqueGuid()
{
$user = User::findOne(['id' => 1]);
$contentContainer = new ContentContainer(['guid' => $user->guid]);
$contentContainer->setPolymorphicRelation($user);
$this->assertFalse($contentContainer->save());
$this->assertNotEmpty($contentContainer->getErrors('guid'));
}
public function testUniqueModel()
{
$user = User::findOne(['id' => 1]);
$contentContainer = new ContentContainer(['guid' => 'xyz']);
$contentContainer->setPolymorphicRelation($user);
$this->assertFalse($contentContainer->save());
$this->assertNotEmpty($contentContainer->getErrors('class'));
}
public function testGuidRequired()
{
$user = User::findOne(['id' => 1]);
$contentContainer = new ContentContainer();
$contentContainer->setPolymorphicRelation($user);
$this->assertFalse($contentContainer->save());
$this->assertNotEmpty($contentContainer->getErrors('guid'));
}
public function testModelRequired()
{
$contentContainer = new ContentContainer();
$this->assertFalse($contentContainer->save());
$this->assertNotEmpty($contentContainer->getErrors('class'));
}
public function testInvalidModel()
{
$contentContainer = new ContentContainer();
$contentContainer->setPolymorphicRelation(Content::findOne(['id' => 1]));
$this->assertFalse($contentContainer->save());
$this->assertNotEmpty($contentContainer->getErrors('class'));
}
public function testFindByGuid()
{
$user = User::findOne(['id' => 1]);
$userRecord = ContentContainer::findRecord($user->guid);
$this->assertInstanceOf(User::class, $userRecord);
$this->assertEquals($user->id, $userRecord->id);
$this->assertEquals($user->contentcontainer_id, $userRecord->contentcontainer_id);
}
public function testFindByInvalidGuid()
{
$this->assertNull(ContentContainer::findRecord('xxx'));
}
}

View File

@ -0,0 +1,98 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace tests\codeception\unit\modules\content;
use humhub\modules\content\tests\codeception\unit\TestContent;
use modules\content\tests\codeception\_support\ContentModelTest;
use humhub\modules\space\models\Space;
use humhub\modules\content\models\Content;
use Yii;
class ContentTest extends ContentModelTest
{
public function testContentDefaults()
{
$this->assertNotEmpty($this->testContent->id);
$this->assertNotEmpty($this->testContent->guid);
$this->assertEquals($this->testContent->object_model, TestContent::class);
$this->assertEquals($this->testContent->object_id, $this->testModel->id);
$this->assertEquals($this->testContent->visibility, Content::VISIBILITY_PUBLIC);
$this->assertEquals($this->testContent->pinned, 0);
$this->assertEquals($this->testContent->archived, 0);
$this->assertNotEmpty($this->testContent->created_at);
$this->assertEquals($this->testContent->created_by, Yii::$app->user->id);
$this->assertNotEmpty($this->testContent->updated_at);
$this->assertEquals($this->testContent->updated_by, Yii::$app->user->id);
$this->assertEquals($this->testContent->updated_at, $this->testContent->created_at);
$this->assertEquals($this->testContent->stream_channel,Content::STREAM_CHANNEL_DEFAULT);
$this->assertEquals($this->testContent->contentcontainer_id,$this->space->contentcontainer_id);
}
public function testInvalidPolymorphicRelation1()
{
$testContent = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$testContent->content->object_model = null;
try {
$testContent->save();
$this->assertTrue(false, 'Content should not be saved!');
} catch (\Exception $e) {
$this->assertTrue(true);
}
}
public function testInvalidPolymorphicRelation2()
{
$testContent = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$testContent->content->object_id = null;
try {
$testContent->save();
$this->assertTrue(false, 'Content should not be saved!');
} catch (\Exception $e) {
$this->assertTrue(true);
}
}
public function testContentRelation()
{
$loadedContent = Content::Get(TestContent::class, $this->testModel->id);
$this->assertTrue($loadedContent->content instanceof Content);
$this->assertEquals($loadedContent->content->id, $this->testModel->content->id);
$this->assertTrue($loadedContent->content->container instanceof Space);
$this->assertEquals($loadedContent->content->container->id, $this->space->id);
$this->assertEquals($loadedContent->content->contentContainer->id, $this->space->contentcontainer_id);
}
public function testContentGet()
{
$loadedContent = Content::Get(TestContent::class, $this->testModel->id);
$this->assertEquals($loadedContent->id, $this->testModel->id);
$this->assertEquals($loadedContent->content->id, $this->testModel->content->id);
}
public function testContentGetNotFound()
{
$loadedContent = Content::Get(TestContent::class, 300);
$this->assertNull($loadedContent);
}
public function testContentGetInvalidClass()
{
$loadedContent = Content::Get('SomeNonExistingClass', 300);
$this->assertNull($loadedContent);
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace tests\codeception\unit\modules\content;
use humhub\modules\content\models\Content;
use humhub\modules\content\models\ContentContainer;
use humhub\modules\content\models\ContentType;
use humhub\modules\content\tests\codeception\unit\TestContent;
use humhub\modules\post\models\Post;
use humhub\modules\user\models\User;
use modules\content\tests\codeception\_support\ContentModelTest;
class ContentTypeTest extends ContentModelTest
{
public function _before()
{
parent::_before();
ContentType::flush();
// Make sure there is no content
Content::deleteAll();
Post::deleteAll();
}
public function testContainerContentTypes()
{
$user = User::findOne(['id' => 1]);
$this->assertEmpty(ContentType::getContentTypes($user));
$testContent = new TestContent($user, ['message' => 'TestContent']);
$this->assertTrue($testContent->save());
ContentType::flush();
$contentTypes = ContentType::getContentTypes($user);
$this->assertCount(1, $contentTypes);
$this->assertEquals(TestContent::class, $contentTypes[0]->typeClass);
$this->assertEquals($testContent->getContentName(), $contentTypes[0]->getContentName());
$testPost = new Post($user, ['message' => 'TestPost']);
$this->assertTrue($testPost->save());
ContentType::flush();
$contentTypes = ContentType::getContentTypes($user);
$this->assertCount(2, $contentTypes);
$this->assertEquals(TestContent::class, $contentTypes[0]->typeClass);
$this->assertEquals($testContent->getContentName(), $contentTypes[0]->getContentName());
$this->assertEquals($testContent->getIcon(), $contentTypes[0]->getIcon());
$this->assertEquals(Post::class, $contentTypes[1]->typeClass);
$this->assertEquals($testPost->getContentName(), $contentTypes[1]->getContentName());
$this->assertEquals($testPost->getIcon(), $contentTypes[1]->getIcon());
$select = ContentType::getContentTypeSelection($user);
$this->assertEquals(TestContent::class, array_keys($select)[0]);
$this->assertEquals($testContent->getContentName(), $select[TestContent::class]);
$this->assertEquals(Post::class, array_keys($select)[1]);
$this->assertEquals($testPost->getContentName(), $select[Post::class]);
}
public function testGlobalContentTypes()
{
$this->assertEmpty(ContentType::getContentTypes());
$testContent = new TestContent(User::findOne(['id' => 1]), ['message' => 'TestContent']);
$this->assertTrue($testContent->save());
ContentType::flush();
$contentTypes = ContentType::getContentTypes();
$this->assertCount(1, $contentTypes);
$this->assertEquals(TestContent::class, $contentTypes[0]->typeClass);
$this->assertEquals($testContent->getContentName(), $contentTypes[0]->getContentName());
$testPost = new Post(User::findOne(['id' => 2]), ['message' => 'TestPost']);
$this->assertTrue($testPost->save());
ContentType::flush();
$contentTypes = ContentType::getContentTypes();
$this->assertCount(2, $contentTypes);
$this->assertEquals(TestContent::class, $contentTypes[0]->typeClass);
$this->assertEquals($testContent->getContentName(), $contentTypes[0]->getContentName());
$this->assertEquals(Post::class, $contentTypes[1]->typeClass);
$this->assertEquals($testPost->getContentName(), $contentTypes[1]->getContentName());
$select = ContentType::getContentTypeSelection();
$this->assertEquals(TestContent::class, array_keys($select)[0]);
$this->assertEquals($testContent->getContentName(), $select[TestContent::class]);
$this->assertEquals(Post::class, array_keys($select)[1]);
$this->assertEquals($testPost->getContentName(), $select[Post::class]);
}
}

View File

@ -0,0 +1,128 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*
*/
namespace tests\codeception\unit\modules\content;
use humhub\modules\content\tests\codeception\unit\TestContent;
use modules\content\tests\codeception\_support\ContentModelTest;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\space\models\Space;
use humhub\modules\content\models\Content;
use Yii;
class ContentVisibilityTest extends ContentModelTest
{
public function testDefaultVisibilityPrivateSpace()
{
$this->space->visibility = Space::VISIBILITY_NONE;
$newModel = new TestContent($this->space, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PRIVATE);
}
public function testDefaultVisibilityProtectedSpace()
{
$this->space->visibility = Space::VISIBILITY_REGISTERED_ONLY;
$newModel = new TestContent($this->space, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PRIVATE);
}
public function testDefaultVisibilityPublicSpace()
{
$this->space->visibility = Space::VISIBILITY_ALL;
$newModel = new TestContent($this->space, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PRIVATE);
}
public function testCreatePublicContentOnPublicSpace()
{
$this->space->visibility = Space::VISIBILITY_ALL;
$newModel = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PUBLIC);
}
public function testCreatePublicContentOnProtectedSpace()
{
$this->space->visibility = Space::VISIBILITY_REGISTERED_ONLY;
$newModel = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PUBLIC);
}
public function testCreateContentOnDefaultContentVisibilityPublic()
{
$this->space->visibility = Space::VISIBILITY_ALL;
$this->space->default_content_visibility = Content::VISIBILITY_PUBLIC;
$newModel = new TestContent($this->space, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PUBLIC);
}
public function testCreateContentOnDefaultContentVisibilityPrivate()
{
$this->space->visibility = Space::VISIBILITY_ALL;
$this->space->default_content_visibility = Content::VISIBILITY_PRIVATE;
$newModel = new TestContent($this->space, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PRIVATE);
}
/**
* Make sure private spaces can not produce public content
*
* Visibility integrity check missing!
*
* @skip
* @throws \yii\base\Exception
*/
public function testCreatePublicContentOnPrivateSpace()
{
$this->space->visibility = Space::VISIBILITY_NONE;
$newModel = new TestContent($this->space, Content::VISIBILITY_PUBLIC, [
'message' => 'Test'
]);
$this->assertTrue($newModel->save());
$this->assertEquals($newModel->content->visibility, Content::VISIBILITY_PRIVATE);
}
}