Joomla Developer Manual
Manual Index
Esempio CLI - Onoffbydate
Introduzione
Ci sono occasioni in cui un sito deve mostrare o nascondere un modulo a seconda della data. Un esempio potrebbe essere un modulo HTML personalizzato che visualizza un messaggio durante l'inverno. Un altro potrebbe essere l'alternanza di moduli personalizzati a seconda del giorno della settimana, ad esempio uno per i giorni feriali e un altro per i fine settimana. L'esempio presentato qui utilizza un plugin, un comando cli e un cron per ottenere l'effetto desiderato.
Il codice è disponibile su GitHub
Ci sono più articoli sull'uso della CLI nel Manuale utente e nella Documentazione Joomla! Programmatori copia locale o origine originale;
Standard di Joomla
Nelle versioni precedenti di Joomla, il sistema di plugin utilizzava un'implementazione del pattern Observable/Observer. Di conseguenza, ogni plugin caricato avrebbe registrato immediatamente tutti i suoi metodi pubblici come osservatori. Questo potrebbe causare problemi.
Joomla 4 e versioni successive utilizzano il pacchetto Joomla Framework Event per gestire gli Eventi dei plugin. Questo garantisce migliori prestazioni e sicurezza. In termini pratici, significa che la struttura dei file di un plugin Joomla 4/5/6 è diversa dalla struttura del plugin Legacy delle versioni precedenti.
La Struttura del Plugin
Il plugin si chiama onoffbydate. Il seguente schema mostra la struttura dei file utilizzata per lo sviluppo locale del plugin:
cefjdemos-plg-onoffbydate
|-- .vscode (folder for build settings)
|-- cefjdemos-plg-onoffbydate (folder compressed to create an installable zip file)
|-- language
|-- en-GB (language folder, kept with the extension code)
|-- plg_system_onoffbydate.ini
|-- plg_system_onoffbydate.sys.ini
|-- services (folder for dependency injection)
|-- provider.php (the DI code)
|-- src (folder for namespaced classes)
|-- Console (folder for extension specific code)
|-- OnoffbydateCommand.php (the plugin execution code)
|-- Extension
|-- Onoffbydate.php (the plugin register code)
|-- onoffbydate.xml (the manifest file)
|-- .gitignore (items not to include in the git repository)
|-- build.xml (instructions for building the extension with phing)
|-- changelog.xml (a record of changes in each release)
|-- README.md (brief description and instructions on use)
|-- updates.xml (the update server specification)
Il file manifesto onoffbydate.xml
Questo è il file di installazione - un elemento standard per qualsiasi estensione.
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="console" method="upgrade">
<name>plg_console_onoffbydate</name>
<author>Clifford E Ford</author>
<creationDate>Jamuary 2025</creationDate>
<copyright>(C) Clifford E Ford</copyright>
<license>GNU General Public License version 3 or later</license>
<authorEmail>cliff@xxx.yyy.co.uk</authorEmail>
<version>0.3.0</version>
<description>PLG_CONSOLE_ONOFFBYDATE_DESCRIPTION</description>
<namespace path="src">Cefjdemos\Plugin\Console\Onoffbydate</namespace>
<files>
<folder>language</folder>
<folder plugin="onoffbydate">services</folder>
<folder>src</folder>
</files>
<changelogurl>https://raw.githubusercontent.com/ceford/cefjdemos-plg-onoffbydate/main/changelog.xml</changelogurl>
<updateservers>
<!-- Note: No spaces or linebreaks allowed between the server tags -->
<server type="extension" priority="2" name="Cefjdemosonoffbydate Update Site">https://raw.githubusercontent.com/ceford/cefjdemos-plg-onoffbydate/main/updates.xml</server>
</updateservers>
<config>
<fields>
</fields>
</config>
</extension>
- La linea namespace indica a Joomla dove trovare il codice con namespace per questo plugin.
- La cartella language è mantenuta con il codice del plugin.
- Un updateserver è necessario per soddisfare i requisiti del JED.
- Non ci sono opzioni di config per questo plugin ma potrebbe essere modificato in futuro. Ad esempio, l'utente potrebbe preferire impostare i mesi invernali utilizzando caselle di controllo anziché averli codificati in modo fisso.
Registrazione: services/provider.php
Questo è il punto di ingresso per il codice del plugin.
<?php
/**
* @package Cefjdemos.Plugin
* @subpackage Console.Onoffbydate
*
* @copyright Copyright (C) 2025 Clifford E Ford.
* @license GNU General Public License version 3 or later.
*/
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\CMS\Factory;
use Joomla\Event\DispatcherInterface;
use Cefjdemos\Plugin\Console\Onoffbydate\Extension\Onoffbydate;
return new class implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new Onoffbydate(
$dispatcher,
(array) PluginHelper::getPlugin('console', 'onoffbydate')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
Iscrizione all'evento: src/Extension/Onoffbydate.php
Questo è il luogo in cui viene registrato l'evento che attiva il plugin e viene impostata la posizione del codice che gestirà l'evento.
<?php
/**
* @package Cefjdemos.Plugin
* @subpackage Console.Onoffbydate
*
* @copyright Copyright (C) 2025 Clifford E Ford.
* @license GNU General Public License version 3 or later.
*/
namespace Cefjdemos\Plugin\Console\Onoffbydate\Extension;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Cefjdemos\Plugin\Console\Onoffbydate\Console\OnoffbydateCommand;
final class Onoffbydate extends CMSPlugin implements SubscriberInterface
{
/**
* Returns the event this subscriber will listen to.
*
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
\Joomla\Application\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands',
];
}
/**
* Returns the command class for the Onoffbydate CLI plugin.
*
* @return void
*/
public function registerCommands(): void
{
$myCommand = new OnoffbydateCommand();
$myCommand->setParams($this->params);
$this->getApplication()->addCommand($myCommand);
}
}
La classe OnoffbydateCommand si trova nella cartella src/Console poiché i comandi della Console integrata di Joomla si trovano in una cartella Console. Avrebbe potuto essere posizionata in una cartella Extension.
Il File di Comando: src/Console/OnoffbydateCommand.php
Questo file contiene il codice che svolge tutto il lavoro per questa estensione.
<?php
/**
* @package Cefjdemos.Plugin
* @subpackage Console.Onoffbydate
*
* @copyright Copyright (C) 2025 Clifford E Ford.
* @license GNU General Public License version 3 or later.
*/
namespace Cefjdemos\Plugin\Console\Onoffbydate\Console;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Database\DatabaseInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Joomla\Database\DatabaseAwareTrait;
class OnoffbydateCommand extends AbstractCommand
{
use DatabaseAwareTrait;
/**
* The default command name
*
* @var string
*
* @since 4.0.0
*/
protected static $defaultName = 'onoffbydate:action';
/**
* @var InputInterface
* @since version
*/
private $cliInput;
/**
* SymfonyStyle Object
* @var SymfonyStyle
* @since 4.0.0
*/
private $ioStyle;
/**
* The params associated with the plugin, plus getter and setter
* These are injected into this class by the plugin instance
*/
protected $params;
protected function getParams()
{
return $this->params;
}
public function setParams($params)
{
$this->params = $params;
}
/**
* Initialise the command.
*
* @return void
*
* @since 4.0.0
*/
protected function configure(): void
{
$lang = Factory::getApplication()->getLanguage();
$test = $lang->load(
'plg_console_onoffbydate',
JPATH_BASE . '/plugins/console/onoffbydate'
);
$this->addArgument(
'season',
InputArgument::REQUIRED,
'winter or weekend'
);
$this->addArgument(
'action',
InputArgument::REQUIRED,
'publish or unpublish'
);
$this->addArgument(
'module_id',
InputArgument::REQUIRED,
'module id'
);
$help = Text::_('PLG_CONSOLE_ONOFFBYDATE_HELP_1');
$help .= Text::_('PLG_CONSOLE_ONOFFBYDATE_HELP_2');
$help .= Text::_('PLG_CONSOLE_ONOFFBYDATE_HELP_3');
$help .= Text::_('PLG_CONSOLE_ONOFFBYDATE_HELP_4');
$help .= Text::_('PLG_CONSOLE_ONOFFBYDATE_HELP_5');
$this->setDescription(Text::_('PLG_CONSOLE_ONOFFBYDATE_DESCRIPTION'));
$this->setHelp($help);
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 4.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$this->configureIO($input, $output);
$season = $this->cliInput->getArgument('season');
$action = $this->cliInput->getArgument('action');
$module_id = $this->cliInput->getArgument('module_id');
switch ($season) {
case 'winter':
$result = $this->winter($module_id, $action);
break;
case 'weekend':
$result = $this->weekend($module_id, $action);
break;
default:
$result = Text::_('PLG_CONSOLE_ONOFFBYDATE_ERROR', $season);
$this->ioStyle->error($result);
return 0;
}
return 1;
}
protected function publish($module_id, $published)
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
//$db = $this->getDatabase();
$query = $db->getQuery(true);
$query->update('#__modules')
->set('published = ' . $published)
->where('id = ' . $module_id);
$db->setQuery($query);
$db->execute();
}
protected function weekend($module_id, $action)
{
// get the day of the week
$day = date('w');
if (in_array($day, array(0,6))) {
$msg = Text::_('PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_IN_A_WEEKEND');
$published = $action === 'publish' ? 1 : 0;
} else {
$msg = Text::_('PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_NOT_IN_A_WEEKEND');
$published = $action === 'publish' ? 0 : 1;
}
$this->publish($module_id, $published);
$state = empty($published) ? Text::_('JUNPUBLISHED') : Text::_('JPUBLISHED');
$result = Text::sprintf('PLG_CONSOLE_ONOFFBYDATE_SUCCESS', $msg, $module_id, $state);
$this->ioStyle->success($result);
}
protected function winter($module_id, $action)
{
// get the month of the month
$month = date('n');
if (in_array($month, array(1,2,11,12))) {
$msg = Text::_('PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_IN_WINTER');
$published = $action === 'publish' ? 1 : 0;
} else {
$msg = Text::_('PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_NOT_IN_WINTER');
$published = $action === 'publish' ? 0 : 1;
}
$this->publish($module_id, $published);
$state = empty($published) ? Text::_('JUNPUBLISHED') : Text::_('JPUBLISHED');
$state = empty($published) ? Text::_('JUNPUBLISHED') : Text::_('JPUBLISHED');
$result = Text::sprintf('PLG_CONSOLE_ONOFFBYDATE_SUCCESS', $msg, $module_id, $state);
$this->ioStyle->success($result);
}
/**
* Configures the IO
*
* @param InputInterface $input Console Input
* @param OutputInterface $output Console Output
*
* @return void
*
* @since 4.0.0
*
*/
private function configureIO(InputInterface $input, OutputInterface $output)
{
$this->cliInput = $input;
$this->ioStyle = new SymfonyStyle($input, $output);
}
}
- La funzione
configurestabilisce che sono richiesti tre argomenti da riga di comando:seasondeve essere uno traweekendowinter.actiondeve essere uno trapublishounpublishmodule_iddeve essere l'ID intero del modulo da pubblicare o non pubblicare.
- La funzione
doExecuteè dove viene eseguito il lavoro. Sono riconosciute due opzioni di azione:winterchiama una funzione per pubblicare o non pubblicare un modulo se oggi è nei mesi invernali.weekendchiama una funzione per pubblicare o non pubblicare un modulo se oggi è un giorno del weekend.
- La funzione
publisheffettua una chiamata al database per impostare il campopublishesdel modulo a0o1come appropriato. - Le funzioni
wintereweekendeffettuano alcuni calcoli sulla data per determinare se oggi è in inverno (o meno) o in un weekend (o meno) per passare i parametri appropriati alla funzione di pubblicazione. - La chiamata alla funzione
$this->ioStyle->success()produce un messaggio di testo nella console con testo nero e sfondo verde.$this->ioStyle->error()produce un messaggio di ERRORE con testo bianco su sfondo marrone.
file di lingua
Ci sono due file di lingua utilizzati durante l'installazione e la configurazione del plugin. Il file en-GB/plg_console_onoffbydate.sys.ini contiene le stringhe visualizzate durante l'installazione e la gestione:
PLG_CONSOLE_ONOFFBYDATE="Console - Onoffbydate"
PLG_CONSOLE_ONOFFBYDATE_DESCRIPTION="Set date-dependent enabled state of a module via cli"
Il file en-GB/plg_console_onoffbydate.ini contiene le stringhe in uso:
PLG_CONSOLE_ONOFFBYDATE="Console - Onoffbydate"
PLG_CONSOLE_ONOFFBYDATE_DESCRIPTION="Set date-dependent enabled state of a module via cli"
PLG_CONSOLE_ONOFFBYDATE_ERROR="Unknown season: %s."
PLG_CONSOLE_ONOFFBYDATE_HELP_1="<info>%command.name%</info> Toggles module Enabled/Disabled state\n"
PLG_CONSOLE_ONOFFBYDATE_HELP_2="Usage: <info>php %command.full_name% season action module_id where\n"
PLG_CONSOLE_ONOFFBYDATE_HELP_3=" season is one of winter or weekend,\n"
PLG_CONSOLE_ONOFFBYDATE_HELP_4=" action is one of publish or unpublish and\n"
PLG_CONSOLE_ONOFFBYDATE_HELP_5=" module_id is the id of the module to publish or unpublish.</info>\n"
PLG_CONSOLE_ONOFFBYDATE_SUCCESS="That seemed to work. %s Module %s has been %s."
PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_IN_A_WEEKEND="Today is in a weekend."
PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_IN_WINTER="Today is in winter."
PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_NOT_IN_A_WEEKEND="Today is not in a weekend."
PLG_CONSOLE_ONOFFBYDATE_TODAY_IS_NOT_IN_WINTER="Today is not in winter."
Nota che l'output del comando è visibile solo a te sulla tua installazione di Joomla.
Il Modulo
Questo codice modifica semplicemente lo stato pubblicato di un modulo a seconda di una funzione della data. Quindi hai bisogno dell'id del modulo come illustrato nella colonna di destra della pagina dell'elenco Moduli (Sito).
La Riga di Comando
Installa il plugin nella tua installazione di test Joomla e abilitato. Se causa un arresto del sito, utilizza phpMyAdmin per trovare il plugin appena installato nella tabella #__extensions e imposta il suo parametro enabled su 0. Risolvi il problema e riprova.
In una finestra del terminale, cambia la directory alla radice del tuo sito ed usa il seguente comando:
php cli/joomla.php list
Se qualcosa va storto in questa fase, verifica che la versione PHP invocata sia quella della riga di comando e non quella utilizzata dal server web. Puoi sopprimere i messaggi di deprecazione con questa versione della riga di comando.
php -d error_reporting="E_ALL & ~E_DEPRECATED" cli/joomla.php onoffbydate:action garbage publish 133
Se il codice funziona vedrai onoffbydate tra l'elenco dei comandi disponibili e puoi invocare l'aiuto per vedere come deve essere utilizzato:
php cli/joomla.php onoffbydate:action --help
Usage:
onoffbydate:action <season> <action> <module_id>
Arguments:
season winter or summer or weekday or weekend
action publish or unpublish
module_id module id
Options:
-h, --help Display the help information
-q, --quiet Flag indicating that all output should be silenced
-V, --version Displays the application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Flag to disable interacting with the user
--live-site[=LIVE-SITE] The URL to your site, e.g. https://www.example.com
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Help:
onoffbydate:action Toggles module Enabled/Disabled state
Usage: php joomla.php onoffbydate:action season action module_id where
season is one of winter or summer or weekday or weekend,
action is one of publish or unpublish and
module_id is the id of the module to publish or unpublish.
E poi provatelo:
php cli/joomla.php onoffbydate:action weekend publish 133
[OK] That seemed to work. Today is not a weekend. Module 133 has been Unpublished
In uso
Questa logica di utilizzo è un po' illogica! Se hai un modulo che dovrebbe essere pubblicato solo nei fine settimana, i parametri da usare ogni giorno sono weekend publish module_id. Questo pubblica il modulo nei giorni del fine settimana e lo depubblica nei giorni non del fine settimana. Per un modulo che deve essere pubblicato nei giorni feriali, i parametri da usare ogni giorno sono weekend unpublish module_id. Questo depubblica il modulo nei giorni del fine settimana e lo pubblica nei giorni non del fine settimana. La stessa logica si applica alla versione winter.
Il cron
Il comando può essere testato in una finestra del terminale, ma probabilmente vorrai usarlo da un cron. L'opzione winter potrebbe essere eseguita il primo giorno di ogni mese. L'opzione weekend verrebbe eseguita quotidianamente. Il punto importante è che puoi avere quanti cron vuoi per cambiare lo stato pubblicato di quanti moduli desideri a qualsiasi intervallo appropriato. Lo stesso codice funziona per tutti.
Su un servizio di hosting, è necessario fornire i percorsi completi all'eseguibile php e al comando cli di joomla. Esempio:
/usr/local/bin/php /home/username/public_html/pathtojoomla/cli/joomla.php onoffbydate:action winter publish 130
A seconda di come hai configurato il tuo cron e il tuo sistema, potresti ricevere un'email rassicurante contenente esattamente le stesse informazioni che vedi nella riga di comando.
Verifica
E naturalmente: vai alla tua pagina principale e verifica che il modulo sia stato effettivamente pubblicato o non pubblicato.
Tradotto da openai.com