feat: Articles Field, Content Templater — complete Regular Labs suite
Universal: Auto Version Bump / Version Bump (push) Successful in 24s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 30s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 59s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 56s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 51s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 55s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Successful in 24s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 30s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 59s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 56s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 51s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 55s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Articles Field (#162): custom ListField type for article selection in any Joomla form, shows "Title [Category]" options - Content Templater (#165): {template alias="x"} tags load pre-defined article templates from DB, returns introtext+fulltext from JSON - content_templates table added to install.mysql.sql - All Regular Labs replacements now implemented: #160 conditions + AMM, #161 articles anywhere, #162 articles field, #164 conditional content, #165 content templater, #167 email protector, #175 rereplacer, #176 snippets, #177 sourcerer, #180 users anywhere, #181 cache cleaner
This commit is contained in:
@@ -326,3 +326,27 @@ CREATE TABLE IF NOT EXISTS `#__mokosuiteclient_replacements` (
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_published` (`published`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
--
|
||||
-- Content Templates
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `#__mokosuiteclient_content_templates` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`alias` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`name` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`description` TEXT NOT NULL,
|
||||
`category` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`color` VARCHAR(8) DEFAULT NULL,
|
||||
`template_data` MEDIUMTEXT NOT NULL,
|
||||
`joomla_category_id` INT NOT NULL DEFAULT 0,
|
||||
`access` INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
`published` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`ordering` INT NOT NULL DEFAULT 0,
|
||||
`checked_out` INT UNSIGNED DEFAULT NULL,
|
||||
`checked_out_time` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_published` (`published`),
|
||||
KEY `idx_alias` (`alias`),
|
||||
KEY `idx_category` (`joomla_category_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@@ -3023,6 +3023,14 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
|
||||
$modified = true;
|
||||
}
|
||||
|
||||
if (stripos($text, '{template') !== false
|
||||
&& (int) $this->params->get('content_templates_enabled', 0) === 1
|
||||
&& $this->contentTemplatesTableExists())
|
||||
{
|
||||
$this->processTemplateTags($text);
|
||||
$modified = true;
|
||||
}
|
||||
|
||||
if (stripos($text, '{article') !== false && (int) $this->params->get('articles_anywhere_enabled', 0) === 1)
|
||||
{
|
||||
$this->processArticleTags($text);
|
||||
@@ -3119,6 +3127,14 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if (stripos($body, '{template') !== false
|
||||
&& (int) $this->params->get('content_templates_enabled', 0) === 1
|
||||
&& $this->contentTemplatesTableExists())
|
||||
{
|
||||
$this->processTemplateTags($body);
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if (stripos($body, '{article') !== false
|
||||
&& (int) $this->params->get('articles_anywhere_enabled', 0) === 1)
|
||||
{
|
||||
@@ -3939,4 +3955,95 @@ class MokoSuiteClient extends CMSPlugin implements BootableExtensionInterface
|
||||
};
|
||||
}, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process {template alias="..."} content tags.
|
||||
*
|
||||
* Loads a content template from the database, decodes the JSON
|
||||
* `template_data` column, and returns the concatenation of its
|
||||
* `introtext` and `fulltext` fields.
|
||||
*
|
||||
* @param string &$text The text to process (modified in place).
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 02.48.00
|
||||
*/
|
||||
private function processTemplateTags(string &$text): void
|
||||
{
|
||||
if (!$this->params->get('content_templates_enabled', 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$pattern = '#\{template\s+([^}]+)\}#i';
|
||||
|
||||
$text = preg_replace_callback($pattern, function ($match) {
|
||||
$attrs = $this->parseTagAttributes($match[1]);
|
||||
$alias = $attrs['alias'] ?? $attrs['id'] ?? '';
|
||||
|
||||
if (empty($alias))
|
||||
{
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class);
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('template_data'))
|
||||
->from($db->quoteName('#__mokosuiteclient_content_templates'))
|
||||
->where($db->quoteName('published') . ' = 1');
|
||||
|
||||
if (is_numeric($alias))
|
||||
{
|
||||
$query->where($db->quoteName('id') . ' = ' . (int) $alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query->where($db->quoteName('alias') . ' = ' . $db->quote($alias));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
$data = json_decode($db->loadResult() ?? '{}');
|
||||
|
||||
return ($data->introtext ?? '') . ($data->fulltext ?? '');
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the content_templates DB table exists.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 02.48.00
|
||||
*/
|
||||
private function contentTemplatesTableExists(): bool
|
||||
{
|
||||
static $exists = null;
|
||||
|
||||
if ($exists !== null)
|
||||
{
|
||||
return $exists;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class);
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
$exists = \in_array($prefix . 'mokosuiteclient_content_templates', $tables, true);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$exists = false;
|
||||
}
|
||||
|
||||
return $exists;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @package MokoSuiteClient
|
||||
* @subpackage plg_system_mokosuiteclient
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* VERSION: 02.47.62
|
||||
* PATH: /src/Field/ArticlesField.php
|
||||
* BRIEF: List field that populates with published Joomla articles
|
||||
*/
|
||||
|
||||
namespace Moko\Plugin\System\MokoSuiteClient\Field;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Form\Field\ListField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
* Form field that renders a dropdown (or multi-select) of all published
|
||||
* articles, grouped by category name.
|
||||
*
|
||||
* Usage in XML:
|
||||
* <field name="related_article" type="Articles" label="Related Article" multiple="true" />
|
||||
*
|
||||
* @since 02.47.62
|
||||
*/
|
||||
class ArticlesField extends ListField
|
||||
{
|
||||
protected $type = 'Articles';
|
||||
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$options = parent::getOptions();
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
$query = $db->getQuery(true)
|
||||
->select([
|
||||
$db->quoteName('a.id', 'value'),
|
||||
$db->quoteName('a.title', 'text'),
|
||||
$db->quoteName('c.title', 'category'),
|
||||
])
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->leftJoin($db->quoteName('#__categories', 'c') . ' ON c.id = a.catid')
|
||||
->where($db->quoteName('a.state') . ' = 1')
|
||||
->order($db->quoteName('a.title') . ' ASC');
|
||||
|
||||
$db->setQuery($query);
|
||||
$articles = $db->loadObjectList() ?: [];
|
||||
|
||||
foreach ($articles as $article) {
|
||||
$label = $article->text;
|
||||
if (!empty($article->category)) {
|
||||
$label .= ' [' . $article->category . ']';
|
||||
}
|
||||
$options[] = HTMLHelper::_('select.option', $article->value, $label);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,14 @@
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
|
||||
<field name="content_templates_enabled" type="radio" default="0"
|
||||
label="Content Templates"
|
||||
description="Enable {template alias="name"} content tags. Loads structured template data from the content_templates table and renders introtext + fulltext."
|
||||
class="btn-group btn-group-yesno">
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
|
||||
<field name="articles_anywhere_enabled" type="radio" default="0"
|
||||
label="Articles Anywhere"
|
||||
description="Enable {article id="42"}[title]{/article} content tags. Insert article data anywhere using template placeholders for title, introtext, author, category, dates, images, and more."
|
||||
|
||||
Reference in New Issue
Block a user