插件教程
介绍
在本节中,我们将开发一个基本的 内容插件,以提供类似于 Wordpress 中的短代码功能的功能。这样,我们就可以在文章中包含对某个字段的引用。
On my Joomla instance {sitename} the default editor is {editor}.
这些字段将被限制在全局参数(在 configuration.php
中定义)中,并且该插件将修改文章以输出这些参数的值,因此显示的内容类似于以下内容
在我的 Joomla 实例 j442 中,默认编辑器是 tinymce。
此外,该插件还演示了以下内容的使用:
- 语言常量 - 既在清单文件中,又在插件代码中
- 从插件方法返回一个值 - 通过使用
onContentAfterTitle
事件。该插件代码在文章标题之后添加了一些文本。
您可以在 Joomla 4 和 Joomla 5 实例上测试此插件,以查看获取参数和返回结果方面的差异(如 Joomla 4 和 5 的更改 中所述)。
该图显示了要编写的插件文件,或者您可以从 短代码插件下载 下载插件的 zip 文件。
清单文件
有关清单文件的常规信息,请参阅 清单文件。
<?xml version="1.0" encoding="utf-8"?>
<extension method="upgrade" type="plugin" group="content">
<name>PLG_CONTENT_SHORTCODES</name>
<version>1.0</version>
<description>PLG_CONTENT_SHORTCODES_DESCRIPTION</description>
<author>Me</author>
<creationDate>Today</creationDate>
<copyright>(C) 2024 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later</license>
<namespace path="src">My\Plugin\Content\Shortcodes</namespace>
<files>
<folder plugin="shortcodes">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.ini</language>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.sys.ini</language>
</languages>
</extension>
在处理插件清单文件时,Joomla 非常挑剔,很容易出现错误,并且会让人疑惑为什么插件无法正常工作。以下是一些必须正确设置的内容。
插件类型/组
<extension method="upgrade" type="plugin" group="content">
之前的部分将插件类型描述为“内容”、“系统”等,但这里 type
是“插件”(因为它是扩展的类型),而 group
指的是插件类型。
语言常量
<name>PLG_CONTENT_SHORTCODES</name>
<description>PLG_CONTENT_SHORTCODES_DESCRIPTION</description>
您不必在此处使用语言字符串,但如果使用,则应在语言 .sys.ini
文件中提供它们的值。(名称和描述在管理员插件表单中显示)。
命名空间
<namespace path="src">My\Plugin\Content\Shortcodes</namespace>
严格来说,您不必遵循 Joomla 推荐的以下方法:
Mycompany\Plugin\<plugin type>\<plugin name>
但是,您必须确保此处的命名空间前缀与以下内容匹配:
use
语句在services/provider.php
文件中,以及namespace
语句在您的主扩展类中,
并且所有类都位于 path
属性中指定的 /src
文件夹下。
如果遇到命名空间问题,则检查 administrator/cache/autoload_psr4.php
可能会有帮助,以验证插件的命名空间前缀是否指向您预期的位置。只要安装任何扩展,就会重新生成此缓存文件(前提是启用了“扩展 - 命名空间更新器”插件),但如果您直接在 Joomla 网站代码中进行更改,则可能需要删除此缓存文件;在您下次浏览 Joomla 实例时,它将被重新生成。
插件入口点
<files>
<folder plugin="shortcodes">services</folder>
<folder>src</folder>
</files>
通过在文件夹上使用 plugin="shortcodes"
,确保您指定插件的入口点在哪里。这也映射到管理员插件表单中的 element
字段。
确保清单 XML 文件的名称与该插件属性匹配(即,必须命名为 shortcodes.xml
)。否则,Joomla 会正常安装您的插件,但不会正确构建命名空间。
语言文件
<languages>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.ini</language>
<language tag="en-GB">language/en-GB/plg_content_shortcodes.sys.ini</language>
</languages>
确保插件语言文件的命名正确。您必须在文件名中包含:
- 插件类型 - 与
<extension group="...">
属性匹配,以及 - 插件元素 - 与
<folder plugin="...">
属性匹配。
服务提供者文件
<?php
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use My\Plugin\Content\Shortcodes\Extension\Shortcode;
return new class() implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$config = (array) PluginHelper::getPlugin('content', 'shortcodes');
$subject = $container->get(DispatcherInterface::class);
$app = Factory::getApplication();
$plugin = new Shortcode($subject, $config);
$plugin->setApplication($app);
return $plugin;
}
);
}
};
这基本上是用于从依赖项注入容器中获取插件的样板代码,您只需要更改似乎是 Joomla 核心插件中的标准代码中的 3 行。
use My\Plugin\Content\Shortcodes\Extension\Shortcode;
确保这与清单文件中的 <namespace>
以及扩展类文件中的 namespace
语句和类名一致。 。
$config = (array) PluginHelper::getPlugin('content', 'shortcodes');
确保这包含您的插件类型和元素,与清单文件匹配。
$plugin = new Shortcode($subject, $config);
确保这与 src/Extension
目录中的类匹配。
扩展类
这是插件的主要代码。希望代码中的注释能够解释正在发生的事情。
如 Joomla 4 和 5 的更改 中所述,触发事件的代码可以使用 GenericEvent
或具体的事件,例如 ContentPrepareEvent
。在这两种情况下,您都可以使用以下方法获取参数:
[$context, $article, $params, $page] = array_values($event->getArguments());
但您必须在代码中检查如何返回结果。
使用这种方法意味着,即使触发事件的代码从使用泛型事件类更改为使用具体的事件类,您也不需要更改插件代码。
<?php
namespace My\Plugin\Content\Shortcodes\Extension;
// no direct access
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Event\Result\ResultAwareInterface;
class Shortcode extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'replaceShortcodes',
'onContentAfterTitle' => 'addShortcodeSubtitle',
];
}
// this will be called whenever the onContentPrepare event is triggered
public function replaceShortcodes(Event $event)
{
/* This function processes the text of an article being presented on the site.
* It replaces any text of the form "{configname}" (where configname is the name
* of a config parameter in configuration.php) with the value of the parameter.
*
* This is similar to shortcodes functionality within wordpress
*/
// The line below restricts the functionality to the site (ie not on api)
// You may not want this, so you need to consider this in your own plugins
if (!$this->getApplication()->isClient('site')) {
return;
}
// use this format to get the arguments for both Joomla 4 and Joomla 5
// In Joomla 4 a generic Event is passed
// In Joomla 5 a concrete ContentPrepareEvent is passed
[$context, $article, $params, $page] = array_values($event->getArguments());
if ($context !== "com_content.article" && $context !== "com_content.featured") return;
$text = $article->text; // text of the article
$config = Factory::getApplication()->getConfig()->toArray(); // config params as an array
// (we can't do a foreach over the config params as a Registry because they're protected)
// the following is just code to replace {configname} with the parameter value
$offset = 0;
// find opening curly brackets ...
while (($start = strpos($text, "{", $offset)) !== false) {
// find the corresponding closing bracket and extract the "shortcode"
if ($end = strpos($text, "}", $start)) {
$shortcode = substr($text, $start + 1, $end - $start - 1);
// cycle through the config array looking for a match
$match_found = false;
foreach ($config as $key => $value) {
if ($key === $shortcode) {
$text = substr_replace($text, htmlspecialchars($value), $start, $end - $start + 1);
$match_found = true;
break;
}
}
// if no match found replace it with an error string
if (!$match_found) {
$this->loadLanguage(); // you need to load the plugin's language constants before using them
// (alternatively you can set: protected $autoloadLanguage = true; and Joomla will load it for you)
$text = substr_replace($text, Text::_('PLG_CONTENT_SHORTCODES_NO_MATCH'), $start, $end - $start + 1);
}
} else {
break;
}
$offset = $end;
}
// now update the article text with the processed text
$article->text = $text;
}
public function addShortcodeSubtitle(Event $event)
{
if (!$this->getApplication()->isClient('site')) {
return;
}
[$context, $article, $params, $page] = array_values($event->getArguments());
if ($context !== "com_content.article" && $context !== "com_content.featured") return;
$eventType = method_exists($event, 'getContext') ? "concrete event class" : "generic event class";
if ($event instanceof ResultAwareInterface) {
$event->addResult("{$eventType} via addResult");
} else {
$result = $event->getArgument('result') ?? [];
$result[] = "{$eventType} via setArgument";
$event->setArgument('result', $result);
}
}
}
语言文件
清单文件中使用的语言常量
PLG_CONTENT_SHORTCODES="Shortcodes Plugin"
PLG_CONTENT_SHORTCODES_DESCRIPTION="Replaces article shortcodes (in {} brackets) with field values"
插件代码中使用的语言常量
PLG_CONTENT_SHORTCODES_NO_MATCH="Error: no match for shortcode found"
安装
将文件复制到本地文件系统后,您可以将目录压缩并安装扩展。请记住启用插件!
编写一篇包含以下内容的文章:
On my Joomla instance {sitename} the default editor is {editor}.
然后导航到显示该文章的 Joomla 网站菜单项,无论是作为单篇文章还是作为精选文章之一。
该代码应在 Joomla 4 和 Joomla 5 上都能正常工作,并且还应在文章标题之后显示有关所用事件类类型以及如何返回值的信息。