diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8675199e..299ccd8b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog - Enh #360: Template - Spaces and Users Element Type - Enh #362: Template - Rendering & Caching - Fix #365: Fix allowed properties for Twig v3.14.1+ +- Enh #367: Migrate all element contents to single table 1.10.11 (Unreleased) -------------------------- diff --git a/migrations/m160907_175706_default_templates.php b/migrations/m160907_175706_default_templates.php index 908af1fd..7fcaa267 100644 --- a/migrations/m160907_175706_default_templates.php +++ b/migrations/m160907_175706_default_templates.php @@ -2,15 +2,13 @@ use humhub\components\Migration; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\TextContent; -use humhub\modules\custom_pages\modules\template\models\FileContent; -use humhub\modules\custom_pages\modules\template\models\ContainerContent; -use humhub\modules\custom_pages\modules\template\models\RichtextContent; class m160907_175706_default_templates extends Migration { public function up() { + $containerContentClass = 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent'; + /** * * Two column template @@ -18,8 +16,8 @@ public function up() */ $twoColumnTemplateId = $this->insertTwoColumnTemplate(); // Insert elements - $this->insertTemplateElement($twoColumnTemplateId, 'content', ContainerContent::class); - $this->insertTemplateElement($twoColumnTemplateId, 'sidebar_container', ContainerContent::class); + $this->insertTemplateElement($twoColumnTemplateId, 'content', $containerContentClass); + $this->insertTemplateElement($twoColumnTemplateId, 'sidebar_container', $containerContentClass); // Insert default container definition for content container $this->insertSilent('custom_pages_template_container_content_definition', ['allow_multiple' => 1, 'is_inline' => 0, 'is_default' => 1]); @@ -28,7 +26,7 @@ public function up() 'element_name' => 'content', 'owner_model' => Template::class, 'owner_id' => $twoColumnTemplateId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); @@ -39,7 +37,7 @@ public function up() 'element_name' => 'sidebar_container', 'owner_model' => Template::class, 'owner_id' => $twoColumnTemplateId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); @@ -51,7 +49,7 @@ public function up() $oneColumnTemplateId = $this->insertOneColumnTemplate(); // Insert elements - $this->insertTemplateElement($oneColumnTemplateId, 'content', ContainerContent::class); + $this->insertTemplateElement($oneColumnTemplateId, 'content', $containerContentClass); // Insert default content definition $this->insertSilent('custom_pages_template_container_content_definition', ['allow_multiple' => 1, 'is_inline' => 0, 'is_default' => 1]); @@ -60,7 +58,7 @@ public function up() 'element_name' => 'content', 'owner_model' => Template::class, 'owner_id' => $oneColumnTemplateId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); @@ -73,7 +71,7 @@ public function up() // Insert elements $this->insertTextTemplateElement($headlineTmplId, 'heading', 'My Headline'); $this->insertTextTemplateElement($headlineTmplId, 'subheading', 'My Subheadline'); - $this->insertTemplateElement($headlineTmplId, 'background', FileContent::class); + $this->insertTemplateElement($headlineTmplId, 'background', 'humhub\\modules\\custom_pages\\modules\\template\\models\\FileContent'); /** * @@ -92,7 +90,7 @@ public function up() $snippetLayoutTemplateId = $this->insertSnippetLayoutTemplate(); // Insert elements - $this->insertTemplateElement($snippetLayoutTemplateId, 'heading', ContainerContent::class); + $this->insertTemplateElement($snippetLayoutTemplateId, 'heading', $containerContentClass); // Insert default content definition $this->insertSilent('custom_pages_template_container_content_definition', ['allow_multiple' => 0, 'is_inline' => 0, 'is_default' => 1]); @@ -103,7 +101,7 @@ public function up() 'element_name' => 'heading', 'owner_model' => Template::class, 'owner_id' => $snippetLayoutTemplateId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); @@ -160,7 +158,9 @@ public function insertTemplateElement($tmplid, $name, $contentType) public function insertTextTemplateElement($tmplid, $name, $default = null) { - $this->insertTemplateElement($tmplid, $name, TextContent::class); + $TextContentClass = 'humhub\\modules\\custom_pages\\modules\\template\\models\\TextContent'; + + $this->insertTemplateElement($tmplid, $name, $TextContentClass); if ($default != null) { $this->insertSilent('custom_pages_template_text_content', [ @@ -171,7 +171,7 @@ public function insertTextTemplateElement($tmplid, $name, $default = null) 'element_name' => $name, 'owner_model' => Template::class, 'owner_id' => $tmplid, - 'content_type' => TextContent::class, + 'content_type' => $TextContentClass, 'content_id' => $this->db->getLastInsertID(), ]); } @@ -179,7 +179,7 @@ public function insertTextTemplateElement($tmplid, $name, $default = null) public function insertRichTextTemplateElement($tmplid, $name, $default = null) { - $this->insertTemplateElement($tmplid, $name, RichtextContent::class); + $this->insertTemplateElement($tmplid, $name, 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent'); if ($default != null) { $this->insertSilent('custom_pages_template_richtext_content', [ @@ -190,7 +190,7 @@ public function insertRichTextTemplateElement($tmplid, $name, $default = null) 'element_name' => $name, 'owner_model' => Template::class, 'owner_id' => $tmplid, - 'content_type' => RichtextContent::class, + 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => $this->db->getLastInsertID(), ]); } diff --git a/migrations/m170413_085114_download_link_template.php b/migrations/m170413_085114_download_link_template.php index 50780ce5..d0305135 100644 --- a/migrations/m170413_085114_download_link_template.php +++ b/migrations/m170413_085114_download_link_template.php @@ -2,20 +2,20 @@ use humhub\components\Migration; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\FileDownloadContent; -use humhub\modules\custom_pages\modules\template\models\ContainerContent; class m170413_085114_download_link_template extends Migration { public function up() { + $containerContentClass = 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent'; + //Create Download Item $downloadItemTemplateId = $this->insertDownloadItemTemplate(); - $this->insertTemplateElement($downloadItemTemplateId, 'file_download', 'File', FileDownloadContent::class); + $this->insertTemplateElement($downloadItemTemplateId, 'file_download', 'File', 'humhub\\modules\\custom_pages\\modules\\template\\models\\FileDownloadContent'); //Create Download List $downloadListTemplateId = $this->insertDownloadListTemplate(); - $this->insertTemplateElement($downloadListTemplateId, 'download_list', 'File List', ContainerContent::class); + $this->insertTemplateElement($downloadListTemplateId, 'download_list', 'File List', $containerContentClass); //Create container definition for download_list container $this->insertSilent('custom_pages_template_container_content_definition', ['allow_multiple' => 1, 'is_inline' => 0, 'is_default' => 1]); @@ -27,7 +27,7 @@ public function up() 'element_name' => 'download_list', 'owner_model' => Template::class, 'owner_id' => $downloadListTemplateId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); diff --git a/migrations/m190213_135905_blank_template.php b/migrations/m190213_135905_blank_template.php index b2b37726..772c45e7 100644 --- a/migrations/m190213_135905_blank_template.php +++ b/migrations/m190213_135905_blank_template.php @@ -1,7 +1,6 @@ insertSilent('custom_pages_template', [ 'name' => 'system_plain_layout', 'engine' => 'twig', @@ -25,14 +26,14 @@ public function safeUp() $tempalteId = $this->db->getLastInsertID(); // Insert elements - $this->insertTemplateElement($tempalteId, 'content', ContainerContent::class); + $this->insertTemplateElement($tempalteId, 'content', $containerContentClass); $this->insertSilent('custom_pages_template_container_content_definition', ['allow_multiple' => 1, 'is_inline' => 0, 'is_default' => 1]); $this->insertSilent('custom_pages_template_container_content', ['definition_id' => $this->db->getLastInsertID()]); $this->insertSilent('custom_pages_template_owner_content', [ 'element_name' => 'content', 'owner_model' => Template::class, 'owner_id' => $tempalteId, - 'content_type' => ContainerContent::class, + 'content_type' => $containerContentClass, 'content_id' => $this->db->getLastInsertID(), ]); } diff --git a/migrations/m190213_135906_text_edit_mode.php b/migrations/m190213_135906_text_edit_mode.php index 438bce2f..10d1f516 100644 --- a/migrations/m190213_135906_text_edit_mode.php +++ b/migrations/m190213_135906_text_edit_mode.php @@ -1,8 +1,6 @@ safeCreateTable('custom_pages_template_element_content', [ + 'id' => $this->primaryKey(), + 'element_id' => $this->integer()->notNull(), + 'dynAttributes' => $this->text(), + 'definition_id' => $this->integer(), + ]); + $this->safeCreateTable('custom_pages_template_element_content_definition', [ + 'id' => $this->primaryKey(), + 'dynAttributes' => $this->text(), + 'is_default' => $this->boolean()->notNull()->defaultValue(0), + ]); + $this->safeAddForeignKey('fk-element_id', 'custom_pages_template_element_content', 'element_id', 'custom_pages_template_element', 'id', 'CASCADE'); + $this->safeAddForeignKey('fk-definition_id', 'custom_pages_template_element_content', 'definition_id', 'custom_pages_template_element_content_definition', 'id', 'CASCADE'); + + $this->migrateElements('custom_pages_template_text_content', 'Text', ['content', 'inline_text']); + $this->migrateElements('custom_pages_template_richtext_content', 'Richtext', ['content']); + $this->migrateElements('custom_pages_template_hh_richtext_content', 'HumHubRichtext', ['content']); + $this->migrateElements('custom_pages_template_file_content', 'File', ['file_guid']); + $this->migrateElements('custom_pages_template_file_download_content', 'FileDownload', ['file_guid', 'title', 'style', 'cssClass', 'showFileinfo', 'showIcon']); + $this->migrateElements('custom_pages_template_rss_content', 'Rss', ['url', 'cache_time', 'limit']); + $this->migrateElements('custom_pages_template_contentcontainer_content', 'User', ['guid'], false); + $this->migrateElements('custom_pages_template_contentcontainer_content', 'Space', ['guid']); + $this->migrateElements('custom_pages_template_records_content', 'Users', ['type', 'options' => 'jsonMerge'], false); + $this->migrateElements('custom_pages_template_records_content', 'Spaces', ['type', 'options' => 'jsonMerge']); + $this->migrateElements('custom_pages_template_image_content', 'Image', ['file_guid', 'alt'], true, 'custom_pages_template_image_content_definition', ['height', 'width', 'style']); + + // Migrate Container Elements: + $this->safeDropForeignKey('fk-tmpl-container-item-content', 'custom_pages_template_container_content_item'); + $this->safeAddColumn('custom_pages_template_container_content_item', 'element_content_id', $this->integer()->after('container_content_id')); + $this->migrateElements( + 'custom_pages_template_container_content', + 'Container', + [], + false, + 'custom_pages_template_container_content_definition', + [ + 'allow_multiple', + 'is_inline', + 'templates' => [ + 'table' => 'custom_pages_template_container_content_template', + 'id' => 'definition_id', + 'column' => 'template_id', + ], + ], + [ + 'custom_pages_template_container_content_item' => [ + 'old' => 'container_content_id', + 'new' => 'element_content_id', + ], + ], + ); + $this->safeDropColumn('custom_pages_template_container_content_item', 'container_content_id'); + $this->safeAddForeignKey('fk-tmpl-container-item-element-content', 'custom_pages_template_container_content_item', 'element_content_id', 'custom_pages_template_element_content', 'id', 'CASCADE'); + $this->safeDropTable('custom_pages_template_container_content_template'); + $this->safeDropTable('custom_pages_template_container_content'); + $this->safeDropTable('custom_pages_template_container_content_definition'); + $this->update( + 'custom_pages_template_owner_content', + ['owner_model' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\ContainerItem'], + ['owner_model' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContentItem'], + ); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + echo "m241220_101915_template_elements cannot be reverted.\n"; + + return false; + } + + private function migrateElements(string $oldTable, string $type, array $dynAttributes, bool $deleteOldTable = true, ?string $oldDefinitionTable = null, ?array $definitionDynAttributes = null, ?array $updateLinkedElementContentTables = null) + { + $oldContentType = 'humhub\\modules\\custom_pages\\modules\\template\\models\\' . $type . 'Content'; + $newContentType = 'humhub\\modules\\custom_pages\\modules\\template\\elements\\' . $type . 'Element'; + + $elements = (new Query()) + ->select('ot.*, e.id AS elementId, oc.id AS ownerContentId') + ->from($oldTable . ' AS ot') + ->innerJoin('custom_pages_template_owner_content AS oc', 'ot.id = oc.content_id AND oc.content_type = :contentType', ['contentType' => $oldContentType]) + ->innerJoin('custom_pages_template_element AS e', 'e.content_type = oc.content_type AND e.name = oc.element_name'); + + // Map between old and new definition Ids; Key - old, Value - new. + $definitionIds = []; + + foreach ($elements->each() as $element) { + $definitionId = null; + if (!empty($element['definition_id'])) { + if (!isset($definitionIds[$element['definition_id']])) { + $definitionIds[$element['definition_id']] = $this->migrateDefinition($element['definition_id'], $oldDefinitionTable, $definitionDynAttributes); + } + $definitionId = $definitionIds[$element['definition_id']]; + } + + $dynValues = []; + foreach ($dynAttributes as $attrKey => $attrName) { + if ($attrName === 'jsonMerge') { + if (!empty($element[$attrKey])) { + $jsonValues = @json_decode($element[$attrKey], true); + if (is_array($jsonValues)) { + $dynValues = array_merge($dynValues, $jsonValues); + } + } + } elseif (isset($element[$attrName])) { + $dynValues[$attrName] = $element[$attrName]; + } + } + + $this->insertSilent('custom_pages_template_element_content', [ + 'element_id' => $element['elementId'], + 'dynAttributes' => empty($dynValues) ? null : json_encode($dynValues), + 'definition_id' => $definitionId, + ]); + $newElementId = $this->db->getLastInsertID(); + + $this->updateSilent( + 'custom_pages_template_owner_content', + ['content_type' => $newContentType, 'content_id' => $newElementId], + ['id' => $element['ownerContentId']], + ); + + $this->updateSilent( + 'custom_pages_template_element', + ['content_type' => $newContentType], + ['content_type' => $oldContentType], + ); + + $this->updateSilent( + 'file', + ['object_model' => $newContentType, 'object_id' => $newElementId], + ['object_model' => $oldContentType, 'object_id' => $element['id']], + ); + + $this->updateLinkedElementContentTables($element['id'], $newElementId, $updateLinkedElementContentTables); + } + + if ($deleteOldTable) { + $this->safeDropTable($oldTable); + if ($oldDefinitionTable !== null) { + $this->safeDropTable($oldDefinitionTable); + } + } + } + + private function migrateDefinition(?int $definition_id, ?string $oldDefinitionTable = null, ?array $definitionDynAttributes = null): ?int + { + if ($oldDefinitionTable === null || $definitionDynAttributes === null || empty($definition_id)) { + return null; + } + + $definition = (new Query()) + ->select('*') + ->from($oldDefinitionTable) + ->where(['id' => $definition_id]) + ->one(); + + if (!$definition) { + return null; + } + + $definitionDynValues = []; + foreach ($definitionDynAttributes as $attrKey => $attrName) { + if (is_array($attrName)) { + $definitionDynValues[$attrKey] = (new Query()) + ->select($attrName['column']) + ->from($attrName['table']) + ->where([$attrName['id'] => $definition_id]) + ->column(); + } elseif (isset($definition[$attrName])) { + $definitionDynValues[$attrName] = $definition[$attrName]; + } + } + + $this->insertSilent('custom_pages_template_element_content_definition', [ + 'dynAttributes' => empty($definitionDynValues) ? null : json_encode($definitionDynValues), + 'is_default' => $definition['is_default'], + ]); + + return $this->db->getLastInsertID(); + } + + private function updateLinkedElementContentTables($oldElementContentId, $newElementContentId, ?array $tables = null) + { + if (!is_array($tables) || empty($oldElementContentId) || empty($newElementContentId)) { + return; + } + + foreach ($tables as $table => $columns) { + $this->updateSilent( + $table, + [$columns['new'] => $newElementContentId], + [$columns['old'] => $oldElementContentId], + ); + } + } +} diff --git a/migrations/uninstall.php b/migrations/uninstall.php index 2cb1bee4..0dc9e105 100644 --- a/migrations/uninstall.php +++ b/migrations/uninstall.php @@ -7,25 +7,13 @@ class uninstall extends Migration public function up() { $this->safeDropTable('custom_pages_template_container_content_item'); - $this->safeDropTable('custom_pages_template_container_content_template'); - $this->safeDropTable('custom_pages_template_file_content'); - $this->safeDropTable('custom_pages_template_container_content'); - $this->safeDropTable('custom_pages_template_image_content'); - $this->safeDropTable('custom_pages_template_hh_richtext_content'); - $this->safeDropTable('custom_pages_template_richtext_content'); - $this->safeDropTable('custom_pages_template_text_content'); - $this->safeDropTable('custom_pages_template_file_download_content'); $this->safeDropTable('custom_pages_template_element'); + $this->safeDropTable('custom_pages_template_element_content'); + $this->safeDropTable('custom_pages_template_element_content_definition'); $this->safeDropTable('custom_pages_template_owner_content'); $this->safeDropTable('custom_pages_template_instance'); - $this->safeDropTable('custom_pages_template_container_content_definition'); - $this->safeDropTable('custom_pages_template_image_content_definition'); $this->safeDropTable('custom_pages_template'); - $this->safeDropTable('custom_pages_page'); - $this->safeDropTable('custom_pages_snippet'); - $this->safeDropTable('custom_pages_container_page'); - $this->safeDropTable('custom_pages_container_snippet'); } public function down() diff --git a/models/CustomPage.php b/models/CustomPage.php index 31822dc1..8c39dd88 100644 --- a/models/CustomPage.php +++ b/models/CustomPage.php @@ -313,7 +313,7 @@ public function afterSave($insert, $changedAttributes) parent::afterSave($insert, $changedAttributes); if (!$this->getContentType()->afterSave($this, $insert, $changedAttributes)) { - throw new LogicException('Could not save content type' . $this->getContentType()->getLabel()); + throw new LogicException('Could not save content type ' . $this->getContentType()->getLabel()); } if ($this->checkAbstract()) { diff --git a/modules/template/components/ActiveRecordDynamicAttributes.php b/modules/template/components/ActiveRecordDynamicAttributes.php new file mode 100644 index 00000000..f5027b3b --- /dev/null +++ b/modules/template/components/ActiveRecordDynamicAttributes.php @@ -0,0 +1,106 @@ +hasDynamicAttribute($name)) { + return $this->dynAttributes[$name] ?? $this->getDynamicAttributeDefaultValue($name); + } + + $value = parent::__get($name); + + if ($name === 'dynAttributes' && !is_array($value)) { + $value = empty($value) ? [] : json_decode($value, true); + $this->setAttribute($name, $value); + } + + return $value; + } + + /** + * @inheritdoc + */ + public function __set($name, $value) + { + if ($this->hasDynamicAttribute($name)) { + $attrs = $this->dynAttributes; + $attrs[$name] = $value; + $this->setAttribute('dynAttributes', $attrs); + } else { + parent::__set($name, $value); + } + } + + /** + * @inheritdoc + * @noinspection PhpMissingReturnTypeInspection + */ + public function beforeSave($insert) + { + if (parent::beforeSave($insert)) { + $this->dynAttributes = is_array($this->dynAttributes) && ! empty($this->dynAttributes) + ? json_encode($this->dynAttributes) + : null; + return true; + } + + return false; + } + + /** + * @inheritdoc + */ + public function createValidators() + { + $validators = parent::createValidators(); + $validators->append(Validator::createValidator('safe', $this, 'dynAttributes')); + + return $validators; + } + + private function hasDynamicAttribute(string $name): bool + { + return array_key_exists($name, $this->getDynamicAttributes()); + } + + private function getDynamicAttributeDefaultValue(string $name): mixed + { + return $this->getDynamicAttributes()[$name] ?? null; + } + +} diff --git a/modules/template/components/TemplateCache.php b/modules/template/components/TemplateCache.php index 9fcf505b..04ec910a 100644 --- a/modules/template/components/TemplateCache.php +++ b/modules/template/components/TemplateCache.php @@ -8,7 +8,7 @@ namespace humhub\modules\custom_pages\modules\template\components; -use humhub\modules\custom_pages\modules\template\models\ContainerContentItem; +use humhub\modules\custom_pages\modules\template\elements\ContainerItem; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\TemplateInstance; use Yii; @@ -29,7 +29,7 @@ public static function flushByTemplateId($templateId) self::flushByTemplateInstance($templateInstance); } - foreach (ContainerContentItem::findByTemplateId($templateId)->all() as $containerItem) { + foreach (ContainerItem::findByTemplateId($templateId)->all() as $containerItem) { $ownerContent = OwnerContent::findByContent($containerItem->container); self::flushByOwnerContent($ownerContent); } @@ -48,7 +48,7 @@ public static function flushByOwnerContent(OwnerContent $ownerContent) while (!$owner instanceof TemplateInstance) { $owner = $ownerContent->owner; - if ($owner instanceof ContainerContentItem) { + if ($owner instanceof ContainerItem) { $ownerContent = OwnerContent::findByContent($owner->container); } elseif (!$owner instanceof TemplateInstance) { // Just to avoid infinity loops in case of invalid data. diff --git a/modules/template/controllers/AdminController.php b/modules/template/controllers/AdminController.php index f8e17f30..c596a5db 100644 --- a/modules/template/controllers/AdminController.php +++ b/modules/template/controllers/AdminController.php @@ -8,24 +8,24 @@ namespace humhub\modules\custom_pages\modules\template\controllers; -use humhub\modules\custom_pages\modules\template\models\ContainerContent; -use humhub\modules\custom_pages\modules\template\models\FileContent; -use humhub\modules\custom_pages\modules\template\models\FileDownloadContent; +use humhub\modules\custom_pages\modules\template\elements\ContainerElement; +use humhub\modules\custom_pages\modules\template\elements\FileDownloadElement; +use humhub\modules\custom_pages\modules\template\elements\FileElement; +use humhub\modules\custom_pages\modules\template\elements\HumHubRichtextElement; +use humhub\modules\custom_pages\modules\template\elements\ImageElement; +use humhub\modules\custom_pages\modules\template\elements\RichtextElement; +use humhub\modules\custom_pages\modules\template\elements\RssElement; +use humhub\modules\custom_pages\modules\template\elements\SpaceElement; +use humhub\modules\custom_pages\modules\template\elements\SpacesElement; +use humhub\modules\custom_pages\modules\template\elements\TextElement; +use humhub\modules\custom_pages\modules\template\elements\UserElement; +use humhub\modules\custom_pages\modules\template\elements\UsersElement; use humhub\modules\custom_pages\modules\template\models\forms\AddElementForm; use humhub\modules\custom_pages\modules\template\models\forms\EditElementForm; use humhub\modules\custom_pages\modules\template\models\forms\ImportForm; -use humhub\modules\custom_pages\modules\template\models\HumHubRichtextContent; -use humhub\modules\custom_pages\modules\template\models\ImageContent; -use humhub\modules\custom_pages\modules\template\models\RichtextContent; -use humhub\modules\custom_pages\modules\template\models\RssContent; -use humhub\modules\custom_pages\modules\template\models\SpaceContent; -use humhub\modules\custom_pages\modules\template\models\SpacesContent; use humhub\modules\custom_pages\modules\template\models\TemplateSearch; -use humhub\modules\custom_pages\modules\template\models\TextContent; use humhub\modules\custom_pages\modules\template\models\Template; use humhub\modules\custom_pages\modules\template\models\TemplateElement; -use humhub\modules\custom_pages\modules\template\models\UserContent; -use humhub\modules\custom_pages\modules\template\models\UsersContent; use humhub\modules\custom_pages\modules\template\services\ExportService; use humhub\modules\custom_pages\modules\template\widgets\TemplateElementAdminRow; use humhub\modules\custom_pages\modules\template\widgets\EditElementModal; @@ -161,21 +161,21 @@ public function actionEditUsage() * Returns a selection of all available template content types. * @return array */ - private function getContentTypes() + private function getContentTypes(): array { return [ - TextContent::$label => TextContent::class, - RichtextContent::$label => RichtextContent::class, - HumHubRichtextContent::$label => HumHubRichtextContent::class, - ImageContent::$label => ImageContent::class, - FileContent::$label => FileContent::class, - FileDownloadContent::$label => FileDownloadContent::class, - ContainerContent::$label => ContainerContent::class, - RssContent::$label => RssContent::class, - UserContent::$label => UserContent::class, - SpaceContent::$label => SpaceContent::class, - UsersContent::$label => UsersContent::class, - SpacesContent::$label => SpacesContent::class, + TextElement::class, + RichtextElement::class, + HumHubRichtextElement::class, + ImageElement::class, + FileElement::class, + FileDownloadElement::class, + ContainerElement::class, + RssElement::class, + UserElement::class, + SpaceElement::class, + UsersElement::class, + SpacesElement::class, ]; } diff --git a/modules/template/controllers/ContainerContentController.php b/modules/template/controllers/ContainerContentController.php index e16c5811..8c7b49cf 100644 --- a/modules/template/controllers/ContainerContentController.php +++ b/modules/template/controllers/ContainerContentController.php @@ -2,23 +2,25 @@ namespace humhub\modules\custom_pages\modules\template\controllers; +use humhub\components\Controller; +use humhub\modules\custom_pages\modules\template\elements\ContainerElement; +use humhub\modules\custom_pages\modules\template\elements\ContainerItem; use Yii; use yii\helpers\Url; use humhub\modules\custom_pages\modules\template\models\forms\AddItemEditForm; use humhub\modules\custom_pages\modules\template\widgets\EditContainerItemModal; use humhub\modules\custom_pages\modules\template\models\forms\EditItemForm; -use humhub\modules\custom_pages\modules\template\models\ContainerContentItem; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\OwnerContentVariable; use humhub\modules\custom_pages\modules\template\models\Template; use humhub\modules\custom_pages\modules\template\components\TemplateCache; /** - * This controller is used to manage ContainerContent and ContainerContentItem instances. + * This controller is used to manage ContainerElement and ContainerItem instances. * * @author buddha */ -class ContainerContentController extends \humhub\components\Controller +class ContainerContentController extends Controller { /** * @inerhitdoc @@ -31,7 +33,7 @@ public function behaviors() } /** - * This action is used for empty ContainerContent instances, which means there is only a default CotnainerContent for the given owner. + * This action is used for empty ContainerElement instances, which means there is only a default ContainerElement for the given owner. * * This action accepts an $ownerContentId, which is the id of the default OwnerContent instance, * and an owner definition ($ownerModel, $ownerId), which defines the actual owner of the element. @@ -68,7 +70,7 @@ public function actionCreateContainer($ownerModel, $ownerId, $ownerContentId, $c } /** - * This action is used to add new ContainerContentItems to an element. + * This action is used to add new ContainerItems to an element. * * This action accepts either an $ownerContentId or an $ownerContent instance of type OwnerContent. * @@ -84,14 +86,16 @@ public function actionAddItem($ownerContentId, $ownerContent = null, $cguid = nu { $ownerContent = (!$ownerContent) ? OwnerContent::findOne(['id' => $ownerContentId]) : $ownerContent; - if (!$ownerContent->instance->canAddItem()) { + /* @var ContainerElement $element */ + $element = $ownerContent->instance; + if (!$element->canAddItem()) { throw new \yii\web\HttpException(403, Yii::t('CustomPagesModule.base', 'This container does not allow any further items!')); } // If the ContentContainerDefinition only allows one specific template, we skip the template selection. - if ($ownerContent->instance->isSingleAllowedTemplate()) { + if ($element->isSingleAllowedTemplate()) { return $this->runAction('edit-add-item', [ - 'templateId' => $ownerContent->instance->allowedTemplates[0]->id, + 'templateId' => $element->allowedTemplates[0]->id, 'ownerContent' => $ownerContent, 'cguid' => $cguid, ]); @@ -99,7 +103,7 @@ public function actionAddItem($ownerContentId, $ownerContent = null, $cguid = nu return $this->asJson([ 'output' => $this->renderAjax('addItemChooseTemplateModal', [ - 'allowedTemplateSelection' => $this->getAllowedTemplateSelection($ownerContent->instance), + 'allowedTemplateSelection' => $this->getAllowedTemplateSelection($element), 'action' => Url::to(['edit-add-item', 'ownerContentId' => $ownerContentId, 'cguid' => $cguid]), ]), ]); @@ -107,15 +111,15 @@ public function actionAddItem($ownerContentId, $ownerContent = null, $cguid = nu /** * Creates a selection array in form of 'template.id' => 'template.name' for all allowed Templates of the - * given ContainerContent instance. + * given ContainerElement instance. * - * @param \humhub\modules\custom_pages\modules\template\models\ContainerContent $containerContent + * @param ContainerElement $containerElementContent * @return array */ - protected function getAllowedTemplateSelection($containerContent) + protected function getAllowedTemplateSelection($containerElementContent) { $result = []; - foreach ($containerContent->definition->allowedTemplates as $allowedTemplate) { + foreach ($containerElementContent->definition->allowedTemplates as $allowedTemplate) { $result[$allowedTemplate->id] = $allowedTemplate->name; } @@ -224,7 +228,7 @@ public function actionDeleteItem() $itemId = Yii::$app->request->post('itemId'); $ownerContentId = Yii::$app->request->post('ownerContentId'); - ContainerContentItem::findOne(['id' => $itemId])->delete(); + ContainerItem::findOne(['id' => $itemId])->delete(); $ownerContent = OwnerContent::findOne(['id' => $ownerContentId]); $variable = new OwnerContentVariable(['ownerContent' => $ownerContent]); diff --git a/modules/template/models/ContentContainerContent.php b/modules/template/elements/BaseContentContainerElement.php similarity index 54% rename from modules/template/models/ContentContainerContent.php rename to modules/template/elements/BaseContentContainerElement.php index 0848feae..9a90f46a 100644 --- a/modules/template/models/ContentContainerContent.php +++ b/modules/template/elements/BaseContentContainerElement.php @@ -6,22 +6,20 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\libs\Html; use humhub\modules\content\components\ContentContainerActiveRecord; use humhub\modules\content\models\ContentContainer; -use humhub\modules\custom_pages\modules\template\widgets\TemplateContentFormFields; use yii\db\IntegrityException; -use yii\helpers\ArrayHelper; /** - * Class ContentContainerContent + * Abstract class to manage content records of the User/Space elements * + * Dynamic attributes: * @property string $guid - * @property string $class */ -abstract class ContentContainerContent extends TemplateContentActiveRecord +abstract class BaseContentContainerElement extends BaseTemplateElementContent { public const CONTAINER_CLASS = null; @@ -30,9 +28,11 @@ abstract class ContentContainerContent extends TemplateContentActiveRecord /** * @inheritdoc */ - public static function tableName() + protected function getDynamicAttributes(): array { - return 'custom_pages_template_contentcontainer_content'; + return [ + 'guid' => null, + ]; } /** @@ -40,41 +40,9 @@ public static function tableName() */ public function rules() { - return array_merge(parent::rules(), [ - [['guid', 'class'], 'string'], - [['class'], 'required'], - ]); - } - - /** - * @inheritdoc - */ - public function scenarios() - { - return ArrayHelper::merge(parent::scenarios(), [ - self::SCENARIO_CREATE => $attributes = ['guid', 'class'], - self::SCENARIO_EDIT_ADMIN => $attributes, - self::SCENARIO_EDIT => $attributes, - ]); - } - - /** - * @inheritdoc - */ - public function getLabel() - { - return static::$label; - } - - /** - * @inheritdoc - */ - public function copy() - { - $clone = new static(); - $clone->guid = $this->guid; - $clone->class = $this->class; - return $clone; + return [ + [['guid'], 'string'], + ]; } /** @@ -96,13 +64,9 @@ public function renderEmpty($options = []) /** * @inheritdoc */ - public function renderForm($form) + public function getFormView(): string { - return TemplateContentFormFields::widget([ - 'type' => strtolower(substr(strrchr(static::CONTAINER_CLASS, '\\'), 1)), - 'form' => $form, - 'model' => $this, - ]); + return strtolower(substr(strrchr(static::CONTAINER_CLASS, '\\'), 1)); } /** @@ -122,7 +86,6 @@ public function setAttributes($values, $safeOnly = true) */ public function beforeValidate() { - $this->class = static::CONTAINER_CLASS; return parent::beforeValidate(); } diff --git a/modules/template/elements/BaseRecordsElement.php b/modules/template/elements/BaseRecordsElement.php new file mode 100644 index 00000000..3611253f --- /dev/null +++ b/modules/template/elements/BaseRecordsElement.php @@ -0,0 +1,152 @@ + null, + 'static' => null, + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'type' => Yii::t('CustomPagesModule.template', 'Type'), + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['type'], 'in', 'range' => array_keys($this->getTypes())], + [['static'], 'safe'], + ]; + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + return Html::encode(static::RECORD_CLASS); + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + return ''; + } + + /** + * @inheritdoc + */ + public function getFormView(): string + { + return 'records'; + } + + /** + * Get types for the records list + * + * @return array + */ + public function getTypes(): array + { + return [ + 'static' => Yii::t('CustomPagesModule.template', 'Static list'), + ]; + } + + /** + * @inheritdoc + */ + public function getItems(): iterable + { + if ($this->records === null) { + if (!$this->isConfigured()) { + // No need to touch DB because option is not configured for the current type + $this->records = []; + } else { + // Get records from DB + $query = $this->getQuery(); + + if ($this->type !== 'static' && !empty($this->limit)) { + // Limit only dynamic list + $query->limit($this->limit); + } + + $this->records = $query->all(); + } + } + + yield from $this->records; + } + + protected function filterStatic(ActiveQuery $query): ActiveQuery + { + return $query->andWhere(['guid' => $this->static]); + } + + /** + * Check if the Element is properly configured + * + * @return bool + */ + protected function isConfigured(): bool + { + return !empty($this->{$this->type}); + } +} diff --git a/modules/template/models/TemplateContentActiveRecord.php b/modules/template/elements/BaseTemplateElementContent.php similarity index 71% rename from modules/template/models/TemplateContentActiveRecord.php rename to modules/template/elements/BaseTemplateElementContent.php index 3c840369..3b103769 100644 --- a/modules/template/models/TemplateContentActiveRecord.php +++ b/modules/template/elements/BaseTemplateElementContent.php @@ -1,41 +1,58 @@ innerJoin( + TemplateElement::tableName(), + self::tableName() . '.element_id = ' . TemplateElement::tableName() . '.id AND ' . + TemplateElement::tableName() . '.content_type = :contentType', + ['contentType' => static::class], + ); + } + + /** + * Copies the values of this content type instance. + * This function can initiate the copy by using `createCopy`. + * + * @see static::createCopy() + * @return static instance copy. + */ + public function copy(): static + { + $clone = new static(); + $clone->element_id = $this->element_id; + $clone->dynAttributes = $this->dynAttributes; + $clone->definition_id = $this->definition_id; + return $clone; + } /** * @return bool determines if the content instance has currently an attribute set. */ - public function hasValues() + public function hasValues(): bool { - $result = false; foreach ($this->attributes() as $key) { if ($this->getAttribute($key) != null && $key != 'id') { - $result = true; - break; + return true; } } - return $result; + + return false; } /** - * Creates an empty copy of the current conten tpye and adopts the definition_id (if present). - * @return TemplateContentActiveRecord + * Creates an empty copy of the current content type and adopts the definition_id (if present). + * @return static */ - protected function createCopy() + protected function createCopy(): static { $copy = Yii::createObject(get_class($this)); if ($this->isDefinitionContent()) { @@ -117,18 +157,21 @@ protected function createCopy() */ public function scenarios() { + $attributes = array_merge( + ['fileList', 'definitionPostData'], + array_keys($this->getDynamicAttributes()), + ); + return [ - self::SCENARIO_DEFAULT => ['fileList', 'definitionPostData'], - self::SCENARIO_CREATE => ['fileList', 'definitionPostData'], - self::SCENARIO_EDIT_ADMIN => ['fileList', 'definitionPostData'], - self::SCENARIO_EDIT => ['fileList', 'definitionPostData'], + self::SCENARIO_DEFAULT => $attributes, + self::SCENARIO_CREATE => $attributes, + self::SCENARIO_EDIT_ADMIN => $attributes, + self::SCENARIO_EDIT => $attributes, ]; } /** - * Loads the given $data and also loads the definition if $definitionPostData is set. - * @param type $data - * @param type $formName + * @inheritdoc */ public function load($data, $formName = null) { @@ -161,16 +204,16 @@ public function formName() } /** - * Returns the ContainerContentDefinition instance of this instance. + + * Returns the BaseTemplateElementContentDefinition instance of this instance. + * This function will create an empty definition instance if this content type has an definitionModel and - * does not have an related definition_id. + * does not have a related definition_id. * - * @return ContainerContentDefinition the definition instance. + * @return BaseTemplateElementContentDefinition|null the definition instance. */ - public function getDefinition() + public function getDefinition(): ?BaseTemplateElementContentDefinition { if (!$this->isDefinitionContent()) { - return; + return null; } if ($this->definitionInstance) { @@ -260,7 +303,7 @@ public function saveFiles() */ public function afterDelete() { - if ($this instanceof ContainerContent) { + if ($this instanceof ContainerElement) { if (self::find()->where(['definition_id' => $this->definition_id])->count() == 0) { $this->definition->delete(); } @@ -281,7 +324,7 @@ protected function wrap($type, $content, $options = [], $attributes = []) $options['template-content-id'] = $this->getPrimaryKey(); } - return \humhub\modules\custom_pages\modules\template\widgets\TemplateEditorElement::widget([ + return TemplateEditorElement::widget([ 'container' => $type, 'templateContent' => $this, 'content' => $content, @@ -328,6 +371,11 @@ public function getOption($options, $key, $default = null) return isset($options[$key]) ? strval($options[$key]) : $default; } + /** + * Check if the Element is empty + * + * @return bool + */ public function isEmpty(): bool { return false; @@ -349,7 +397,7 @@ public function getPage(): ?CustomPage { $ownerModel = $this->getOwner(); - if ($ownerModel instanceof ContainerContentItem) { + if ($ownerModel instanceof ContainerItem) { $ownerModel = $ownerModel->getTemplateInstance(); } @@ -384,6 +432,15 @@ public function canView($user = null): bool return false; } + /** + * @param null $user + * @return bool + */ + public function canEdit($user = null): bool + { + return PagePermission::canEdit(); + } + /** * @return bool False - if the content has a dynamic content, and it must not be cached */ @@ -391,4 +448,14 @@ public function isCacheable(): bool { return true; } + + /** + * Get a view file name to render a form with fields for this Element Content + * + * @return string + */ + public function getFormView(): string + { + return lcfirst(substr(strrchr(static::class, '\\'), 1, -7)); + } } diff --git a/modules/template/elements/BaseTemplateElementContentDefinition.php b/modules/template/elements/BaseTemplateElementContentDefinition.php new file mode 100644 index 00000000..a89c2e42 --- /dev/null +++ b/modules/template/elements/BaseTemplateElementContentDefinition.php @@ -0,0 +1,53 @@ +formName = $formName; + } + + public function hasValues(): bool + { + foreach ($this->attributes() as $key) { + if ($this->getAttribute($key) !== null && $key !== 'id' && $key !== 'is_default') { + return true; + } + } + + return false; + } + + /** + * @inheritdoc + */ + public function formName() + { + return $this->formName ?? parent::formName(); + } +} diff --git a/modules/template/elements/ContainerDefinition.php b/modules/template/elements/ContainerDefinition.php new file mode 100644 index 00000000..9f06b307 --- /dev/null +++ b/modules/template/elements/ContainerDefinition.php @@ -0,0 +1,96 @@ + null, + 'allow_multiple' => null, + 'is_inline' => null, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['templates'], 'safe'], + [['allow_multiple', 'is_inline'], 'boolean'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'templates' => Yii::t('CustomPagesModule.template', 'Allowed Templates'), + 'allow_multiple' => Yii::t('CustomPagesModule.template', 'Allow multiple items?'), + 'is_inline' => Yii::t('CustomPagesModule.template', 'Render items as inline-blocks within the inline editor?'), + ]; + } + + /** + * @inheritdoc + */ + public function attributeHints() + { + return [ + 'templates' => Yii::t('CustomPagesModule.base', 'An empty allowed template selection will allow all container templates for this container.'), + ]; + } + + /** + * @return Template[] + */ + public function getAllowedTemplates(): array + { + if (empty($this->_templates)) { + $condition = ['type' => Template::TYPE_CONTAINER]; + if (!empty($this->templates)) { + $condition['id'] = $this->templates; + } + $this->_templates = Template::findAll($condition); + } + + return $this->_templates; + } + + public function isSingleAllowedTemplate(): bool + { + return count($this->templates) === 1; + } +} diff --git a/modules/template/models/ContainerContent.php b/modules/template/elements/ContainerElement.php similarity index 50% rename from modules/template/models/ContainerContent.php rename to modules/template/elements/ContainerElement.php index c0528caa..844bda5c 100644 --- a/modules/template/models/ContainerContent.php +++ b/modules/template/elements/ContainerElement.php @@ -1,63 +1,63 @@ definitionModel = ContainerContentDefinition::class; + return Yii::t('CustomPagesModule.template', 'Container'); } /** - * @return string the associated database table name + * @inheritdoc */ - public static function tableName() - { - return 'custom_pages_template_container_content'; - } + public $definitionModel = ContainerDefinition::class; - public function validate($attributeNames = null, $clearErrors = true) + /** + * @inheritdoc + */ + protected function getDynamicAttributes(): array { - return parent::validate() && $this->definition->validate(); + return []; } - public function getAllowedTemplates() + /** + * @inheritdoc + */ + public function validate($attributeNames = null, $clearErrors = true) { - if (empty($this->definition->templates)) { - return Template::findAllByType(Template::TYPE_CONTAINER); - } - return $this->definition->templates; + return parent::validate($attributeNames, $clearErrors) && $this->definition->validate(); } - public function afterSave($insert, $changedAttributes) + public function getAllowedTemplates(): array { - parent::afterSave($insert, $changedAttributes); - - if (!empty($this->allowedTemplateSelection)) { - ContainerContentTemplate::deleteAll(['container_content_id' => $this->id]); - foreach ($this->allowedTemplateSelection as $allowedTemplateId) { - $allowedTemplate = new ContainerContentTemplate(); - $allowedTemplate->template_id = $allowedTemplateId; - $allowedTemplate->container_content_id = $this->id; - $allowedTemplate->save(); - } - } + return $this->definition->allowedTemplates; } + /** + * @inheritdoc + */ public function beforeDelete() { if ($this->hasItems()) { @@ -69,20 +69,11 @@ public function beforeDelete() return parent::beforeDelete(); } - public function getLabel() - { - return self::$label; - } - - public function copy() - { - // We do not have to set additional attributes here - return $this->createCopy(); - } - + /** + * @inheritdoc + */ public function render($options = []) { - $items = $this->items; $result = ''; @@ -105,6 +96,9 @@ public function render($options = []) } } + /** + * @inheritdoc + */ public function renderEmpty($options = []) { $options['jsWidget'] = 'custom_pages.template.TemplateContainer'; @@ -118,11 +112,11 @@ public function addContainerItem($templateId, $index = null) { $index = ($index == null) ? $this->getNextIndex() : $index; - ContainerContentItem::incrementIndex($this->id, $index); + ContainerItem::incrementIndex($this->id, $index); - $item = new ContainerContentItem(); + $item = new ContainerItem(); $item->template_id = $templateId; - $item->container_content_id = $this->id; + $item->element_content_id = $this->id; $item->sort_order = $index; $item->save(); @@ -131,9 +125,9 @@ public function addContainerItem($templateId, $index = null) public function moveItem($itemId, $step) { - $item = ContainerContentItem::findOne(['id' => $itemId]); + $item = ContainerItem::findOne(['id' => $itemId]); - if ($item == null || $item->container_content_id != $this->id) { + if ($item == null || $item->element_content_id != $this->id) { return; } @@ -145,7 +139,7 @@ public function moveItem($itemId, $step) $newIndex = $oldIndex + $step; $item->sort_order = ($newIndex < $nextIndex) ? $newIndex : ($nextIndex - 1); - ContainerContentItem::decrementBetween($this->id, $oldIndex, $item->sort_order); + ContainerItem::decrementBetween($this->id, $oldIndex, $item->sort_order); $item->save(); } elseif ($step < 0 && $item->sort_order != 0) { @@ -153,7 +147,7 @@ public function moveItem($itemId, $step) $newIndex = $oldIndex + $step; $item->sort_order = ($newIndex > 0) ? $newIndex : 0; - ContainerContentItem::incrementBetween($this->id, $item->sort_order, $oldIndex); + ContainerItem::incrementBetween($this->id, $item->sort_order, $oldIndex); $item->save(); } @@ -163,9 +157,9 @@ public function createEmptyItem($templateId, $index = null) { $index = ($index == null) ? $this->getNextIndex() : $index; - $item = new ContainerContentItem(); + $item = new ContainerItem(); $item->template_id = $templateId; - $item->container_content_id = $this->id; + $item->element_content_id = $this->id; $item->sort_order = $index; return $item; } @@ -175,38 +169,29 @@ public function getNextIndex() return $this->getItems()->count(); } - public function hasItems() + public function hasItems(): bool { return $this->getItems()->count() > 0; } - public function getItems() + public function getItems(): ActiveQuery { - return $this->hasMany(ContainerContentItem::class, ['container_content_id' => 'id'])->orderBy('sort_order ASC'); + return $this->hasMany(ContainerItem::class, ['element_content_id' => 'id'])->orderBy('sort_order ASC'); } - public function canAddItem() + public function canAddItem(): bool { - return $this->definition == null || $this->definition->allow_multiple || !$this->hasItems(); + return $this->definition === null || $this->definition->allow_multiple || !$this->hasItems(); } - public function getTemplates() + public function getTemplates(): array { - return $this->definition->templates; + return $this->definition->allowedTemplates; } - public function isSingleAllowedTemplate() + public function isSingleAllowedTemplate(): bool { - return count($this->templates) === 1; - } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'container', - 'form' => $form, - 'model' => $this, - ]); + return $this->definition->isSingleAllowedTemplate(); } /** diff --git a/modules/template/models/ContainerContentItem.php b/modules/template/elements/ContainerItem.php similarity index 59% rename from modules/template/models/ContainerContentItem.php rename to modules/template/elements/ContainerItem.php index 80357351..737bab34 100644 --- a/modules/template/models/ContainerContentItem.php +++ b/modules/template/elements/ContainerItem.php @@ -1,23 +1,36 @@ 1], ['and', ['>=', 'sort_order', $index], ['container_content_id' => $cotnainerId]]); + self::updateAllCounters(['sort_order' => 1], ['and', ['>=', 'sort_order', $index], ['element_content_id' => $containerId]]); } - public static function incrementBetween($cotnainerId, $start, $end) + public static function incrementBetween($containerId, $start, $end) { - self::updateAllCounters(['sort_order' => 1], ['and', ['>=', 'sort_order', $start], ['<=', 'sort_order', $end], ['container_content_id' => $cotnainerId]]); + self::updateAllCounters(['sort_order' => 1], ['and', ['>=', 'sort_order', $start], ['<=', 'sort_order', $end], ['element_content_id' => $containerId]]); } - public static function decrementIndex($cotnainerId, $index) + public static function decrementIndex($containerId, $index) { - self::updateAllCounters(['sort_order' => -1], ['and', ['<=', 'sort_order', $index], ['container_content_id' => $cotnainerId]]); + self::updateAllCounters(['sort_order' => -1], ['and', ['<=', 'sort_order', $index], ['element_content_id' => $containerId]]); } - public static function decrementBetween($cotnainerId, $start, $end) + public static function decrementBetween($containerId, $start, $end) { - self::updateAllCounters(['sort_order' => -1], ['and', ['>=', 'sort_order', $start], ['<=', 'sort_order', $end], ['container_content_id' => $cotnainerId]]); + self::updateAllCounters(['sort_order' => -1], ['and', ['>=', 'sort_order', $start], ['<=', 'sort_order', $end], ['element_content_id' => $containerId]]); } public function getTemplate() @@ -72,7 +85,7 @@ public function getTemplate() public function getContainer() { - return $this->hasOne(ContainerContent::class, ['id' => 'container_content_id']); + return $this->hasOne(ContainerElement::class, ['id' => 'element_content_id']); } public function render($editMode, $inline = false) @@ -95,8 +108,8 @@ public function wrap($content, $inline) 'data-template-item' => $this->id, 'data-template-edit-url' => Url::to(['/custom_pages/template/container-admin/edit-source', 'id' => $this->template_id]), 'data-template-item-title' => $this->title, - 'data-template-owner' => ContainerContent::class, - 'data-template-owner-id' => $this->container_content_id, + 'data-template-owner' => ContainerElement::class, + 'data-template-owner-id' => $this->element_content_id, ], ]); } @@ -114,7 +127,7 @@ public static function findByTemplateId($templateId) public function getTemplateInstance(): ?TemplateInstance { $container = $this->container; - if ($container instanceof ContainerContent) { + if ($container instanceof ContainerElement) { $ownerContent = $container->ownerContent; if ($ownerContent instanceof OwnerContent) { $owner = $ownerContent->getOwner(); diff --git a/modules/template/elements/FileDownloadElement.php b/modules/template/elements/FileDownloadElement.php new file mode 100644 index 00000000..7ee90f09 --- /dev/null +++ b/modules/template/elements/FileDownloadElement.php @@ -0,0 +1,161 @@ + null, + 'title' => null, + 'style' => null, + 'cssClass' => null, + 'showFileinfo' => 1, + 'showIcon' => 1, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['file_guid'], 'required'], + [['title', 'style', 'cssClass'], 'string'], + [['showFileinfo', 'showIcon'], 'integer'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'file_guid' => Yii::t('CustomPagesModule.base', 'File'), + 'title' => Yii::t('CustomPagesModule.base', 'Title'), + 'style' => Yii::t('CustomPagesModule.base', 'Style'), + 'cssClass' => Yii::t('CustomPagesModule.base', 'Css Class'), + 'showFileinfo' => Yii::t('CustomPagesModule.base', 'Show additional file information (size)'), + 'showIcon' => Yii::t('CustomPagesModule.base', 'Add a file icon before the title'), + ]; + } + + /** + * @inheritdoc + */ + public function saveFiles() + { + $files = File::findByRecord($this); + + foreach ($files as $file) { + if ($file->guid !== $this->file_guid) { + $file->delete(); + } + } + + $this->fileManager->attach($this->file_guid); + } + + public function getFile(): ?File + { + return File::findOne(['guid' => $this->file_guid]); + } + + public function hasFile(): bool + { + return $this->file_guid != null && $this->getFile() != null; + } + + public function getUrl(): ?string + { + $file = $this->getFile(); + return ($file != null) ? $file->getUrl() : null; + } + + public function getDownloadUrl(): ?string + { + $file = $this->getFile(); + return $file ? Url::to(['/file/file/download', 'guid' => $file->guid]) : null; + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + if (!$this->hasFile()) { + return ''; + } + + $file = $this->getFile(); + $options['htmlOptions'] = [ + 'href' => $this->getDownloadUrl(), + 'style' => Html::encode($this->style), + 'class' => Html::encode($this->cssClass), + 'target' => '_blank', + 'data-pjax-prevent' => '1', + ]; + + $content = ($this->title) ? $this->title : $file->file_name; + $content = Html::encode($content); + + $fileInfo = FileHelper::getFileInfos($file); + + if ($this->showIcon) { + $options['htmlOptions']['class'] .= ' mime ' . $fileInfo['mimeIcon']; + } + + if ($this->showFileinfo) { + $content .= Html::tag('small', ' - ' . $fileInfo['size_format'], ['class' => 'file-fileInfo']); + } + + return $this->isEditMode($options) + ? $this->wrap('a', $content, $options) + : Html::tag('a', $content, $options['htmlOptions']); + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + return ''; + } +} diff --git a/modules/template/elements/FileElement.php b/modules/template/elements/FileElement.php new file mode 100644 index 00000000..d35e91c3 --- /dev/null +++ b/modules/template/elements/FileElement.php @@ -0,0 +1,131 @@ + null, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['file_guid'], 'required'], + [['file_guid'], 'string'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'file_guid' => Yii::t('CustomPagesModule.base', 'File'), + ]; + } + + /** + * @inheritdoc + */ + public function saveFiles() + { + $files = File::findByRecord($this); + + foreach ($files as $file) { + if ($file->guid !== $this->file_guid) { + $file->delete(); + } + } + + $this->fileManager->attach($this->file_guid); + } + + /** + * Get File + * + * @return File|null + */ + public function getFile(): ?File + { + return empty($this->file_guid) ? null : File::findOne(['guid' => $this->file_guid]); + } + + /** + * Check if a File is found for this Element + * + * @return bool + */ + public function hasFile(): bool + { + return $this->getFile() !== null; + } + + /** + * @return string|null + */ + public function getUrl(): ?string + { + return $this->getFile()?->getUrl(); + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + if ($this->hasFile()) { + return $this->getFile()->getUrl(); + } + return ''; + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + return ''; + } + + /** + * @inheritdoc + */ + public function isEmpty(): bool + { + return !$this->hasFile(); + } +} diff --git a/modules/template/elements/HumHubRichtextElement.php b/modules/template/elements/HumHubRichtextElement.php new file mode 100644 index 00000000..0fe5dc3d --- /dev/null +++ b/modules/template/elements/HumHubRichtextElement.php @@ -0,0 +1,87 @@ + null, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + ['content', 'safe'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'content' => Yii::t('CustomPagesModule.template', 'Content'), + ]; + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + if ($this->isEditMode($options)) { + return $this->wrap('div', Richtext::output($this->content), $options); + } + + return Richtext::output($this->content); + } + + /** + * @inheritdoc + */ + public function saveFiles() + { + Richtext::postProcess($this->content, $this); + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty HumHub Richtext'), $options); + } +} diff --git a/modules/template/elements/ImageDefinition.php b/modules/template/elements/ImageDefinition.php new file mode 100644 index 00000000..10a4632b --- /dev/null +++ b/modules/template/elements/ImageDefinition.php @@ -0,0 +1,57 @@ + null, + 'width' => null, + 'height' => null, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['style'], 'string'], + [['height', 'width'], 'integer'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return array_merge(parent::attributeLabels(), [ + 'style' => Yii::t('CustomPagesModule.template', 'Style'), + 'height' => Yii::t('CustomPagesModule.template', 'Height'), + 'width' => Yii::t('CustomPagesModule.template', 'Width'), + ]); + } +} diff --git a/modules/template/models/ImageContent.php b/modules/template/elements/ImageElement.php similarity index 56% rename from modules/template/models/ImageContent.php rename to modules/template/elements/ImageElement.php index 7236480c..0f758a25 100644 --- a/modules/template/models/ImageContent.php +++ b/modules/template/elements/ImageElement.php @@ -1,31 +1,53 @@ definitionModel = ImageContentDefinition::class; + return Yii::t('CustomPagesModule.template', 'Image'); } /** - * @return string the associated database table name + * @inheritdoc + */ + public $definitionModel = ImageDefinition::class; + + /** + * @inheritdoc */ - public static function tableName() + protected function getDynamicAttributes(): array { - return 'custom_pages_template_image_content'; + return array_merge(parent::getDynamicAttributes(), [ + 'alt' => null, + ]); } + /** + * @inheritdoc + */ public function rules() { $result = []; - // We preven the content instance from beeing saved if there is no definition setting, to get sure we have an empty content in this case + // We prevent the content instance from being saved if there is no definition setting, to get sure we have an empty content in this case // TODO: perhaps overwrite the validate method and call parent validate only if no definition is set if ($this->definition == null || !$this->definition->hasValues()) { $result[] = [['file_guid'], 'required']; @@ -34,33 +56,19 @@ public function rules() return $result; } - public function scenarios() - { - $scenarios = parent::scenarios(); - $scenarios[self::SCENARIO_CREATE][] = 'alt'; - $scenarios[self::SCENARIO_EDIT_ADMIN][] = 'alt'; - $scenarios[self::SCENARIO_EDIT][] = 'alt'; - return $scenarios; - } - /** - * @return array customized attribute labels (name=>label) + * @inheritdoc */ public function attributeLabels() { - return [ - 'file_guid' => Yii::t('CustomPagesModule.base', 'File'), + return array_merge(parent::attributeLabels(), [ 'alt' => Yii::t('CustomPagesModule.base', 'Alternate text'), - ]; - } - - public function copy() - { - $clone = parent::copy(); - $clone->alt = $this->alt; - return $clone; + ]); } + /** + * @inheritdoc + */ public function render($options = []) { if ($this->hasFile() != null) { @@ -84,18 +92,11 @@ public function render($options = []) return ''; } + /** + * @inheritdoc + */ public function renderEmpty($options = []) { return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty Image'), $options); } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'image', - 'form' => $form, - 'model' => $this, - ]); - } - } diff --git a/modules/template/elements/RichtextElement.php b/modules/template/elements/RichtextElement.php new file mode 100644 index 00000000..c2bc2b49 --- /dev/null +++ b/modules/template/elements/RichtextElement.php @@ -0,0 +1,78 @@ + null, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + ['content', 'safe'], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'content' => Yii::t('CustomPagesModule.template', 'Content'), + ]; + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + if ($this->isEditMode($options)) { + return $this->wrap('div', $this->purify($this->content), $options); + } + + return $this->purify($this->content); + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty Richtext'), $options); + } +} diff --git a/modules/template/models/RssContent.php b/modules/template/elements/RssElement.php similarity index 77% rename from modules/template/models/RssContent.php rename to modules/template/elements/RssElement.php index 3b69fcc9..a5f5f2b8 100644 --- a/modules/template/models/RssContent.php +++ b/modules/template/elements/RssElement.php @@ -6,49 +6,43 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\libs\Html; -use humhub\modules\custom_pages\modules\template\widgets\TemplateContentFormFields; +use humhub\modules\custom_pages\modules\template\models\TemplateContentIterable; use SimpleXMLElement; use Yii; -use yii\helpers\ArrayHelper; /** - * Class RssContent + * Class to manage content records of the RSS elements * + * Dynamic attributes: * @property string $url * @property int $cache_time * @property int $limit */ -class RssContent extends TemplateContentActiveRecord implements TemplateContentIterable +class RssElement extends BaseTemplateElementContent implements TemplateContentIterable { - public static $label = 'RSS'; - - private SimpleXMLElement|null|false $rssData = null; - /** - * @inheridoc + * @inheritdoc */ - public function init() + public function getLabel(): string { - parent::init(); - - if ($this->cache_time === null) { - $this->cache_time = 3600; - } - - if ($this->limit === null) { - $this->limit = 10; - } + return Yii::t('CustomPagesModule.template', 'Rss'); } + private SimpleXMLElement|null|false $rssData = null; + /** * @inheritdoc */ - public static function tableName() + protected function getDynamicAttributes(): array { - return 'custom_pages_template_rss_content'; + return [ + 'url' => null, + 'cache_time' => 3600, + 'limit' => 10, + ]; } /** @@ -56,24 +50,12 @@ public static function tableName() */ public function rules() { - return array_merge(parent::rules(), [ + return [ [['url'], 'string', 'length' => [1, 1000]], [['url'], 'url'], [['cache_time'], 'integer', 'min' => 0], [['limit'], 'integer', 'min' => 0], - ]); - } - - /** - * @inheritdoc - */ - public function scenarios() - { - return ArrayHelper::merge(parent::scenarios(), [ - self::SCENARIO_CREATE => $attributes = ['url', 'cache_time', 'limit'], - self::SCENARIO_EDIT_ADMIN => $attributes, - self::SCENARIO_EDIT => $attributes, - ]); + ]; } /** @@ -99,26 +81,6 @@ public function attributeHints() ]; } - /** - * @inheritdoc - */ - public function getLabel() - { - return self::$label; - } - - /** - * @inheritdoc - */ - public function copy() - { - $clone = new RssContent(); - $clone->url = $this->url; - $clone->cache_time = $this->cache_time; - $clone->limit = $this->limit; - return $clone; - } - /** * @inheritdoc */ @@ -143,18 +105,6 @@ public function renderEmpty($options = []) return ''; } - /** - * @inheritdoc - */ - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'rss', - 'form' => $form, - 'model' => $this, - ]); - } - private function getRssFileContent(): string { if ($this->isEmpty()) { diff --git a/modules/template/models/SpaceContent.php b/modules/template/elements/SpaceElement.php similarity index 57% rename from modules/template/models/SpaceContent.php rename to modules/template/elements/SpaceElement.php index 46637ea2..a2749f05 100644 --- a/modules/template/models/SpaceContent.php +++ b/modules/template/elements/SpaceElement.php @@ -6,18 +6,25 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\modules\space\models\Space; use Yii; /** - * Class SpaceContent + * Class to manage content records of the Space elements */ -class SpaceContent extends ContentContainerContent +class SpaceElement extends BaseContentContainerElement { public const CONTAINER_CLASS = Space::class; - public static $label = 'Space'; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Yii::t('CustomPagesModule.template', 'Space'); + } /** * @inheritdoc diff --git a/modules/template/models/SpacesContent.php b/modules/template/elements/SpacesElement.php similarity index 60% rename from modules/template/models/SpacesContent.php rename to modules/template/elements/SpacesElement.php index 6e997984..8b7e7909 100644 --- a/modules/template/models/SpacesContent.php +++ b/modules/template/elements/SpacesElement.php @@ -6,7 +6,7 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\modules\space\models\Membership; use humhub\modules\space\models\Space; @@ -14,13 +14,63 @@ use yii\db\ActiveQuery; /** - * Class UsersContent + * Class to manage content records of the elements with Spaces list + * + * Dynamic attributes: + * @property array $member + * @property string $memberType + * @property array $tag + * @property int $limit */ -class SpacesContent extends RecordsContent +class SpacesElement extends BaseRecordsElement { public const RECORD_CLASS = Space::class; - public static $label = 'Spaces'; - public string $formView = 'spaces'; + public string $subFormView = 'spaces'; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Yii::t('CustomPagesModule.template', 'Spaces'); + } + + /** + * @inheritdoc + */ + protected function getDynamicAttributes(): array + { + return array_merge(parent::getDynamicAttributes(), [ + 'member' => null, + 'memberType' => null, + 'tag' => null, + 'limit' => null, + ]); + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return array_merge(parent::attributeLabels(), [ + 'static' => Yii::t('CustomPagesModule.template', 'Select spaces'), + 'member' => Yii::t('CustomPagesModule.template', 'User'), + 'memberType' => Yii::t('CustomPagesModule.template', 'Space member type'), + 'tag' => Yii::t('CustomPagesModule.template', 'Tag'), + 'limit' => Yii::t('CustomPagesModule.template', 'Limit'), + ]); + } + + /** + * @inheritdoc + */ + public function attributeHints() + { + return array_merge(parent::attributeHints(), [ + 'member' => Yii::t('CustomPagesModule.template', 'When no user is selected, the current logged in user will be used.'), + ]); + } /** * @inheritdoc @@ -62,13 +112,13 @@ protected function getQuery(): ActiveQuery protected function filterMember(ActiveQuery $query): ActiveQuery { - $userGuid = $this->options['member'] ?: Yii::$app->user->getGuid(); + $userGuid = $this->member ?: Yii::$app->user->getGuid(); - if (empty($this->options['memberType']) || empty($userGuid)) { + if (empty($this->memberType) || empty($userGuid)) { return $query->andWhere(false); } - return match ($this->options['memberType']) { + return match ($this->memberType) { 'member' => $query->leftJoin('space_membership', 'space_membership.space_id = space.id') ->leftJoin('user', 'user.id = space_membership.user_id') ->andWhere(['user.guid' => $userGuid]) @@ -87,7 +137,7 @@ protected function filterTag(ActiveQuery $query): ActiveQuery return $query->leftJoin('contentcontainer_tag_relation', 'contentcontainer_tag_relation.contentcontainer_id = space.contentcontainer_id') ->leftJoin('contentcontainer_tag', 'contentcontainer_tag.id = contentcontainer_tag_relation.tag_id') ->andWhere(['contentcontainer_tag.contentcontainer_class' => Space::class]) - ->andWhere(['contentcontainer_tag.name' => $this->options['tag']]); + ->andWhere(['contentcontainer_tag.name' => $this->tag]); } /** diff --git a/modules/template/elements/TextElement.php b/modules/template/elements/TextElement.php new file mode 100644 index 00000000..7387f4f0 --- /dev/null +++ b/modules/template/elements/TextElement.php @@ -0,0 +1,107 @@ + null, + 'inline_text' => 1, + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + ['content', 'trim'], + ['inline_text', 'boolean'], + ['content', 'string', 'length' => [1, 255]], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'content' => Yii::t('CustomPagesModule.template', 'Content'), + 'inline_text' => Yii::t('CustomPagesModule.template', 'Is inline text'), + ]; + } + + /** + * @inheritdoc + */ + public function scenarios() + { + $scenarios = parent::scenarios(); + + // We disallow editing this field in page editor + $index = array_search('inline_text', $scenarios[self::SCENARIO_EDIT]); + if ($index !== false) { + unset($scenarios[self::SCENARIO_EDIT][$index]); + } + + return $scenarios; + } + + /** + * @inheritdoc + */ + public function render($options = []) + { + $result = $this->inline_text ? $this->purify($this->content) : Html::encode($this->content); + + if ($this->isEditMode($options) && $this->inline_text) { + return $this->wrap('span', $result, $options); + } + + return $result; + } + + /** + * @inheritdoc + */ + public function renderEmpty($options = []) + { + if ($this->inline_text) { + $options['class'] = 'emptyBlock text'; + return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty Text'), $options); + } + + return ''; + } +} diff --git a/modules/template/models/UserContent.php b/modules/template/elements/UserElement.php similarity index 83% rename from modules/template/models/UserContent.php rename to modules/template/elements/UserElement.php index f6838f2a..335c2f07 100644 --- a/modules/template/models/UserContent.php +++ b/modules/template/elements/UserElement.php @@ -6,19 +6,26 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\modules\content\components\ContentContainerActiveRecord; use humhub\modules\user\models\User; use Yii; /** - * Class UserContent + * Class to manage content records of the User elements */ -class UserContent extends ContentContainerContent +class UserElement extends BaseContentContainerElement { public const CONTAINER_CLASS = User::class; - public static $label = 'User'; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Yii::t('CustomPagesModule.template', 'User'); + } /** * @inheritdoc diff --git a/modules/template/models/UsersContent.php b/modules/template/elements/UsersElement.php similarity index 54% rename from modules/template/models/UsersContent.php rename to modules/template/elements/UsersElement.php index 7b6ad4f7..31176388 100644 --- a/modules/template/models/UsersContent.php +++ b/modules/template/elements/UsersElement.php @@ -6,7 +6,7 @@ * @license https://www.humhub.com/licences */ -namespace humhub\modules\custom_pages\modules\template\models; +namespace humhub\modules\custom_pages\modules\template\elements; use humhub\modules\user\models\Group; use humhub\modules\user\models\User; @@ -14,13 +14,60 @@ use yii\db\ActiveQuery; /** - * Class UsersContent + * Class to manage content records of the elements with Users list + * + * Dynamic attributes: + * @property string $group + * @property array $friend + * @property int $limit */ -class UsersContent extends RecordsContent +class UsersElement extends BaseRecordsElement { public const RECORD_CLASS = User::class; - public static $label = 'Users'; - public string $formView = 'users'; + public string $subFormView = 'users'; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Yii::t('CustomPagesModule.template', 'Users'); + } + + /** + * @inheritdoc + */ + protected function getDynamicAttributes(): array + { + return array_merge(parent::getDynamicAttributes(), [ + 'group' => null, + 'friend' => null, + 'limit' => null, + ]); + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return array_merge(parent::attributeLabels(), [ + 'static' => Yii::t('CustomPagesModule.template', 'Select users'), + 'group' => Yii::t('CustomPagesModule.template', 'Select group'), + 'friend' => Yii::t('CustomPagesModule.template', 'User'), + 'limit' => Yii::t('CustomPagesModule.template', 'Limit'), + ]); + } + + /** + * @inheritdoc + */ + public function attributeHints() + { + return array_merge(parent::attributeHints(), [ + 'friend' => Yii::t('CustomPagesModule.template', 'When no user is selected, the current logged in user will be used.'), + ]); + } /** * @inheritdoc @@ -33,6 +80,18 @@ public function getTypes(): array ]); } + /** + * @inheritdoc + */ + public function rules() + { + return array_merge(parent::rules(), [ + [['group'], 'in', 'range' => array_keys($this->getGroupOptions())], + [['friend'], 'safe'], + [['limit'], 'integer'], + ]); + } + public function getGroupOptions(): array { $groups = Yii::$app->user->isAdmin() @@ -64,12 +123,12 @@ protected function getQuery(): ActiveQuery protected function filterGroup(ActiveQuery $query): ActiveQuery { return $query->leftJoin('group_user', 'group_user.user_id = user.id') - ->andWhere(['group_user.group_id' => $this->options['group']]); + ->andWhere(['group_user.group_id' => $this->group]); } protected function filterFriend(ActiveQuery $query): ActiveQuery { - $friendGuid = $this->options['friend'] ?: Yii::$app->user->getGuid(); + $friendGuid = $this->friend ?: Yii::$app->user->getGuid(); if (empty($friendGuid)) { return $query->andWhere(false); diff --git a/modules/template/models/ContainerContentDefinition.php b/modules/template/models/ContainerContentDefinition.php deleted file mode 100644 index e1b8f083..00000000 --- a/modules/template/models/ContainerContentDefinition.php +++ /dev/null @@ -1,107 +0,0 @@ - Yii::t('CustomPagesModule.template', 'Allow multiple items?'), - 'allowedTemplateSelection' => Yii::t('CustomPagesModule.template', 'Allowed Templates'), - 'is_inline' => Yii::t('CustomPagesModule.template', 'Render items as inline-blocks within the inline editor?'), - ]); - } - - public function afterSave($insert, $changedAttributes) - { - parent::afterSave($insert, $changedAttributes); - - $this->saveAllowedTemplateSelection(); - } - - public function saveAllowedTemplateSelection() - { - if (!empty($this->allowedTemplateSelection)) { - ContainerContentTemplate::deleteAll(['definition_id' => $this->id]); - foreach ($this->allowedTemplateSelection as $allowedTemplateId) { - $allowedTemplate = new ContainerContentTemplate(); - $allowedTemplate->template_id = $allowedTemplateId; - $allowedTemplate->definition_id = $this->id; - $allowedTemplate->save(); - } - } - } - - public function beforeDelete() - { - ContainerContentTemplate::deleteAll(['definition_id' => $this->id]); - return parent::beforeDelete(); - } - - public function getAllowedTemplates() - { - if (empty($this->templates)) { - return Template::findAllByType(Template::TYPE_CONTAINER); - } - return $this->templates; - } - - public function isSingleAllowedTemplate() - { - return count($this->templates) === 1; - } - - public function initAllowedTemplateSelection($actualSelection = true) - { - $this->allowedTemplateSelection = $this->getAllowedTemplateArray($actualSelection); - } - - protected function getAllowedTemplateArray($actualSelection = true) - { - $selectionTemplates = ($actualSelection) ? $this->templates : $this->allowedTemplates; - - $result = []; - foreach ($selectionTemplates as $allowedTemplate) { - $result[] = $allowedTemplate->id; - } - - return $result; - } - - public function getContentTemplates() - { - return $this->hasMany(ContainerContentTemplate::class, ['definition_id' => 'id']); - } - - public function getTemplates() - { - return $this->hasMany(Template::class, ['id' => 'template_id']) - ->via('contentTemplates')->all(); - } -} diff --git a/modules/template/models/ContainerContentTemplate.php b/modules/template/models/ContainerContentTemplate.php deleted file mode 100644 index 6525bf4e..00000000 --- a/modules/template/models/ContainerContentTemplate.php +++ /dev/null @@ -1,37 +0,0 @@ -hasOne(Template::class, ['id' => 'template_id']); - } - - public function getContent() - { - return $this->hasOne(ContainerContent::class, ['id' => 'definition_id']); - } -} diff --git a/modules/template/models/ContainerItemVariable.php b/modules/template/models/ContainerItemVariable.php index de0653d0..d5af31a6 100644 --- a/modules/template/models/ContainerItemVariable.php +++ b/modules/template/models/ContainerItemVariable.php @@ -2,12 +2,13 @@ namespace humhub\modules\custom_pages\modules\template\models; +use humhub\modules\custom_pages\modules\template\elements\ContainerItem; use yii\base\Model; class ContainerItemVariable extends Model { /** - * @var ContainerContentItem + * @var ContainerItem */ public $item; diff --git a/modules/template/models/ContentDefinition.php b/modules/template/models/ContentDefinition.php deleted file mode 100644 index f4565c48..00000000 --- a/modules/template/models/ContentDefinition.php +++ /dev/null @@ -1,43 +0,0 @@ -formName = $formName; - } - - public function hasValues() - { - $result = false; - foreach ($this->attributes() as $key) { - if ($this->getAttribute($key) != null && $key != 'id' && $key != 'is_default') { - $result = true; - break; - } - } - return $result; - } - - public function formName() - { - return ($this->formName != null) ? $this->formName : parent::formName(); - } - - public function load($data, $formName = null) - { - parent::load($data, $formName); - } - -} diff --git a/modules/template/models/FileContent.php b/modules/template/models/FileContent.php deleted file mode 100644 index cbec89e2..00000000 --- a/modules/template/models/FileContent.php +++ /dev/null @@ -1,129 +0,0 @@ -label) - */ - public function attributeLabels() - { - return [ - 'file_guid' => Yii::t('CustomPagesModule.base', 'File'), - ]; - } - - public function saveFiles() - { - $files = File::findByRecord($this); - - foreach ($files as $file) { - if ($file->guid !== $this->file_guid) { - $file->delete(); - } - } - - $this->fileManager->attach($this->file_guid); - } - - public function getLabel() - { - return static::$label; - } - - public function getFile() - { - return File::findOne(['guid' => $this->file_guid]); - } - - public function hasFile() - { - return $this->file_guid != null && $this->getFile() != null; - } - - public function getUrl() - { - $file = $this->getFile(); - return ($file != null) ? $file->getUrl() : null; - } - - public function copy() - { - $clone = $this->createCopy(); - $clone->file_guid = $this->file_guid; - return $clone; - } - - public function render($options = []) - { - if ($this->hasFile()) { - return $this->getFile()->getUrl(); - } - return ''; - } - - public function renderEmpty($options = []) - { - return ''; - } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'file', - 'form' => $form, - 'model' => $this, - ]); - } - - public function canEdit($user = null): bool - { - return PagePermission::canEdit(); - } - - public function isEmpty(): bool - { - return !$this->hasFile(); - } - -} diff --git a/modules/template/models/FileDownloadContent.php b/modules/template/models/FileDownloadContent.php deleted file mode 100644 index d9534906..00000000 --- a/modules/template/models/FileDownloadContent.php +++ /dev/null @@ -1,188 +0,0 @@ -showFileinfo === null) { - $this->showFileinfo = 1; - } - - if ($this->showIcon === null) { - $this->showIcon = 1; - } - } - - /** - * @inheritdoc - */ - public function rules() - { - $result = parent::rules(); - $result[] = [['file_guid'], 'required']; - $result[] = [['title', 'style', 'cssClass'], 'string']; - $result[] = [['showFileinfo', 'showIcon'], 'integer']; - return $result; - } - - /** - * @inheritdoc - */ - public function scenarios() - { - $scenarios = parent::scenarios(); - array_push($scenarios[self::SCENARIO_CREATE], 'file_guid', 'title', 'style', 'cssClass', 'showFileinfo', 'showIcon'); - array_push($scenarios[self::SCENARIO_EDIT_ADMIN], 'file_guid', 'title', 'style', 'cssClass', 'showFileinfo', 'showIcon'); - array_push($scenarios[self::SCENARIO_EDIT], 'file_guid', 'title', 'style', 'cssClass', 'showFileinfo', 'showIcon'); - return $scenarios; - } - - /** - * @return array customized attribute labels (name=>label) - */ - public function attributeLabels() - { - return [ - 'file_guid' => Yii::t('CustomPagesModule.base', 'File'), - 'title' => Yii::t('CustomPagesModule.base', 'Title'), - 'style' => Yii::t('CustomPagesModule.base', 'Style'), - 'cssClass' => Yii::t('CustomPagesModule.base', 'Css Class'), - 'showFileinfo' => Yii::t('CustomPagesModule.base', 'Show additional file information (size)'), - 'showIcon' => Yii::t('CustomPagesModule.base', 'Add a file icon before the title'), - ]; - } - - public function saveFiles() - { - $files = File::findByRecord($this); - - foreach ($files as $file) { - if ($file->guid !== $this->file_guid) { - $file->delete(); - } - } - - $this->fileManager->attach($this->file_guid); - } - - public function getLabel() - { - return static::$label; - } - - public function getFile() - { - return File::findOne(['guid' => $this->file_guid]); - } - - public function hasFile() - { - return $this->file_guid != null && $this->getFile() != null; - } - - public function getUrl() - { - $file = $this->getFile(); - return ($file != null) ? $file->getUrl() : null; - } - - public function getDownloadUrl() - { - $file = $this->getFile(); - if ($file) { - return Url::to(['/file/file/download', 'guid' => $file->guid]); - } - } - - public function copy() - { - $clone = $this->createCopy(); - $clone->file_guid = $this->file_guid; - $clone->title = $this->title; - $clone->style = $this->style; - $clone->cssClass = $this->cssClass; - $clone->showFileinfo = $this->showFileinfo; - $clone->showIcon = $this->showIcon; - return $clone; - } - - public function render($options = []) - { - if ($this->hasFile()) { - $file = $this->getFile(); - $options['htmlOptions'] = [ - 'href' => $this->getDownloadUrl(), - 'style' => Html::encode($this->style), - 'class' => Html::encode($this->cssClass), - 'target' => '_blank', - 'data-pjax-prevent' => '1', - ]; - - $content = ($this->title) ? $this->title : $file->file_name; - $content = Html::encode($content); - - $fileInfo = FileHelper::getFileInfos($file); - - if ($this->showIcon) { - $options['htmlOptions']['class'] .= ' mime ' . $fileInfo['mimeIcon']; - } - - if ($this->showFileinfo) { - $content .= Html::tag('small', ' - ' . $fileInfo['size_format'], ['class' => 'file-fileInfo']); - } - - if ($this->isEditMode($options)) { - return $this->wrap('a', $content, $options); - } else { - return Html::tag('a', $content, $options['htmlOptions']); - } - } - return ''; - } - - public function renderEmpty($options = []) - { - return ''; - } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'fileDownload', - 'form' => $form, - 'model' => $this, - ]); - } - -} diff --git a/modules/template/models/HumHubRichtextContent.php b/modules/template/models/HumHubRichtextContent.php deleted file mode 100644 index a031a181..00000000 --- a/modules/template/models/HumHubRichtextContent.php +++ /dev/null @@ -1,91 +0,0 @@ -label) - */ - public function attributeLabels() - { - return [ - 'content' => 'Content', - ]; - } - - public function getLabel() - { - return self::$label; - } - - public function copy() - { - return new HumHubRichtextContent(['content' => $this->content]); - } - - public function render($options = []) - { - if ($this->isEditMode($options)) { - return $this->wrap('div', Richtext::output($this->content), $options); - } - - return Richtext::output($this->content); - } - - public function saveFiles() - { - Richtext::postProcess($this->content, $this); - } - - public function renderEmpty($options = []) - { - return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty HumHub Richtext'), $options); - } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'humhub_richtext', - 'form' => $form, - 'model' => $this, - ]); - } - -} diff --git a/modules/template/models/ImageContentDefinition.php b/modules/template/models/ImageContentDefinition.php deleted file mode 100644 index 5a148faf..00000000 --- a/modules/template/models/ImageContentDefinition.php +++ /dev/null @@ -1,38 +0,0 @@ - Yii::t('CustomPagesModule.template', 'Style'), - 'height' => Yii::t('CustomPagesModule.template', 'Height'), - 'width' => Yii::t('CustomPagesModule.template', 'Width'), - ]); - } -} diff --git a/modules/template/models/OwnerContent.php b/modules/template/models/OwnerContent.php index 6c0a99df..2dda9f1c 100644 --- a/modules/template/models/OwnerContent.php +++ b/modules/template/models/OwnerContent.php @@ -5,13 +5,15 @@ use humhub\components\ActiveRecord; use humhub\modules\content\components\ContentActiveRecord; use humhub\modules\content\models\Content; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; +use humhub\modules\custom_pages\modules\template\elements\UserElement; use Yii; /** * This is the model class for table "custom_pages_template_content". * * An OwnerContent instance is used to assign a content to a template placeholder. - * The owner of the content can either be a Template (default content) or an TemplateContentOwner (e.g. TemplateInstance, ContainerContentItem). + * The owner of the content can either be a Template (default content) or an TemplateContentOwner (e.g. TemplateInstance, ContainerItem). * * @property string $element_name * @property string $owner_model @@ -32,7 +34,7 @@ public function behaviors() return [ [ 'class' => \humhub\components\behaviors\PolymorphicRelation::class, - 'mustBeInstanceOf' => [TemplateContentActiveRecord::class], + 'mustBeInstanceOf' => [BaseTemplateElementContent::class], 'classAttribute' => 'content_type', 'pkAttribute' => 'content_id', ], @@ -75,17 +77,16 @@ public function beforeDelete() } /** - * Returns the underlying TemplateContentActiveRecord instance. + * Returns the underlying BaseTemplateElementContent instance. * * If $createDummy is set to true, this function will return a empty dummy * object of this content_type. * - * @param type $createDummy - * @return TemplateContentActiveRecord + * @param bool $createDummy + * @return BaseTemplateElementContent|null */ - public function getInstance($createDummy = false) + public function getInstance(bool $createDummy = false): ?BaseTemplateElementContent { - if ($this->getPolymorphicRelation() == null && $createDummy) { return Yii::createObject($this->content_type); } @@ -140,9 +141,9 @@ public static function getOwnerModel($model, $id) /** * Sets the object_model and object_id by means of the given $content instance. * - * @param TemplateContentActiveRecord $content + * @param BaseTemplateElementContent $content */ - public function setContent(TemplateContentActiveRecord $content) + public function setContent(BaseTemplateElementContent $content) { $this->content_type = get_class($content); $this->content_id = $content->id; @@ -160,9 +161,9 @@ public function copy() /** * Returns a copy of the related content instance. * - * @return \humhub\modules\custom_pages\modules\template\models\TemplateContentActiveRecord + * @return BaseTemplateElementContent */ - public function copyContent() + public function copyContent(): BaseTemplateElementContent { return $this->getPolymorphicRelation()->copy(); } @@ -246,7 +247,7 @@ public static function findByOwner($ownerClass, $ownerId = null, $elementName = */ public static function deleteByOwner($ownerClass, $ownerId = null, $elementName = null) { - // We can't use delteAll since it won't trigger the afetDelete + // We can't use deleteAll since it won't trigger the afetDelete foreach (self::findByOwner($ownerClass, $ownerId, $elementName)->all() as $instance) { $instance->delete(); } @@ -282,6 +283,6 @@ public function getItems(): iterable public function getProfileField(string $field = null): string { $instance = $this->getInstance(); - return $instance instanceof UserContent ? $instance->getProfileField($field) : ''; + return $instance instanceof UserElement ? $instance->getProfileField($field) : ''; } } diff --git a/modules/template/models/OwnerContentVariable.php b/modules/template/models/OwnerContentVariable.php index a553b982..6f41f283 100644 --- a/modules/template/models/OwnerContentVariable.php +++ b/modules/template/models/OwnerContentVariable.php @@ -2,6 +2,7 @@ namespace humhub\modules\custom_pages\modules\template\models; +use humhub\modules\custom_pages\modules\template\elements\ContainerElement; use yii\base\Model; class OwnerContentVariable extends Model @@ -55,7 +56,7 @@ public function render($editMode = false) ], $this->options); // We only need the template_id for container content elements - if ($this->ownerContent->content_type == ContainerContent::class) { + if ($this->ownerContent->content_type == ContainerElement::class) { $options['template_id'] = $this->ownerContent->owner->getTemplateId(); } } else { diff --git a/modules/template/models/RecordsContent.php b/modules/template/models/RecordsContent.php deleted file mode 100644 index 1eb6ee6e..00000000 --- a/modules/template/models/RecordsContent.php +++ /dev/null @@ -1,225 +0,0 @@ - array_keys($this->getTypes())], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'type' => Yii::t('CustomPagesModule.template', 'Type'), - ]; - } - - /** - * @inheritdoc - */ - public function scenarios() - { - return ArrayHelper::merge(parent::scenarios(), [ - self::SCENARIO_CREATE => $attributes = ['type', 'options'], - self::SCENARIO_EDIT_ADMIN => $attributes, - self::SCENARIO_EDIT => $attributes, - ]); - } - - /** - * @inheritdoc - */ - public function getLabel() - { - return static::$label; - } - - /** - * @inheritdoc - */ - public function copy() - { - $clone = new static(); - $clone->type = $this->type; - $clone->class = $this->class; - $clone->options = $this->options; - return $clone; - } - - /** - * @inheritdoc - */ - public function render($options = []) - { - return Html::encode($this->class); - } - - /** - * @inheritdoc - */ - public function renderEmpty($options = []) - { - return ''; - } - - /** - * @inheritdoc - */ - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'records', - 'form' => $form, - 'model' => $this, - ]); - } - - /** - * @inheritdoc - */ - public function __get($name) - { - $value = parent::__get($name); - - if ($name === 'options' && !is_array($value)) { - $value = empty($value) ? [] : json_decode($value, true); - $this->setAttribute($name, $value); - } - - return $value; - } - - /** - * @inheritdoc - */ - public function beforeValidate() - { - $this->class = static::RECORD_CLASS; - return parent::beforeValidate(); - } - - /** - * @inheritdoc - */ - public function beforeSave($insert) - { - if (parent::beforeSave($insert)) { - $this->options = is_array($this->options) ? json_encode($this->options) : null; - return true; - } - - return false; - } - - /** - * Get types for the records list - * - * @return array - */ - public function getTypes(): array - { - return [ - 'static' => Yii::t('CustomPagesModule.template', 'Static list'), - ]; - } - - /** - * @inheritdoc - */ - public function getItems(): iterable - { - if ($this->records === null) { - if (!$this->isConfigured()) { - // No need to touch DB because option is not configured for the current type - $this->records = []; - } else { - // Get records from DB - $query = $this->getQuery(); - - if ($this->type !== 'static' && !empty($this->options['limit'])) { - // Limit only dynamic list - $query->limit($this->options['limit']); - } - - $this->records = $query->all(); - } - } - - yield from $this->records; - } - - protected function filterStatic(ActiveQuery $query): ActiveQuery - { - return $query->andWhere(['guid' => $this->options['static']]); - } - - /** - * Check if the Element is properly configured - * - * @return bool - */ - protected function isConfigured(): bool - { - return !empty($this->options[$this->type]); - } -} diff --git a/modules/template/models/RichtextContent.php b/modules/template/models/RichtextContent.php deleted file mode 100644 index 99969b46..00000000 --- a/modules/template/models/RichtextContent.php +++ /dev/null @@ -1,97 +0,0 @@ -label) - */ - public function attributeLabels() - { - return [ - 'content' => 'Content', - ]; - } - - public function getLabel() - { - return self::$label; - } - - public function copy() - { - $clone = new RichtextContent(); - $clone->content = $this->content; - return $clone; - } - - public function render($options = []) - { - if ($this->isEditMode($options)) { - return $this->wrap('div', $this->purify($this->content), $options); - } - - return $this->purify($this->content); - } - - public function renderEmpty($options = []) - { - return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty Richtext'), $options); - } - - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'richtext', - 'form' => $form, - 'model' => $this, - ]); - } - - /** - * @param null $user - * @return bool - */ - public function canEdit($user = null): bool - { - return PagePermission::canEdit(); - } - -} diff --git a/modules/template/models/Template.php b/modules/template/models/Template.php index 11cca5dc..2cd30ddc 100644 --- a/modules/template/models/Template.php +++ b/modules/template/models/Template.php @@ -6,6 +6,8 @@ use humhub\modules\content\models\Content; use humhub\modules\custom_pages\lib\templates\TemplateEngineFactory; use humhub\modules\custom_pages\models\CustomPage; +use humhub\modules\custom_pages\modules\template\elements\ContainerDefinition; +use humhub\modules\custom_pages\modules\template\elements\ContainerElement; use yii\db\ActiveQuery; use yii\helpers\ArrayHelper; @@ -151,7 +153,12 @@ public function isInUse() if ($this->isLayout()) { return TemplateInstance::findByTemplateId($this->id, Content::STATE_PUBLISHED)->count() > 0; } else { - return ContainerContentTemplate::find()->where(['template_id' => $this->id])->count() > 0; + return TemplateElement::find() + ->leftJoin(ContainerElement::tableName(), TemplateElement::tableName() . '.id = ' . ContainerElement::tableName() . '.element_id') + ->leftJoin(ContainerDefinition::tableName(), ContainerElement::tableName() . '.definition_id = ' . ContainerDefinition::tableName() . '.id') + ->where([TemplateElement::tableName() . '.content_type' => ContainerElement::class]) + ->andWhere(['REGEXP', ContainerDefinition::tableName() . '.dynAttributes', '"templates":[^\\]]*' . $this->id . '[,\\]]']) + ->exists(); } } @@ -162,11 +169,11 @@ public function getLinkedRecordsQuery(): ActiveQuery } else { return Template::find() ->leftJoin(OwnerContent::tableName(), Template::tableName() . '.id = ' . OwnerContent::tableName() . '.owner_id') - ->leftJoin(ContainerContent::tableName(), ContainerContent::tableName() . '.id = ' . OwnerContent::tableName() . '.content_id') - ->leftJoin(ContainerContentTemplate::tableName(), ContainerContentTemplate::tableName() . '.definition_id = ' . ContainerContent::tableName() . '.definition_id') + ->leftJoin(ContainerElement::tableName(), ContainerElement::tableName() . '.id = ' . OwnerContent::tableName() . '.content_id') + ->leftJoin(ContainerDefinition::tableName(), ContainerDefinition::tableName() . '.id = ' . ContainerElement::tableName() . '.definition_id') ->where([OwnerContent::tableName() . '.owner_model' => Template::class]) - ->andWhere([OwnerContent::tableName() . '.content_type' => ContainerContent::class]) - ->andWhere([ContainerContentTemplate::tableName() . '.template_id' => $this->id]); + ->andWhere([OwnerContent::tableName() . '.content_type' => ContainerElement::class]) + ->andWhere(['REGEXP', ContainerDefinition::tableName() . '.dynAttributes', '"templates":[^\\]]*' . $this->id . '[,\\]]']); } } diff --git a/modules/template/models/TemplateElement.php b/modules/template/models/TemplateElement.php index 4ed3566c..3f40fd27 100644 --- a/modules/template/models/TemplateElement.php +++ b/modules/template/models/TemplateElement.php @@ -2,6 +2,7 @@ namespace humhub\modules\custom_pages\modules\template\models; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; use Yii; use yii\db\ActiveRecord; @@ -9,12 +10,13 @@ * This is the model class for table "custom_pages_template". * * TemplateElements represent the placeholders of a template. - * A TemplateElement consists of an name which is unique within the template and content type definition. + * A TemplateElement consists of a name which is unique within the template and content type definition. * - * @property $name string - * @property $content_type string - * @property $template_id int - * @property $title string + * @property int $id + * @property int $template_id + * @property string $name + * @property string $content_type + * @property string $title */ class TemplateElement extends ActiveRecord { @@ -89,7 +91,7 @@ public function uniqueTemplateElementName($attribute, $params) * This will overwrite the default content of a template placeholder for the given * $owner instance. * - * $content is the actual content instance of type TemplateContentActiveRecord which will + * $content is the actual content instance of type BaseTemplateElementContent which will * be assigned to this placeholder for the given $owner. * * If the given $content instance was not persisted yet, it will be saved first. @@ -99,10 +101,10 @@ public function uniqueTemplateElementName($attribute, $params) * Note that all current OwnerContent entries for this placeholder owned by $owner are delted. * * @param ActiveRecord $owner the owner - * @param TemplateContentActiveRecord $content + * @param BaseTemplateElementContent $content * @return OwnerContent the new created owner content instance. */ - public function saveInstance(ActiveRecord $owner, TemplateContentActiveRecord $content, $useDefault = false) + public function saveInstance(ActiveRecord $owner, BaseTemplateElementContent $content, $useDefault = false) { $content->save(); @@ -127,10 +129,10 @@ public function saveInstance(ActiveRecord $owner, TemplateContentActiveRecord $c * * Note that the current default content of this placeholder will be delted. * - * @param TemplateContentActiveRecord $content + * @param BaseTemplateElementContent $content * @return bool */ - public function saveAsDefaultContent(TemplateContentActiveRecord $content) + public function saveAsDefaultContent($content) { if (get_class($content) != $this->content_type) { return false; @@ -139,6 +141,10 @@ public function saveAsDefaultContent(TemplateContentActiveRecord $content) // Delete all current default content elements. OwnerContent::deleteByOwner(Template::class, $this->template_id, $this->name); + if ($content instanceof BaseTemplateElementContent) { + $content->element_id = $this->id; + } + if ($content->save()) { $contentInstance = new OwnerContent(); $contentInstance->element_name = $this->name; @@ -207,7 +213,7 @@ public function afterDelete() parent::afterDelete(); } - public function getTemplateContent(): TemplateContentActiveRecord + public function getTemplateContent(): BaseTemplateElementContent { return Yii::createObject($this->content_type); } @@ -220,7 +226,4 @@ public function getLabel() { return $this->getTemplateContent()->getLabel(); } - - - } diff --git a/modules/template/models/TextContent.php b/modules/template/models/TextContent.php deleted file mode 100644 index 7c3e0f61..00000000 --- a/modules/template/models/TextContent.php +++ /dev/null @@ -1,134 +0,0 @@ -isNewRecord) { - $this->inline_text = 1; - } - } - - /** - * @return string the associated database table name - */ - public static function tableName() - { - return 'custom_pages_template_text_content'; - } - - /** - * @inheritdoc - */ - public function rules() - { - $result = parent::rules(); - $result[] = ['content', 'trim']; - $result[] = ['inline_text', 'boolean']; - $result[] = ['content', 'string', 'length' => [1, 255]]; - return $result; - } - - /** - * @inheritdoc - */ - public function scenarios() - { - $scenarios = parent::scenarios(); - $scenarios[self::SCENARIO_CREATE][] = 'content'; - $scenarios[self::SCENARIO_EDIT_ADMIN][] = 'content'; - $scenarios[self::SCENARIO_EDIT][] = 'content'; - - $scenarios[self::SCENARIO_CREATE][] = 'inline_text'; - $scenarios[self::SCENARIO_EDIT_ADMIN][] = 'inline_text'; - - // We disallow editing this field in page editor - //$scenarios[self::SCENARIO_EDIT][] = 'inline_text'; - - return $scenarios; - } - - /** - * @return array customized attribute labels (name=>label) - */ - public function attributeLabels() - { - return [ - 'content' => 'Content', - 'inline_text' => 'Is inline text', - ]; - } - - /** - * @inheritdoc - */ - public function getLabel() - { - return self::$label; - } - - /** - * @inheritdoc - */ - public function copy() - { - $clone = new TextContent(); - $clone->content = $this->content; - $clone->inline_text = $this->inline_text; - return $clone; - } - - /** - * @inheritdoc - */ - public function render($options = []) - { - $result = $this->inline_text ? $this->purify($this->content) : Html::encode($this->content); - - if ($this->isEditMode($options) && $this->inline_text) { - return $this->wrap('span', $result, $options); - } - - return $result; - } - - /** - * @inheritdoc - */ - public function renderEmpty($options = []) - { - if ($this->inline_text) { - $options['class'] = 'emptyBlock text'; - return $this->renderEmptyDiv(Yii::t('CustomPagesModule.model', 'Empty Text'), $options); - } - - return ''; - } - - /** - * @inheritdoc - */ - public function renderForm($form) - { - return TemplateContentFormFields::widget([ - 'type' => 'text', - 'form' => $form, - 'model' => $this, - ]); - } - -} diff --git a/modules/template/models/forms/AddElementForm.php b/modules/template/models/forms/AddElementForm.php index 4f858aee..f2e9626f 100644 --- a/modules/template/models/forms/AddElementForm.php +++ b/modules/template/models/forms/AddElementForm.php @@ -82,9 +82,7 @@ public function setElementDefinition($templateId, $type) public function save() { if ($this->validate()) { - // Try saving the default content if - $this->element->saveAsDefaultContent($this->content); - return $this->element->save(false); + return $this->element->save(false) && $this->element->saveAsDefaultContent($this->content); } else { return false; } diff --git a/modules/template/models/forms/ContentFormItem.php b/modules/template/models/forms/ContentFormItem.php index 3a9eab73..ef6296a6 100644 --- a/modules/template/models/forms/ContentFormItem.php +++ b/modules/template/models/forms/ContentFormItem.php @@ -8,23 +8,29 @@ namespace humhub\modules\custom_pages\modules\template\models\forms; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; +use humhub\modules\custom_pages\modules\template\models\OwnerContent; +use humhub\modules\custom_pages\modules\template\models\TemplateElement; +use yii\base\Model; + /** * Form model used to add new TemplateElement instances to a Template. * * @author buddha */ -class ContentFormItem extends \yii\base\Model +class ContentFormItem extends Model { - public $ownerContent; - public $editDefault = true; - public $content; - public $element; - public $isLoaded = false; + public ?OwnerContent $ownerContent = null; + public bool $editDefault = true; + public ?BaseTemplateElementContent $content = null; + public ?TemplateElement $element = null; + public bool $isLoaded = false; public $key; public function init() { $this->content = $this->ownerContent->getInstance(true); + $this->content->element_id = $this->element->id; if ($this->ownerContent->isNewRecord) { $this->key = $this->ownerContent->element_name; diff --git a/modules/template/models/forms/EditElementForm.php b/modules/template/models/forms/EditElementForm.php index a8b176db..2f0368f1 100644 --- a/modules/template/models/forms/EditElementForm.php +++ b/modules/template/models/forms/EditElementForm.php @@ -8,6 +8,7 @@ namespace humhub\modules\custom_pages\modules\template\models\forms; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; use Yii; use humhub\modules\custom_pages\modules\template\models\TemplateElement; @@ -47,9 +48,10 @@ public function setElementId($elementId) public function save() { - if ($this->validate()) { - $this->element->save(); - + if ($this->validate() && $this->element->save()) { + if ($this->content instanceof BaseTemplateElementContent) { + $this->content->element_id = $this->element->id; + } // Try saving the default content if if ($this->content->save()) { $this->defaultOwnerContent->setContent($this->content); diff --git a/modules/template/models/forms/EditItemForm.php b/modules/template/models/forms/EditItemForm.php index 013a296c..c80740b0 100644 --- a/modules/template/models/forms/EditItemForm.php +++ b/modules/template/models/forms/EditItemForm.php @@ -8,7 +8,7 @@ namespace humhub\modules\custom_pages\modules\template\models\forms; -use humhub\modules\custom_pages\modules\template\models\ContainerContentItem; +use humhub\modules\custom_pages\modules\template\elements\ContainerItem; /** * Description of UserGroupForm @@ -36,7 +36,7 @@ public function scenarios() public function setItem($itemId) { - $this->owner = ContainerContentItem::findOne(['id' => $itemId]); + $this->owner = ContainerItem::findOne(['id' => $itemId]); $this->title = $this->owner->title; $this->setTemplate($this->owner->template_id); } diff --git a/modules/template/models/forms/TemplateElementForm.php b/modules/template/models/forms/TemplateElementForm.php index a7a8fafb..db07e45d 100644 --- a/modules/template/models/forms/TemplateElementForm.php +++ b/modules/template/models/forms/TemplateElementForm.php @@ -8,6 +8,7 @@ namespace humhub\modules\custom_pages\modules\template\models\forms; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; use Yii; /** @@ -31,7 +32,7 @@ class TemplateElementForm extends \yii\base\Model /** * Default content instance. * - * @var \humhub\modules\custom_pages\modules\template\models\TemplateContentActiveRecord + * @var BaseTemplateElementContent */ public $content; @@ -91,7 +92,7 @@ public function load($data, $formName = null) public function validate($attributeNames = null, $clearErrors = true) { - return parent::validate() && $this->element->validate(); + return parent::validate() && $this->element->validate() && $this->content->validate(); } public function getLabel() diff --git a/modules/template/services/ExportService.php b/modules/template/services/ExportService.php index dbae231f..c60731fe 100644 --- a/modules/template/services/ExportService.php +++ b/modules/template/services/ExportService.php @@ -8,9 +8,9 @@ namespace humhub\modules\custom_pages\modules\template\services; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\TemplateContentActiveRecord; use humhub\modules\custom_pages\modules\template\models\TemplateContentOwner; use humhub\modules\file\models\File; use Yii; @@ -51,7 +51,7 @@ public function export(): self } $contentObject = $defaultContent->getInstance(); - if ($contentObject instanceof TemplateContentActiveRecord) { + if ($contentObject instanceof BaseTemplateElementContent) { $this->data['elements'][$e]['ownerContent']['contentObject'] = $contentObject->attributes; // Attach files diff --git a/modules/template/services/ImportService.php b/modules/template/services/ImportService.php index 5ed32e7b..cc66ec90 100644 --- a/modules/template/services/ImportService.php +++ b/modules/template/services/ImportService.php @@ -9,9 +9,9 @@ namespace humhub\modules\custom_pages\modules\template\services; use humhub\components\ActiveRecord; +use humhub\modules\custom_pages\modules\template\elements\BaseTemplateElementContent; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\TemplateContentActiveRecord; use humhub\modules\custom_pages\modules\template\models\TemplateContentOwner; use humhub\modules\custom_pages\modules\template\models\TemplateElement; use humhub\modules\file\models\FileContent; @@ -160,7 +160,7 @@ private function importOwnerContent(array $data): ?OwnerContent } $contentObject = $this->createObjectByData($data['content_type'], $data['contentObject']); - if ($contentObject instanceof TemplateContentActiveRecord) { + if ($contentObject instanceof BaseTemplateElementContent) { $ownerContent->content_type = $data['content_type']; $ownerContent->content_id = $contentObject->id; } diff --git a/modules/template/views/admin/editSource.php b/modules/template/views/admin/editSource.php index 6ca59aaa..cfdd95dd 100644 --- a/modules/template/views/admin/editSource.php +++ b/modules/template/views/admin/editSource.php @@ -1,13 +1,16 @@ registerJsConfig('custom_pages.template.source', [ 'text' => [ @@ -18,7 +21,7 @@
Richtext Test
'); + $I->jsFillField('RichtextElement[content]', 'Richtext Test
'); $I->click('.btn-primary', '#globalModal'); $I->waitForElementNotVisible('#globalModal'); @@ -53,10 +53,10 @@ public function testCreateTemplate(AcceptanceTester $I) $I->attachFile('files[]', 'test.jpg'); $I->click('.collapsableTrigger'); //Show more $I->wait(2); - $I->fillField('ImageContent[definitionPostData][height]', '100'); - $I->fillField('ImageContent[definitionPostData][width]', '100'); - $I->fillField('ImageContent[definitionPostData][style]', 'border:1px solid black'); - $I->fillField('ImageContent[alt]', 'This is my test alt text'); + $I->fillField('ImageElement[definitionPostData][height]', '100'); + $I->fillField('ImageElement[definitionPostData][width]', '100'); + $I->fillField('ImageElement[definitionPostData][style]', 'border:1px solid black'); + $I->fillField('ImageElement[alt]', 'This is my test alt text'); $I->click('Save', '#globalModal'); $I->waitForElementNotVisible('#globalModal'); diff --git a/tests/codeception/fixtures/ContainerContentTemplateFixture.php b/tests/codeception/fixtures/ContainerContentTemplateFixture.php deleted file mode 100644 index a1d4d6b3..00000000 --- a/tests/codeception/fixtures/ContainerContentTemplateFixture.php +++ /dev/null @@ -1,19 +0,0 @@ - 1, 'definition_id' => 1], - ['id' => 2, 'definition_id' => 2], + ['id' => 6, 'element_id' => 3, 'definition_id' => 1], + ['id' => 7, 'element_id' => 4, 'definition_id' => 2], ]; diff --git a/tests/codeception/fixtures/data/richtextContent.php b/tests/codeception/fixtures/data/elementContentContainerItem.php similarity index 63% rename from tests/codeception/fixtures/data/richtextContent.php rename to tests/codeception/fixtures/data/elementContentContainerItem.php index 2b018961..3b7f303e 100644 --- a/tests/codeception/fixtures/data/richtextContent.php +++ b/tests/codeception/fixtures/data/elementContentContainerItem.php @@ -18,10 +18,9 @@ * GNU Affero General Public License for more details. */ return [ - ['id' => 1, 'content' => 'Default
'], - ['id' => 2, 'content' => 'ContainerText
'], + ['id' => 1, 'template_id' => 3, 'element_content_id' => 6, 'sort_order' => 0, 'title' => 'test1'], - ['id' => 3, 'content' => 'ContainerText
'], - ['id' => 4, 'content' => 'ContainerText
'], - ['id' => 5, 'content' => 'ContainerText
'], + ['id' => 2, 'template_id' => 4, 'element_content_id' => 7, 'sort_order' => 0, 'title' => 'test2'], + ['id' => 3, 'template_id' => 4, 'element_content_id' => 7, 'sort_order' => 1, 'title' => 'test3'], + ['id' => 4, 'template_id' => 4, 'element_content_id' => 7, 'sort_order' => 2, 'title' => 'test4'], ]; diff --git a/tests/codeception/fixtures/data/containerContentDefinition.php b/tests/codeception/fixtures/data/elementContentDefinitionContainer.php similarity index 73% rename from tests/codeception/fixtures/data/containerContentDefinition.php rename to tests/codeception/fixtures/data/elementContentDefinitionContainer.php index c91340a2..43d455bf 100644 --- a/tests/codeception/fixtures/data/containerContentDefinition.php +++ b/tests/codeception/fixtures/data/elementContentDefinitionContainer.php @@ -18,6 +18,6 @@ * GNU Affero General Public License for more details. */ return [ - ['id' => 1, 'allow_multiple' => 0, 'is_default' => 1, 'is_inline' => 0], - ['id' => 2, 'allow_multiple' => 1, 'is_default' => 1, 'is_inline' => 1], + ['id' => 1, 'dynAttributes' => json_encode(['allow_multiple' => 0, 'is_inline' => 0, 'templates' => []]), 'is_default' => 1], + ['id' => 2, 'dynAttributes' => json_encode(['allow_multiple' => 1, 'is_inline' => 1, 'templates' => []]), 'is_default' => 1], ]; diff --git a/tests/codeception/fixtures/data/containerContentItem.php b/tests/codeception/fixtures/data/elementContentRichtext.php similarity index 57% rename from tests/codeception/fixtures/data/containerContentItem.php rename to tests/codeception/fixtures/data/elementContentRichtext.php index e3a6db0f..c4d4c981 100644 --- a/tests/codeception/fixtures/data/containerContentItem.php +++ b/tests/codeception/fixtures/data/elementContentRichtext.php @@ -18,9 +18,10 @@ * GNU Affero General Public License for more details. */ return [ - ['id' => 1, 'template_id' => 3, 'container_content_id' => 1, 'sort_order' => 0, 'title' => 'test1'], + ['id' => 1, 'element_id' => 1, 'dynAttributes' => json_encode(['content' => 'Default
'])], + ['id' => 2, 'element_id' => 5, 'dynAttributes' => json_encode(['content' => 'ContainerText
'])], - ['id' => 2, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 0, 'title' => 'test2'], - ['id' => 3, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 1, 'title' => 'test3'], - ['id' => 4, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 2, 'title' => 'test4'], + ['id' => 3, 'element_id' => 6, 'dynAttributes' => json_encode(['content' => 'ContainerText
'])], + ['id' => 4, 'element_id' => 6, 'dynAttributes' => json_encode(['content' => 'ContainerText
'])], + ['id' => 5, 'element_id' => 6, 'dynAttributes' => json_encode(['content' => 'ContainerText
'])], ]; diff --git a/tests/codeception/fixtures/data/ownerContent.php b/tests/codeception/fixtures/data/ownerContent.php index b99d5e08..44db0603 100644 --- a/tests/codeception/fixtures/data/ownerContent.php +++ b/tests/codeception/fixtures/data/ownerContent.php @@ -18,16 +18,16 @@ * GNU Affero General Public License for more details. */ return [ - ['id' => 1, 'element_name' => 'test_content', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\Template', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 1], + ['id' => 1, 'element_name' => 'test_content', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\Template', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\RichtextElement', 'content_id' => 1], // Container of Layout1 - ['id' => 2, 'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\TemplateInstance', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent', 'content_id' => 1], + ['id' => 2, 'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\TemplateInstance', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\ContainerElement', 'content_id' => 6], // Sub Container - ['id' => 3,'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent', 'content_id' => 2], - ['id' => 4,'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 2], + ['id' => 3,'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\elements\ContainerItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\ContainerElement', 'content_id' => 7], + ['id' => 4,'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\elements\ContainerItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\RichtextElement', 'content_id' => 2], - ['id' => 5, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 3], - ['id' => 6, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 3, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 4], - ['id' => 7, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 4, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 5], + ['id' => 5, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\elements\ContainerItem', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\RichtextElement', 'content_id' => 3], + ['id' => 6, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\elements\ContainerItem', 'owner_id' => 3, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\RichtextElement', 'content_id' => 4], + ['id' => 7, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\elements\ContainerItem', 'owner_id' => 4, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\elements\\RichtextElement', 'content_id' => 5], ]; diff --git a/tests/codeception/fixtures/data/templateElement.php b/tests/codeception/fixtures/data/templateElement.php index 58556b4d..51734527 100644 --- a/tests/codeception/fixtures/data/templateElement.php +++ b/tests/codeception/fixtures/data/templateElement.php @@ -18,10 +18,10 @@ * GNU Affero General Public License for more details. */ return [ - ['id' => 1, 'name' => 'test_content', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'], - ['id' => 2, 'name' => 'test_text', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'], - ['id' => 3, 'name' => 'container', 'template_id' => 2, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\ContainerContent'], - ['id' => 4, 'name' => 'container', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\ContainerContent'], - ['id' => 5, 'name' => 'text', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'], - ['id' => 6, 'name' => 'text', 'template_id' => 4, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'], + ['id' => 1, 'name' => 'test_content', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\RichtextElement'], + ['id' => 2, 'name' => 'test_text', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\RichtextElement'], + ['id' => 3, 'name' => 'container', 'template_id' => 2, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\ContainerElement'], + ['id' => 4, 'name' => 'container', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\ContainerElement'], + ['id' => 5, 'name' => 'text', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\RichtextElement'], + ['id' => 6, 'name' => 'text', 'template_id' => 4, 'content_type' => 'humhub\modules\custom_pages\modules\template\elements\RichtextElement'], ]; diff --git a/tests/codeception/unit/template/ContainerContentTest.php b/tests/codeception/unit/template/ContainerElementContentTest.php similarity index 52% rename from tests/codeception/unit/template/ContainerContentTest.php rename to tests/codeception/unit/template/ContainerElementContentTest.php index 04dcb42b..5f359390 100644 --- a/tests/codeception/unit/template/ContainerContentTest.php +++ b/tests/codeception/unit/template/ContainerElementContentTest.php @@ -2,19 +2,18 @@ namespace tests\codeception\unit\modules\custom_page\template; -use humhub\modules\custom_pages\modules\template\models\ContainerContentTemplate; +use humhub\modules\custom_pages\modules\template\elements\ContainerDefinition; +use humhub\modules\custom_pages\modules\template\elements\ContainerElement; +use humhub\modules\custom_pages\modules\template\elements\ContainerItem; +use humhub\modules\custom_pages\modules\template\elements\RichtextElement; use humhub\modules\custom_pages\modules\template\models\TemplateElement; use tests\codeception\_support\HumHubDbTestCase; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\TemplateInstance; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\RichtextContent; use humhub\modules\custom_pages\models\CustomPage; -use humhub\modules\custom_pages\modules\template\models\ContainerContent; -use humhub\modules\custom_pages\modules\template\models\ContainerContentItem; -use humhub\modules\custom_pages\modules\template\models\ContainerContentDefinition; -class ContainerContentTest extends HumHubDbTestCase +class ContainerElementContentTest extends HumHubDbTestCase { public $owner2; public $owner1; @@ -23,66 +22,66 @@ class ContainerContentTest extends HumHubDbTestCase public function testDeleteContainerItem() { - ContainerContentItem::findOne(['id' => 2])->delete(); - ContainerContentItem::findOne(['id' => 3])->delete(); - ContainerContentItem::findOne(['id' => 4])->delete(); + ContainerItem::findOne(['id' => 2])->delete(); + ContainerItem::findOne(['id' => 3])->delete(); + ContainerItem::findOne(['id' => 4])->delete(); $this->assertNull(OwnerContent::findOne(['id' => 5])); $this->assertNull(OwnerContent::findOne(['id' => 6])); $this->assertNull(OwnerContent::findOne(['id' => 7])); - $this->assertNull(RichtextContent::findOne(['id' => 3])); - $this->assertNull(RichtextContent::findOne(['id' => 4])); - $this->assertNull(RichtextContent::findOne(['id' => 5])); + $this->assertNull(RichtextElement::findOne(['id' => 3])); + $this->assertNull(RichtextElement::findOne(['id' => 4])); + $this->assertNull(RichtextElement::findOne(['id' => 5])); } - public function testDeleteContainerContent() + public function testDeleteContainerElementContent() { - $container = ContainerContent::findOne(['id' => 2]); + $container = ContainerElement::findOne(['id' => 7]); $this->assertEquals(3, $container->getItems()->count()); $container->delete(); - $this->assertNull(ContainerContentDefinition::findOne(['id' => 2])); + $this->assertNull(ContainerDefinition::findOne(['id' => 2])); - $this->assertNull(ContainerContentItem::findOne(['id' => 2])); - $this->assertNull(ContainerContentItem::findOne(['id' => 3])); - $this->assertNull(ContainerContentItem::findOne(['id' => 4])); + $this->assertNull(ContainerItem::findOne(['id' => 2])); + $this->assertNull(ContainerItem::findOne(['id' => 3])); + $this->assertNull(ContainerItem::findOne(['id' => 4])); $this->assertNull(OwnerContent::findOne(['id' => 5])); $this->assertNull(OwnerContent::findOne(['id' => 6])); $this->assertNull(OwnerContent::findOne(['id' => 7])); - $this->assertNull(RichtextContent::findOne(['id' => 3])); - $this->assertNull(RichtextContent::findOne(['id' => 4])); - $this->assertNull(RichtextContent::findOne(['id' => 5])); + $this->assertNull(RichtextElement::findOne(['id' => 3])); + $this->assertNull(RichtextElement::findOne(['id' => 4])); + $this->assertNull(RichtextElement::findOne(['id' => 5])); } public function testDeleteParentContainer() { - $container = ContainerContent::findOne(['id' => 1]); + $container = ContainerElement::findOne(['id' => 6]); $container->delete(); - $this->assertNull(ContainerContentItem::findOne(['id' => 2])); - $this->assertNull(ContainerContentItem::findOne(['id' => 3])); - $this->assertNull(ContainerContentItem::findOne(['id' => 4])); + $this->assertNull(ContainerItem::findOne(['id' => 2])); + $this->assertNull(ContainerItem::findOne(['id' => 3])); + $this->assertNull(ContainerItem::findOne(['id' => 4])); $this->assertNull(OwnerContent::findOne(['id' => 5])); $this->assertNull(OwnerContent::findOne(['id' => 6])); $this->assertNull(OwnerContent::findOne(['id' => 7])); - $this->assertNull(RichtextContent::findOne(['id' => 3])); - $this->assertNull(RichtextContent::findOne(['id' => 4])); - $this->assertNull(RichtextContent::findOne(['id' => 5])); + $this->assertNull(RichtextElement::findOne(['id' => 3])); + $this->assertNull(RichtextElement::findOne(['id' => 4])); + $this->assertNull(RichtextElement::findOne(['id' => 5])); } @@ -99,17 +98,17 @@ public function testDeletePage() $page = CustomPage::findOne(['id' => 2]); $this->assertNotFalse($page->hardDelete()); - $this->assertNull(ContainerContentItem::findOne(['id' => 2])); - $this->assertNull(ContainerContentItem::findOne(['id' => 3])); - $this->assertNull(ContainerContentItem::findOne(['id' => 4])); + $this->assertNull(ContainerItem::findOne(['id' => 2])); + $this->assertNull(ContainerItem::findOne(['id' => 3])); + $this->assertNull(ContainerItem::findOne(['id' => 4])); $this->assertNull(OwnerContent::findOne(['id' => 5])); $this->assertNull(OwnerContent::findOne(['id' => 6])); $this->assertNull(OwnerContent::findOne(['id' => 7])); - $this->assertNull(RichtextContent::findOne(['id' => 3])); - $this->assertNull(RichtextContent::findOne(['id' => 4])); - $this->assertNull(RichtextContent::findOne(['id' => 5])); + $this->assertNull(RichtextElement::findOne(['id' => 3])); + $this->assertNull(RichtextElement::findOne(['id' => 4])); + $this->assertNull(RichtextElement::findOne(['id' => 5])); } public function testDeleteAll() @@ -119,11 +118,11 @@ public function testDeleteAll() $this->assertEquals(0, OwnerContent::find()->where(['not', ['owner_model' => Template::class]])->count()); $this->assertEquals(0, TemplateInstance::find()->count()); - $this->assertEquals(1, RichtextContent::find()->count()); - $this->assertEquals(0, ContainerContentItem::find()->count()); - $this->assertEquals(0, ContainerContent::find()->count()); + $this->assertEquals(1, RichtextElement::findByType()->count()); + $this->assertEquals(0, ContainerItem::find()->count()); + $this->assertEquals(0, ContainerElement::findByType()->count()); - $this->assertEquals(0, ContainerContentDefinition::find()->count()); + $this->assertEquals(0, ContainerDefinition::find()->count()); $this->assertEquals(1, Template::findOne(['id' => 1])->delete()); $this->assertEquals(1, Template::findOne(['id' => 2])->delete()); @@ -131,7 +130,7 @@ public function testDeleteAll() $this->assertEquals(1, Template::findOne(['id' => 3])->delete()); $this->assertEquals(1, Template::findOne(['id' => 4])->delete()); - $this->assertEquals(0, RichtextContent::find()->count()); + $this->assertEquals(0, RichtextElement::findByType()->count()); $this->assertEquals(0, TemplateElement::find()->count()); } } diff --git a/tests/codeception/unit/template/TemplateContentTest.php b/tests/codeception/unit/template/TemplateContentTest.php index b1fff837..a42ead92 100644 --- a/tests/codeception/unit/template/TemplateContentTest.php +++ b/tests/codeception/unit/template/TemplateContentTest.php @@ -2,10 +2,11 @@ namespace tests\codeception\unit\modules\custom_page\template; -use tests\codeception\_support\HumHubDbTestCase; use Codeception\Specify; +use humhub\modules\custom_pages\modules\template\elements\RichtextElement; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\TemplateInstance; +use tests\codeception\_support\HumHubDbTestCase; class TemplateContentTest extends HumHubDbTestCase { @@ -21,8 +22,8 @@ public function setUp(): void public function testRenderHtml() { - - $content = new \humhub\modules\custom_pages\modules\template\models\RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Test
'; $content->save(); diff --git a/tests/codeception/unit/template/TemplateElementTest.php b/tests/codeception/unit/template/TemplateElementTest.php index 0dccd483..2d8e054c 100644 --- a/tests/codeception/unit/template/TemplateElementTest.php +++ b/tests/codeception/unit/template/TemplateElementTest.php @@ -2,13 +2,13 @@ namespace tests\codeception\unit\modules\custom_page\template; -use tests\codeception\_support\HumHubDbTestCase; use Codeception\Specify; +use humhub\modules\custom_pages\modules\template\elements\RichtextElement; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\RichtextContent; use humhub\modules\custom_pages\modules\template\models\TemplateInstance; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\TemplateElement; +use tests\codeception\_support\HumHubDbTestCase; class TemplateElementTest extends HumHubDbTestCase { @@ -27,7 +27,7 @@ public function setUp(): void $this->element = TemplateElement::findOne(['id' => 1]); $this->element2 = TemplateElement::findOne(['id' => 2]); - $this->defaultContent1 = RichtextContent::findOne(['id' => 1]); + $this->defaultContent1 = RichtextElement::findOne(['id' => 1]); $this->owner = TemplateInstance::findOne(['id' => 1]); } @@ -39,13 +39,14 @@ public function testRenderDefaultContent() $this->assertStringContainsString('Default
', $result); $this->assertStringContainsString('data-template-element="test_content"', $result); $this->assertStringContainsString('data-template-owner="' . get_class($this->owner) . '"', $result); - $this->assertStringContainsString('data-template-content="' . RichtextContent::class . '"', $result); + $this->assertStringContainsString('data-template-content="' . RichtextElement::class . '"', $result); $this->assertStringContainsString('data-template-empty="0"', $result); } public function testOverwriteDefaultContent() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Non Default
'; $this->element->saveInstance($this->owner, $content); @@ -55,7 +56,7 @@ public function testOverwriteDefaultContent() $this->assertStringContainsString('Non Default
', $result); $this->assertStringContainsString('data-template-element="test_content"', $result); $this->assertStringContainsString('data-template-owner="' . get_class($this->owner) . '"', $result); - $this->assertStringContainsString('data-template-content="' . RichtextContent::class . '"', $result); + $this->assertStringContainsString('data-template-content="' . RichtextElement::class . '"', $result); $this->assertStringContainsString('data-template-empty="0"', $result); // Test empty element $this->assertStringContainsString('data-template-empty="1"', $result); @@ -63,7 +64,8 @@ public function testOverwriteDefaultContent() public function testOverwriteEmptyDefaultContent() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Non Default2
'; $this->element2->saveInstance($this->owner, $content); @@ -77,12 +79,14 @@ public function testOverwriteEmptyDefaultContent() public function testOverwriteOldContent() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Non Default2
'; $this->element2->saveInstance($this->owner, $content); - $content2 = new RichtextContent(); + $content2 = new RichtextElement(); + $content2->element_id = 1; $content2->content = 'Non Default New
'; $content2->save(); @@ -91,12 +95,13 @@ public function testOverwriteOldContent() $result = $this->template->render($this->owner, true); $this->assertStringContainsString('Non Default New
', $result); - $this->assertNull(RichtextContent::findOne(['id' => $content->id])); + $this->assertNull(RichtextElement::findOne(['id' => $content->id])); } public function testSaveAsDefaultContent() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Default2
'; $content->save(); $this->element->saveAsDefaultContent($content); @@ -105,7 +110,7 @@ public function testSaveAsDefaultContent() $this->assertStringContainsString('Default2
', $result); // Get sure the old default content was removed - $this->assertNull(RichtextContent::findOne(['id' => $this->defaultContent1->id])); + $this->assertNull(RichtextElement::findOne(['id' => $this->defaultContent1->id])); } public function testUniqueTemplateElementName() @@ -113,7 +118,7 @@ public function testUniqueTemplateElementName() $newElement = new TemplateElement(); $newElement->scenario = 'create'; $newElement->name = 'test_content'; - $newElement->content_type = RichtextContent::class; + $newElement->content_type = RichtextElement::class; $newElement->template_id = $this->template->id; $newElement->save(); @@ -127,11 +132,13 @@ public function testGetDefaultContent() public function testDeleteElement() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 2; $content->content = 'Non Default
'; $content->save(); - $content2 = new RichtextContent(); + $content2 = new RichtextElement(); + $content2->element_id = 2; $content2->content = 'Non Default2
'; $content2->save(); @@ -144,9 +151,9 @@ public function testDeleteElement() $this->element->delete(); $this->assertNull(OwnerContent::findOne(['id' => $defaultContent->id])); - $this->assertNull(RichtextContent::findOne(['id' => $content->id])); + $this->assertNull(RichtextElement::findOne(['id' => $content->id])); - $this->assertNotNull(RichtextContent::findOne(['id' => $content2->id])); + $this->assertNotNull(RichtextElement::findOne(['id' => $content2->id])); $this->assertNull($this->template->getElement('test_content')); $this->assertNotNull($this->template->getElement('test_text')); @@ -154,11 +161,13 @@ public function testDeleteElement() public function testDeleteTemplate() { - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Non Default
'; $content->save(); - $content2 = new RichtextContent(); + $content2 = new RichtextElement(); + $content2->element_id = 1; $content2->content = 'Non Default2
'; $content2->save(); @@ -176,6 +185,6 @@ public function testDeleteTemplate() $this->assertEquals('1', $this->template->delete()); $this->assertNull(OwnerContent::findOne(['id' => $defaultContent->id])); - $this->assertNull(RichtextContent::findOne(['id' => $content->id])); + $this->assertNull(RichtextElement::findOne(['id' => $content->id])); } } diff --git a/tests/codeception/unit/template/TemplateInstanceTest.php b/tests/codeception/unit/template/TemplateInstanceTest.php index 7153faed..33a0ab01 100644 --- a/tests/codeception/unit/template/TemplateInstanceTest.php +++ b/tests/codeception/unit/template/TemplateInstanceTest.php @@ -2,12 +2,12 @@ namespace tests\codeception\unit\modules\custom_page\template; +use humhub\modules\custom_pages\modules\template\elements\RichtextElement; use tests\codeception\_support\HumHubDbTestCase; use Codeception\Specify; use humhub\modules\custom_pages\modules\template\models\OwnerContent; use humhub\modules\custom_pages\modules\template\models\TemplateInstance; use humhub\modules\custom_pages\modules\template\models\Template; -use humhub\modules\custom_pages\modules\template\models\RichtextContent; use humhub\modules\custom_pages\models\CustomPage; class TemplateInstanceTest extends HumHubDbTestCase @@ -52,7 +52,7 @@ public function testDeleteOwner() $template = Template::findOne(['id' => 1]); $element = $template->getElement('test_content'); - $richtext = new RichtextContent(['content' => 'testContent']); + $richtext = new RichtextElement(['element_id' => 1, 'content' => 'testContent']); $ownerContent = $element->saveInstance($owner2, $richtext); $ownerTestContent = $element->getOwnerContent($owner2); @@ -65,7 +65,7 @@ public function testDeleteOwner() $owner2->delete(); $this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id])); - $this->assertNull(RichtextContent::findOne(['id' => $richtext->id])); + $this->assertNull(RichtextElement::findOne(['id' => $richtext->id])); } @@ -84,14 +84,14 @@ public function testDeleteByOwner() $owner = TemplateInstance::findOne(['page_id' => $page->id]); - $richtext = new RichtextContent(['content' => 'testContent']); + $richtext = new RichtextElement(['element_id' => 1, 'content' => 'testContent']); $ownerContent = $element->saveInstance($owner, $richtext); TemplateInstance::deleteByOwner($page); $this->assertNull(TemplateInstance::findOne(['id' => $owner->id])); $this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id])); - $this->assertNull(RichtextContent::findOne(['id' => $richtext->id])); + $this->assertNull(RichtextElement::findOne(['id' => $richtext->id])); } public function testDeletePage() @@ -109,7 +109,7 @@ public function testDeletePage() $owner = TemplateInstance::findOne(['page_id' => $page->id]); - $richtext = new RichtextContent(['content' => 'testContent']); + $richtext = new RichtextElement(['element_id' => 1, 'content' => 'testContent']); $ownerContent = $element->saveInstance($owner, $richtext); $this->assertFalse($richtext->isNewRecord); @@ -119,7 +119,7 @@ public function testDeletePage() $this->assertNull(TemplateInstance::findOne(['id' => $owner->id])); $this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id])); - $this->assertNull(RichtextContent::findOne(['id' => $richtext->id])); + $this->assertNull(RichtextElement::findOne(['id' => $richtext->id])); } public function testFindByOwner() @@ -148,15 +148,18 @@ public function testFindByOwner() $owner2->save(); - $content = new RichtextContent(); + $content = new RichtextElement(); + $content->element_id = 1; $content->content = 'Test
'; $content->save(); - $content2 = new RichtextContent(); + $content2 = new RichtextElement(); + $content2->element_id = 1; $content2->content = 'Test
'; $content2->save(); - $content3 = new RichtextContent(); + $content3 = new RichtextElement(); + $content3->element_id = 1; $content3->content = 'Test
'; $content3->save(); diff --git a/tests/config/test.php b/tests/config/test.php index 22c8783b..e468de61 100644 --- a/tests/config/test.php +++ b/tests/config/test.php @@ -5,14 +5,13 @@ 'fixtures' => [ 'default', 'template' => 'tests\codeception\fixtures\modules\custom_pages\template\TemplateFixture', - 'containerContent' => 'tests\codeception\fixtures\modules\custom_pages\template\ContainerContentFixture', 'templateInstance' => 'tests\codeception\fixtures\modules\custom_pages\template\TemplateInstanceFixture', 'templateElement' => 'tests\codeception\fixtures\modules\custom_pages\template\TemplateElementFixture', - 'richtextContent' => 'tests\codeception\fixtures\modules\custom_pages\template\RichtextContentFixture', + 'elementContentRichtext' => 'tests\codeception\fixtures\modules\custom_pages\template\ElementContentRichtextFixture', + 'elementContentContainer' => 'tests\codeception\fixtures\modules\custom_pages\template\ElementContentContainerFixture', + 'elementContentDefinitionContainer' => 'tests\codeception\fixtures\modules\custom_pages\template\ElementContentDefinitionContainerFixture', + 'elementContentContainerItem' => 'tests\codeception\fixtures\modules\custom_pages\template\ElementContentContainerItemFixture', 'ownerContent' => 'tests\codeception\fixtures\modules\custom_pages\template\OwnerContentFixture', - 'containerContentDefinition' => 'tests\codeception\fixtures\modules\custom_pages\template\ContainerContentDefinitionFixture', - 'containerContentTemplate' => \tests\codeception\fixtures\modules\custom_pages\template\ContainerContentTemplateFixture::class, - 'containerContentItem' => 'tests\codeception\fixtures\modules\custom_pages\template\ContainerContentItemFixture', 'page' => 'tests\codeception\fixtures\modules\custom_pages\template\CustomPageFixture', ], ];