Complementos de CCM19-
CCM19 puede ampliarse con diversas funcionalidades mediante complementos.
Denominación
Cada complemento tiene un nombre único, compuesto por un prefijo del fabricante-y el nombre del complemento propiamente dicho-. El nombre del complemento-debe estar formado por palabras en inglés.
Los prefijos de fabricante-se asignarán de forma centralizada en el futuro. Si desarrolla plugins por su cuenta, le rogamos que, por el momento, elija un nombre lo más único posible para evitar colisiones de nombres con el prefijo del fabricante.
Para el directorio del plugin-, el nombre se compone del prefijo del fabricante-escrito en CamelCase-.
Como ID del plugin-en el campo name-del archivo composer.json, el nombre se escribe como fabricante/nombre_del_plugin en snake_case.
Los complementos desarrollados por el equipo de desarrollo de CCM19-llevan el prefijo Ccm19.
Por lo tanto, un complemento de CCM19 para «compatibilidad ampliada con iframes-» se llamaría ccm19/extended_iframe o Ccm19ExtendedIframe, mientras que un plugin del fabricante «Example Vendor» se llamaría, por ejemplo, examplevendor/really_special_plugin o ExamplevendorReallySpecialPlugin.
Estructura básica
Un plugin se crea como una carpeta en el directorio plugins y contiene los siguientes archivos y carpetas:
composer.json: metadatos sobre el pluginpreview.{png|jpeg|svg|webp|gif} – Imagen de vista previasrc/– Archivos fuente PHP-(idealmente en una subestructura como la del CCM19-src-)src/Controller/– Archivos PHP-- de controladores (opcional)config/– Archivos de configuración de Symfony-- (opcional)templates/– Plantillas de Twig-(opcional)translations/– Archivos de traducción (opcional)
Composer.json
El archivo composer.json tiene el siguiente aspecto:
{
"name": "examplevendor/really_special_plugin",
"description": "Este plugin hace algo realmente especial que debería describirse aquí.",
"version": "1.0",
"type": "-plugin ccm19",
"license": "proprietary",
"authors": [
{
"name": "ExampleVendor GmbH",
"role": "Fabricante"
}
],
"extra": {
"copyright": "(c) Copyright...",
"activatePerDomain": true, // El plugin se puede activar en la versión de Agency-para dominios individuales (de-)
"label": {
"en": "Really special plugin", // Nombre que se muestra en la gestión de plugins
"de": "Wirklich besonderes Plugin",
"fr": "Plugin vraiment spécial"
},
"description": {
"de": "Este plugin hace algo muy especial que debería describirse aquí.",
"fr": "Ce plugin fait quelque chose de vraiment spécial qui devrait être décrit ici."
},
"manufacturerLink": {
"de": "https://examplevendor.example",
"en": "https://examplevendor.example"
},
"supportLink": {
"de": "https://examplevendor.example/support_de/",
"en": "https://examplevendor.example/support_en/"
},
"preview": "ruta/a/preview.png" // OPCIONAL: si la imagen de vista previa no se encuentra en la ubicación estándar-mencionada anteriormente
}
}
Si faltan datos traducibles en un idioma, se utilizará la versión en inglés como alternativa.
Los plugins también pueden incluir sus propias dependencias mediante Composer. Para ello, deben incluirse los archivos composer.lock y el directorio vendor/-en el directorio principal del plugin. Las dependencias del plugin se cargarán automáticamente al activarlo.
Plantillas
Las plantillas del directorio templates/-del plugin se incorporan mediante los espacios de nombres de Twig-siguiendo el esquema @plugin:DirectorioDelPlugin/.
En el ejemplo anterior del plugin-, templates/index.html.twig sería, por tanto, @plugin:ExamplevendorReallySpecialPlugin/index.html.twig.
Modificar las plantillas del sistema principal
Para añadir o editar plantillas de otras entradas del menú distintas de las propias, se pueden utilizar los eventos App\Event\TemplateResolveEvent y App\Event\TemplateRenderEvent.
Con App\Event\TemplateResolveEvent se puede ampliar o sustituir cualquier archivo de plantilla-(incluso aquellos que, por ejemplo, se incluyen con {% include(…) %}) se puede ampliar o sustituir por una plantilla propia. Para ello, se suele utilizar $event->extendTemplate('@plugin:ExamplevendorReallySpecialPlugin/….twig'), con el fin de sustituir o completar partes de la salida con bloques de Twig-.
Con App\Event\TemplateRenderEvent, las variables de la plantilla-pueden leerse o establecerse mediante $event->get() y $event->set(), y también pueden ampliarse o sustituirse las plantillas de las opciones de menú-(plantillas de sub-no incluidas).
class BackendTemplateListener implements EventSubscriberInterface
{
private $pluginState;
public function __construct(PluginState $pluginState)
{
$this->pluginState = $pluginState;
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
return [
TemplateRenderEvent::nameForView('domain/index.html.twig') => ['onRenderDomainIndex', 100],
];
}
/**
* @return void
*/
public function onRenderDomainIndex(TemplateRenderEvent $event)
{
if (!$this->pluginState->isActiveForCurrentDomain()) {
return;
}
$event->extendTemplate('@plugin:ExamplevendorReallySpecialPlugin/domain_index.html.twig');
$event->set('someVariable', ...);
}
}
Rutas y elementos de menú
Las rutas y los elementos de menú se pueden crear mediante las anotaciones @Route y @Menu-en los controladores de src/Controller/. Las dependencias de las anotaciones ya las importa el sistema principal. Por lo tanto, en el plugin solo tiene que importar las anotaciones con use Symfony\Component\Routing\Annotation\Route; y use App\Component\Menu\Annotation\Menu;.
Las rutas-Nombre deben empezar por plugin_nombre_del_fabricante_nombre_del_plugin_, para que se aplique la gestión de permisos y las rutas del plugin-se activen o desactiven en función de si un plugin está activado para un usuario. El nombre del plugin-y el prefijo del fabricante deben escribirse con el mismo formato «snake_case»-que en el archivo composer.json (solo que con guion bajo en lugar de barra).
Anotación @Menu-
La anotación Menu-crea una entrada de menú para una ruta.
Puede tener los siguientes parámetros:
- primer parámetro o
name: cadena El nombre que se utilizará para mostrar la entrada del menú. Se traduce mediante el sistema de traducción habitual. group: cadena Grupo del menú en el que se mostrará la opción de menú (opcional; por defecto, en «Plugins»)icon: cadena Nombre del icono-. Actualmente se admitenglyphicon-…yfa-….order: entero ID de orden-con el que se determina la posición en el menúroute: cadena Ruta a la que debe conducir la opción del menú (por defecto: se lee de la anotación@Route-del método)route_group: cadena Prefijo de todas las rutas que pertenecen a la opción de menú (por defecto: se lee de la anotación@Route-de la clase, si existe; de lo contrario, es igual aroute). Se utiliza para indicar si la opción de menú sigue activa.access={…}: cadena[] Quién debe tener acceso a la opción de menú. Los valores posibles son"admin","user"y"subuser". (Por defecto: se determina a partir de la superclase de la clase-del controlador).navigation: cadena En qué navegación debe mostrarse la opción de menú. Los valores posibles sonmain,domainymeta. En este caso,maincorresponde a la navegación que se muestra al usuario al iniciar sesión. Es decir,domainometa, según la edición y el tipo de usuario. (Por defecto:domainenDomainDependantControllers; en los demás casos,main).editions={…}: cadena[] Ediciones en las que debe mostrarse la opción de menú. (Por defecto:{"extended"}paraHostingController; en los demás casos, todas las ediciones).envs={…}: string[] Entornos en los que debe mostrarse la opción de menú. Por ejemplo,{"dev"}para que solo se muestre en modo de desarrollo. (Por defecto: todos)
Configuración predeterminada de los derechos de acceso y del área de navegación
Si no se especifica explícitamente con access y navigation, depende de la clase de la que derive el controlador quién puede ver la opción de menú y dónde.
DomainDependantController: En la navegación del dominio-. Solo visible para los usuarios con acceso al dominio (no para los administradores de la edición Agency-).AdminController: En la navegación principal. Solo visible para los administradores de la edición Agency-y para los usuarios de la edición Basic-.HostingController: En la navegación principal. Solo visible para los administradores de la edición Agency-.AbstractController: En la navegación principal. Solo visible para los usuarios que pueden tener acceso al dominio (excepto los administradores de la edición Agency-).UniversalController: En la navegación principal. Visible para todos.UnrestrictedController: En la navegación principal. Visible para todos.
Lo que se entiende por «navegación principal» varía según la edición y el tipo de usuario. Siempre se trata de la navegación que aparece tras iniciar sesión. Es decir, la navegación del dominio-para los usuarios de las ediciones Basic-y-, y la navegación Meta/User-para los clientes de Agency-. En el caso de los administradores de Agency-, vuelve a ser la navegación «Domain»-(aunque no exista ningún dominio).
Ejemplo:
namespace Plugins\ExamplevendorReallySpecialPlugin;
use App\Component\Menu\Annotation\Menu;
use App\Controller\DomainDependantController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/domains/{_domainId}/plugins/examplevendor/really_special/", name="plugin_examplevendor_really_special_plugin_")
*/
class MainController extends DomainDependantController
{
/**
* @Menu("Really special", icon="fa-plug")
* @Route("", name="index", methods={"HEAD", "GET"})
*/
public function index(...): Response
{
...
}
/**
* @Route("", name="save")
*/
public function indexSave(...): Response
{
...
}
}
Se puede utilizar el autowiring de Symfony-. Dentro de los plugins, además, se admiten dos argumentos especiales de autowiring-:
App\Model\PluginStateproporciona el-estado-del plugin para el plugin actual, con el que, por ejemplo, se puede comprobar si el plugin debe estar activo.string $PLUGINPATHproporciona la ruta completa al directorio del plugin-.
Las opciones del menú del dominio-(es decir, aquellas cuyo controlador deriva de DomainDependantController) se muestran o se ocultan automáticamente en- cuando se activa el plugin para el dominio (de-).
Las entradas del menú que no dependen del dominio se ocultan automáticamente en la versión de Agency-cuando se desactiva el plugin para un cliente.
Para requisitos más específicos, también es posible establecer manualmente entradas del menú mediante un listener de eventos-para el evento App\Event\MenuGenerationEvent.
Responder a rutas de otros controladores
Con el evento ccm19.controller.request.<route_name>, por ejemplo, ccm19.controller.request.app_dashboard, se puede responder a una ruta antes de que se ejecute el controlador correspondiente.
El objeto del evento - es un Symfony\Component\HttpKernel\Event\ControllerEvent. Con el método setController(), la solicitud también se puede redirigir a otro controlador.
Por su parte, el evento ccm19.controller.response.<route_name> permite ejecutar código una vez que el controlador ha finalizado su ejecución.
Ninguno de estos eventos se activa en caso de acceso no autorizado; por ejemplo, cuando se accede a una ruta que requiere un usuario registrado sin una sesión válida.
Importante: En todos los controladores de eventos -, se debe comprobar siempre mediante $pluginState->isActiveForCurrentDomain() o, según el contexto, $pluginState->isAllowedForCurrentUser(), si se desea que el plugin realice una acción en el contexto actual.