From d1ce494dd06d2be128f5503e2eb89e5e4a129659 Mon Sep 17 00:00:00 2001 From: Lina Wolf <48202465+linawolf@users.noreply.github.com> Date: Sun, 27 Oct 2024 04:30:34 +0100 Subject: [PATCH] [TASK] Deprecate plugin content element and plugin subtypes (list_type) (#4821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [TASK] Deprecate plugin content element and plugin subtypes (list_type) Depends on https://github.com/TYPO3-Documentation/TYPO3CMS-Reference-TCA/pull/1183 Resolves: https://github.com/TYPO3-Documentation/Changelog-To-Doc/issues/1071 Releases: main, 13.4 --------- Co-authored-by: Stefan Frömken <123929835+sfroemkenjw@users.noreply.github.com> --- .../ContentElements/ContentElementsWizard.rst | 9 +- .../ContentElements/CustomBackendPreview.rst | 21 +-- .../ApiOverview/ContentElements/Index.rst | 41 +---- .../ContentElements/MigrationListType.rst | 160 ++++++++++++++++++ .../PluginListTypeToCTypeUpdate.php | 31 ++++ .../_Migration/_ext_localconf.php.diff | 12 ++ .../_Migration/_non_extbase_tca.diff | 34 ++++ .../_Migration/_tca_registration.php.diff | 36 ++++ .../_Migration/_typoscript.diff | 20 +++ .../_RepositoryWithJunctions.php | 18 +- Documentation/ApiOverview/FlexForms/Index.rst | 1 - .../Config/ExtensionDevelopment/Extbase.php | 9 + .../NewContentElementWizard.rst.txt | 23 --- ...ExtbasePluginListTypeToCTypeUpdate.rst.txt | 10 ++ .../PluginHaikuListRegistration.rst.txt | 26 ++- .../BestPractises/NamingConventions.rst | 133 ++++++++------- .../Extbase/Reference/FrontendPlugins.rst | 39 ++--- .../_FrontendPlugin/_ext_localconf.php | 1 + .../_FrontendPlugin/_ext_localconf_rss.php | 2 + .../_FrontendPlugin/_setup.typoscript | 2 +- .../Reference/_FrontendPlugin/_tt_content.php | 4 +- .../HowTo/FrontendPlugin/Index.rst | 10 +- .../ManualScreenshots/Extbase/ListType.png | Bin 10321 -> 0 bytes 23 files changed, 463 insertions(+), 179 deletions(-) create mode 100644 Documentation/ApiOverview/ContentElements/MigrationListType.rst create mode 100644 Documentation/ApiOverview/ContentElements/_Migration/PluginListTypeToCTypeUpdate.php create mode 100644 Documentation/ApiOverview/ContentElements/_Migration/_ext_localconf.php.diff create mode 100644 Documentation/ApiOverview/ContentElements/_Migration/_non_extbase_tca.diff create mode 100644 Documentation/ApiOverview/ContentElements/_Migration/_tca_registration.php.diff create mode 100644 Documentation/ApiOverview/ContentElements/_Migration/_typoscript.diff delete mode 100644 Documentation/CodeSnippets/Extbase/FrontendPlugins/NewContentElementWizard.rst.txt create mode 100644 Documentation/CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt delete mode 100644 Documentation/Images/ManualScreenshots/Extbase/ListType.png diff --git a/Documentation/ApiOverview/ContentElements/ContentElementsWizard.rst b/Documentation/ApiOverview/ContentElements/ContentElementsWizard.rst index 650b628f62..2550c96394 100644 --- a/Documentation/ApiOverview/ContentElements/ContentElementsWizard.rst +++ b/Documentation/ApiOverview/ContentElements/ContentElementsWizard.rst @@ -64,7 +64,7 @@ the available groups. Plain content elements or plugins ================================= -You can add a content element or plain plugin (non-extbase) using method +You can add a content element or plain plugin (no Extbase) using method `ExtensionManagementUtility::addPlugin() `__: of class :php:`\TYPO3\CMS\Core\Utility\ExtensionManagementUtility`. @@ -74,11 +74,12 @@ of class :php:`\TYPO3\CMS\Core\Utility\ExtensionManagementUtility`. The key `value` in the parameter `$itemArray` is used as key of the newly added content element representing the plugin. -When you are using `CType` (recommended) for parameter `$type` the content +When you are using `CType` for parameter `$type` the content element is added to the select item list of column `CType` in table `tt_content`. -If you are using the default `list_type` for the parameter it is added as -subtype. +.. deprecated:: 13.4 + Using the default `list_type` for the parameter is deprecated. All content + elements and plugins should be added with string `CType` for parameter `$type`. This method supplies some default values: diff --git a/Documentation/ApiOverview/ContentElements/CustomBackendPreview.rst b/Documentation/ApiOverview/ContentElements/CustomBackendPreview.rst index 0904eb272b..5e5c8f0a32 100644 --- a/Documentation/ApiOverview/ContentElements/CustomBackendPreview.rst +++ b/Documentation/ApiOverview/ContentElements/CustomBackendPreview.rst @@ -118,23 +118,12 @@ approaches: This specifies the preview renderer only for records of type :php:`$type` as determined by the :ref:`type field ` of your table. -#. Table and field have a :php:`subtype_value_field` TCA setting +.. deprecated:: 13.4 + Registration of subtypes has been deprecated. Registration of custom + types should therefore always be done by using + :confval:`record types `. - If your table and field have a - :ref:`subtype_value_field ` TCA - setting (like :php:`tt_content.list_type` for example) and you want to - register a preview renderer that applies only when that value is selected - (assume, when a certain plugin type is selected and you can't match it with - the "type" of the record alone): - - .. code-block:: php - - $GLOBALS['TCA'][$table]['types'][$type]['previewRenderer'][$subType] - = MyVendor\MyExtension\Preview\MyPreviewRenderer::class; - - Where :php:`$type` is, for example, :php:`list` (indicating a plugin) and - :php:`$subType` is the value of the :php:`list_type` field when the type of - plugin you want to target is selected as plugin type. + See also :ref:`t3tca:migration-subtype-previewrenderer`. .. note:: The :ref:`recommended location ` is in the diff --git a/Documentation/ApiOverview/ContentElements/Index.rst b/Documentation/ApiOverview/ContentElements/Index.rst index a14888be06..1ebcde4321 100644 --- a/Documentation/ApiOverview/ContentElements/Index.rst +++ b/Documentation/ApiOverview/ContentElements/Index.rst @@ -19,6 +19,7 @@ created, how existing content elements or plugins can be customized etc. CustomBackendPreview ContentElementsWizard BestPractices + MigrationListType .. _cePluginsIntroduction: @@ -143,8 +144,9 @@ An Extbase plugin is configured for the frontend with .. literalinclude:: _Plugins/_ext_localconf_extbase_plugin.php :caption: EXT:my_extension/ext_localconf.php -By using `ExtensionUtility::PLUGIN_TYPE_PLUGIN` as fifth parameter is is also -possible to add the plugin as a list type. See :ref:`plugins-list_type`. +.. deprecated:: 13.4 + Setting the fifth parameter to any value but `ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT` + is deprecated. See :ref:`plugins-list_type-migration`. Method :php:`ExtensionUtility::configurePlugin()` also takes care of registering the plugin for frontend output in TypoScript using an object of type @@ -183,12 +185,9 @@ To register such a plugin as content element you can use function .. literalinclude:: _Plugins/_tt_content_plugin.php :caption: EXT:my_extension/Configuration/TCA/Overrides/tt_content.php -By using `'list_type'` as second parameter is is also possible to add the plugin -as a list type. See :ref:`plugins-list_type`. - -**Plugins** are a specific type of content elements. Plugins use the CType='list'. -Each plugin has its own plugin type, which is used in the database field -tt_content.list_type. The list_type could be understood as subtype of CType. +.. deprecated:: 13.4 + Setting the second parameter to `list_type` + is deprecated. See :ref:`plugins-list_type-migration`. .. _plugins-characteristics: @@ -215,32 +214,6 @@ has a plugin that allow frontend users, stored in table `fe_users` to log into the website. :composer:`typo3/cms-indexed-search` has a plugin that can be used to search in the index and display search results. -.. _plugins-list_type: - -CType vs list_type plugins -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Historically it was common to add plugins as a list type to the content element -types. In this case the column `CType` is set to `'list'` for all plugins while -the field `list_type` contains the key of the actual plugin. - -As different plugins need different fields in the backend form this let to -the creation of all type of complicated TCA constructs to influence the -behaviour of backend forms for plugins. - -The existence of the `list_type` also made a separate layer of content element -definitions in the TypoScript necessary. - -Therefore the `list_type` complicates registration and configuration of plugins -while it poses no advantages. Therefore it is recommended to always use the -CType for new plugin types while the `list_type` is retained for now for -backward compatibility. - -If you are refactoring the plugins of your extension, for example while getting -rid of switchable controller actions it is recommended to migrate your plugins -to use the CType. You should then supply a -:ref:`upgrade wizard ` -for easy migration for your users. .. _plugins-editing: diff --git a/Documentation/ApiOverview/ContentElements/MigrationListType.rst b/Documentation/ApiOverview/ContentElements/MigrationListType.rst new file mode 100644 index 0000000000..166fbc85f9 --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/MigrationListType.rst @@ -0,0 +1,160 @@ +.. include:: /Includes.rst.txt +.. _plugins-list-type: +.. _plugins-list-type-migration: + +========================================= +Migration: `list_type` plugins to `CType` +========================================= + +.. deprecated:: 13.4 + The plugin content element (:php:`list`) and the plugin sub types + field (:php:`list_type`) have been marked as deprecated in TYPO3 v13.4 and + will be removed in TYPO3 v14.0. + +Several steps are important in the migration from `list_type` plugins to `CType` +plugins: + +* Register plugins using the `CType` record type +* Create update wizard which extends :php:`\TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate` + and add :php:`list_type` to :php:`CType` mapping for each plugin to migrate. +* Migrate possible FlexForm registration and add dedicated :php:`showitem` TCA + configuration +* Migrate possible `PreviewRenderer` registration in TCA +* Adapt possible content element wizard items in page TSconfig, where + :php:`list_type` is used +* Adapt possible content element restrictions in backend layouts or container + elements defined by third-party extensions like :t3ext:`content_defender` + +.. contents:: Table of content + +.. _plugins-list-type-migration-extbase: + +Migration example: Extbase plugin +================================= + +.. _plugins-list-type-migration-extbase-configuration: + +1. Adjust the Extbase plugin configuration +------------------------------------------ + +Extbase plugins are usually registered using the utility method +:php:`\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin()` in file +:file:`EXT:my_extension/ext_localconf.php`. + +Add value `ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT` as fifth parameter, +`$pluginType`, to the method :php:`ExtensionUtility::configurePlugin()`: + +.. literalinclude:: _Migration/_ext_localconf.php.diff + :caption: EXT:examples/ext_localconf.php (difference) + +If the fourth parameter, `$nonCacheableControllerActions` was missing you can +set it to an empty array, the default value. + +It is theoretically possible that the extension author did not use this utility +method. In that case you have to change the TypoScript used to display your +plugin manually. This step is similar to adjusting the TypoScript of a +Core-based plugin. + +.. _plugins-list-type-migration-extbase-flexform: + +2. Adjust the registration of FlexForms and additional fields +------------------------------------------------------------- + +.. literalinclude:: _Migration/_tca_registration.php.diff + :caption: EXT:examples/Configuration/TCA/Overrides/tt_content.php (difference) + :linenos: + +The `CType` based plugin does not inherit the default fields provided by the +TCA of the content element "List". These where in many cases removed by +using :confval:`subtypes_excludelist `. + +As these fields are not displayed automatically anymore you can remove this +definition without replacement: Line 15 in the diff. If they have not been +removed and are still needed, you will need to manually add them to your plugin type. + +The :confval:`subtypes_addlist ` was used to +display the field containing the FlexForm, an possibly other fields in the +`list_type` plugin. We remove this definition (Line 17) and replace it +by using the utility method +:php:`\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes()` +(Line 25-30). + +The utility method :php:`ExtensionManagementUtility::addPiFlexFormValue()` +needs to be changed from using the first parameter for the `$pluginSignature` +to using the third parameter. The first parameter requires a certain `list_type` +setting it to the wildcard `*` allows all list types. The third parameter limits +it to the `CType`. + +.. _plugins-list-type-migration-extbase-upgrade-wizard: + +3. Provide an upgrade wizard +---------------------------- + +.. versionadded:: 13.4 + If your extension also should support TYPO3 version 12.4 or even 11.5 see + Example: :ref:`plugins-list-type-migration-core-plugin-migration`. + +You can extend class :php-short:`TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate` +to provide a custom upgrade wizard that moves existing plugins from the +`list_type` definition to the `CType` definition. The resulting upgrade wizard +will even adjust backend user permissions for the defined plugins: + +.. include:: /CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt + +.. _plugins-list-type-migration-extbase-replace: + +4. Search your code and replace any mentioning of `list_type` +------------------------------------------------------------- + +Search your code. If you used the `list_type` of you plugin in any custom +database statement or referred to the according + +Search your TCA definitions for any use of the now outdated configuration +options + +.. _plugins-list-type-migration-core: + +Migration example: Core-based plugin +==================================== + +.. _plugins-list-type-migration-core-plugin-registration: + +1. Adjust the plugin registration +---------------------------------- + +.. literalinclude:: _Migration/_non_extbase_tca.diff + :caption: EXT:my_extension/Configuration/TCA/tt_content.php (diff) + +.. _plugins-list-type-migration-core-plugin-typoscript: + +2. Adjust the TypoScript of the plugin +--------------------------------------- + +If your plugin was rendered using :composer:`typo3/cms-fluid-styled-content` you are +probably using the top level TypoScript object +:ref:`tt_content ` to render the plugin. The path to +the plugin rendering needs to be adjusted as you cannot use the deprecated content +element "list" anymore: + +.. literalinclude:: _Migration/_typoscript.diff + :caption: EXT:my_extension/Configuration/Sets/MyPluginSet/setup.typoscript (diff) + +.. _plugins-list-type-migration-core-plugin-migration: + +3. Provide an upgrade wizard for automatic content migration for TYPO3 v13.4 and v12.4 +--------------------------------------------------------------------------------------- + +If you extension only support TYPO3 v13 and above you can extend the Core class +:php:`\TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate`. + +If your extension also supports TYPO3 v12 and maybe even TYPO3 v11 you can use +class :php:`Linawolf\ListTypeMigration\Upgrades\AbstractListTypeToCTypeUpdate` +instead. Require via composer: :composer:`linawolf/list-type-migration` or +copy the file into your extension using your own namespaces: + +.. literalinclude:: _Migration/PluginListTypeToCTypeUpdate.php + :caption: EXT:my_extension/Classes/Upgrades/PluginListTypeToCTypeUpdate.php + +If you also have to be compatible with TYPO3 v11, register the upgrade wizard +manually: +:ref:`Registering wizards for TYPO3 v11 `. diff --git a/Documentation/ApiOverview/ContentElements/_Migration/PluginListTypeToCTypeUpdate.php b/Documentation/ApiOverview/ContentElements/_Migration/PluginListTypeToCTypeUpdate.php new file mode 100644 index 0000000000..4f25bf39e7 --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/_Migration/PluginListTypeToCTypeUpdate.php @@ -0,0 +1,31 @@ + 'my_extension_pi1', + 'my_extension_pi2' => 'my_extension_newpluginname', + ]; + } + + public function getTitle(): string + { + return 'Migrates my_extension plugins'; + } + + public function getDescription(): string + { + return 'Migrates my_extension_pi1, my_extension_pi2 from list_type to CType. '; + } +} diff --git a/Documentation/ApiOverview/ContentElements/_Migration/_ext_localconf.php.diff b/Documentation/ApiOverview/ContentElements/_Migration/_ext_localconf.php.diff new file mode 100644 index 0000000000..e235e69ec2 --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/_Migration/_ext_localconf.php.diff @@ -0,0 +1,12 @@ + use T3docs\Examples\Controller\FalExampleController; + use TYPO3\CMS\Extbase\Utility\ExtensionUtility; + + ExtensionUtility::configurePlugin( + 'Examples', + 'HtmlParser', + [ + HtmlParserController::class => 'index', + ], ++ [], ++ ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT, + ); diff --git a/Documentation/ApiOverview/ContentElements/_Migration/_non_extbase_tca.diff b/Documentation/ApiOverview/ContentElements/_Migration/_non_extbase_tca.diff new file mode 100644 index 0000000000..90d8c6d16a --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/_Migration/_non_extbase_tca.diff @@ -0,0 +1,34 @@ + $pluginSignature = 'examples_pi1'; + $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang_db.xlf:tt_content.examples_pi1.title'; + $extensionKey = 'examples'; + $flexFormPath = 'FILE:EXT:examples/Configuration/Flexforms/flexform_ds1.xml'; + + // Add the plugins to the list of plugins + ExtensionManagementUtility::addPlugin( + [$pluginTitle, $pluginSignature, '', 'plugin'], +- 'list_type', ++ 'CType', + $extensionKey, + ); + +-// Disable the display of layout and select_key fields for the plugin +-$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] +- = 'layout,select_key,pages'; +- +-// Activate the display of the plug-in flexform field and set FlexForm definition +-$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] = 'pi_flexform'; + ++// Activate the display of the FlexForm field ++ExtensionManagementUtility::addToAllTCAtypes( ++ 'tt_content', ++ '--div--;Configuration,pi_flexform,', ++ $pluginSignature, ++ 'after:subheader', ++); + + ExtensionManagementUtility::addPiFlexFormValue( +- $pluginSignature, ++ '*', + $flexFormPath, ++ $pluginSignature, + ); diff --git a/Documentation/ApiOverview/ContentElements/_Migration/_tca_registration.php.diff b/Documentation/ApiOverview/ContentElements/_Migration/_tca_registration.php.diff new file mode 100644 index 0000000000..e95861a870 --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/_Migration/_tca_registration.php.diff @@ -0,0 +1,36 @@ + use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + use TYPO3\CMS\Extbase\Utility\ExtensionUtility; + + $extensionKey = 'Examples'; + $pluginName = 'HtmlParser'; + $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang.xlf:htmlparser_plugin_title'; + + // Register the HTML Parser plugin + $pluginSignature = ExtensionUtility::registerPlugin( + $extensionKey, + $pluginName, + $pluginTitle, + ); + +-$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] +- = 'layout,select_key,pages'; +-$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] +- = 'pi_flexform'; +- +-ExtensionManagementUtility::addPiFlexFormValue( +- $pluginSignature, +- 'FILE:EXT:examples/Configuration/Flexforms/HtmlParser.xml', +-); + ++ExtensionManagementUtility::addToAllTCAtypes( ++ 'tt_content', ++ '--div--;Configuration,pi_flexform,', ++ $pluginSignature, ++ 'after:subheader', ++); ++ ++ExtensionManagementUtility::addPiFlexFormValue( ++ '*', ++ 'FILE:EXT:examples/Configuration/Flexforms/HtmlParser.xml', ++ $pluginSignature, ++); diff --git a/Documentation/ApiOverview/ContentElements/_Migration/_typoscript.diff b/Documentation/ApiOverview/ContentElements/_Migration/_typoscript.diff new file mode 100644 index 0000000000..b682c39181 --- /dev/null +++ b/Documentation/ApiOverview/ContentElements/_Migration/_typoscript.diff @@ -0,0 +1,20 @@ +-tt_content.list.20.examples_pi1 = USER +-tt_content.list.20.examples_pi1 { ++tt_content.examples_pi1 = USER ++tt_content.examples_pi1 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + settings { + singlePid = 42 + listPid = 55 + } + view { + templateRootPath = {$templateRootPath} + partialRootPath = {$partialRootPath} + layoutRootPath = {$layoutRootPath} + } + } + + # Or if you used the plugin top level object: + +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1 < plugin.tx_examples_pi1 diff --git a/Documentation/ApiOverview/Database/ExpressionBuilder/_RepositoryWithJunctions.php b/Documentation/ApiOverview/Database/ExpressionBuilder/_RepositoryWithJunctions.php index 1165edbea6..c280bba58b 100644 --- a/Documentation/ApiOverview/Database/ExpressionBuilder/_RepositoryWithJunctions.php +++ b/Documentation/ApiOverview/Database/ExpressionBuilder/_RepositoryWithJunctions.php @@ -16,23 +16,23 @@ public function __construct(private readonly ConnectionPool $connectionPool) {} public function findSomething(): QueryBuilder { // WHERE - // (`tt_content`.`CType` = 'list') + // (`tt_content`.`CType` = 'header') // AND ( - // (`tt_content`.`list_type` = 'example_pi1') + // (`tt_content`.`header_position` = 'center') // OR - // (`tt_content`.`list_type` = 'example_pi2') + // (`tt_content`.`header_position` = 'right') // ) - $queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content'); + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME); $queryBuilder->where( - $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), + $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('header')), $queryBuilder->expr()->or( $queryBuilder->expr()->eq( - 'list_type', - $queryBuilder->createNamedParameter('example_pi1', Connection::PARAM_STR), + 'header_position', + $queryBuilder->createNamedParameter('center', Connection::PARAM_STR), ), $queryBuilder->expr()->eq( - 'list_type', - $queryBuilder->createNamedParameter('example_pi2', Connection::PARAM_STR), + 'header_position', + $queryBuilder->createNamedParameter('right', Connection::PARAM_STR), ), ), ); diff --git a/Documentation/ApiOverview/FlexForms/Index.rst b/Documentation/ApiOverview/FlexForms/Index.rst index e8757fe8ae..dbc358e9fb 100644 --- a/Documentation/ApiOverview/FlexForms/Index.rst +++ b/Documentation/ApiOverview/FlexForms/Index.rst @@ -123,7 +123,6 @@ Steps to perform (extension developer) :caption: EXT:my_extension/Configuration/TCA/Overrides/tt_content.php \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue( - // 'list_type' does not apply here '*', // FlexForm configuration schema file 'FILE:EXT:example/Configuration/FlexForms/Registration.xml', diff --git a/Documentation/CodeSnippets/Config/ExtensionDevelopment/Extbase.php b/Documentation/CodeSnippets/Config/ExtensionDevelopment/Extbase.php index 1c0768d81e..8cba6b13a9 100644 --- a/Documentation/CodeSnippets/Config/ExtensionDevelopment/Extbase.php +++ b/Documentation/CodeSnippets/Config/ExtensionDevelopment/Extbase.php @@ -1,5 +1,7 @@ 'createCodeSnippet', @@ -341,4 +343,11 @@ 'targetFileName' => 'CodeSnippets/Extbase/UriBuilder.rst.txt', 'withCode' => false, ], + [ + 'action' => 'createPhpClassCodeSnippet', + 'class' => ExtbasePluginListTypeToCTypeUpdate::class, + 'withComment' => true, + 'withClassComment' => false, + 'targetFileName' => 'CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt', + ], ]; diff --git a/Documentation/CodeSnippets/Extbase/FrontendPlugins/NewContentElementWizard.rst.txt b/Documentation/CodeSnippets/Extbase/FrontendPlugins/NewContentElementWizard.rst.txt deleted file mode 100644 index 5b82db56c6..0000000000 --- a/Documentation/CodeSnippets/Extbase/FrontendPlugins/NewContentElementWizard.rst.txt +++ /dev/null @@ -1,23 +0,0 @@ - -.. code-block:: typoscript - :caption: `EXT:blog_example/Configuration/page.tsconfig` - :linenos: - - mod.wizards.newContentElement.wizardItems { - // add the content elementS to the tab "plugins" - plugins { - elements { - // ... - blogexample_postsingle { - iconIdentifier = blog_example_icon - title = PostSingle - description = Display a single blog post - tt_content_defValues { - CType = list - list_type = = blogexample_postsingle - } - } - } - show := addToList(blogexample_postlist,blogexample_postsingle,blogexample_blogadmin) - } - } diff --git a/Documentation/CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt b/Documentation/CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt new file mode 100644 index 0000000000..ddfb1e793e --- /dev/null +++ b/Documentation/CodeSnippets/Extbase/Upgrades/ExtbasePluginListTypeToCTypeUpdate.rst.txt @@ -0,0 +1,10 @@ +.. Generated by https://github.com/TYPO3-Documentation/t3docs-codesnippets +.. Extracted from T3docs\Examples\Upgrades\ExtbasePluginListTypeToCTypeUpdate + +.. code-block:: php + :caption: Class T3docs\\Examples\\Upgrades\\ExtbasePluginListTypeToCTypeUpdate + + final class ExtbasePluginListTypeToCTypeUpdate extends AbstractListTypeToCTypeUpdate + { + + } diff --git a/Documentation/CodeSnippets/FlexForms/Examples/PluginHaikuListRegistration.rst.txt b/Documentation/CodeSnippets/FlexForms/Examples/PluginHaikuListRegistration.rst.txt index be955ff5d7..460a264747 100644 --- a/Documentation/CodeSnippets/FlexForms/Examples/PluginHaikuListRegistration.rst.txt +++ b/Documentation/CodeSnippets/FlexForms/Examples/PluginHaikuListRegistration.rst.txt @@ -11,6 +11,7 @@ * This file is part of the TYPO3 CMS project. [...] */ + use TYPO3\CMS\Core\Schema\Struct\SelectItem; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; /* @@ -19,21 +20,30 @@ defined('TYPO3') or die(); + $pluginSignature = 'examples_haiku_list'; + ExtensionManagementUtility::addPlugin( - [ + new SelectItem( + 'select', 'LLL:EXT:examples/Resources/Private/Language/PluginHaiku/locallang_db.xlf:list.title', - 'examples_haiku_list', + $pluginSignature, 'tx_examples-haiku', - ], - 'list_type', + 'plugins', + 'LLL:EXT:examples/Resources/Private/Language/PluginHaiku/locallang_db.xlf:list.description', + ), + 'CType', 'examples', ); - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['examples_haiku_list'] = 'pages,layout,select_key,recursive'; - - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['examples_haiku_list'] = 'pi_flexform'; + ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + '--div--;Configuration,pi_flexform,', + $pluginSignature, + 'after:header', + ); ExtensionManagementUtility::addPiFlexFormValue( - 'examples_haiku_list', + '*', 'FILE:EXT:examples/Configuration/Flexforms/PluginHaikuList.xml', + $pluginSignature, ); diff --git a/Documentation/ExtensionArchitecture/BestPractises/NamingConventions.rst b/Documentation/ExtensionArchitecture/BestPractises/NamingConventions.rst index 594cbd66d9..22bc9145c1 100644 --- a/Documentation/ExtensionArchitecture/BestPractises/NamingConventions.rst +++ b/Documentation/ExtensionArchitecture/BestPractises/NamingConventions.rst @@ -337,6 +337,11 @@ Examples (from TYPO3 Core extensions): Plugin signature ================ +.. deprecated:: 13.4 + Adding frontend plugins as a "General Plugin", setting the content + record :sql:`CType` to :sql:`'list'` and `list_type` to the plugin signature + is deprecated. See :ref:`plugins-list_type-migration`. + The plugin signature of non-Extbase plugins, registered via :php:`ExtensionManagementUtility::addPlugin()` is an arbitrarily defined string. By convention it should always be the extension name with all underscores removed @@ -365,45 +370,49 @@ Example: $extensionName = 'my_extension'; $pluginName = 'MyCoolPlugin'; - $pluginSignature == "myextension_mycoolplugin" + $pluginSignature = "myextension_mycoolplugin" The plugin signature is used in: -* the database field `tt_content.list_type` -* when defining a :ref:`FlexForm ` to be used for the plugin in - :php:`addPiFlexFormValue()` -* in TypoScript, :typoscript:`plugin.tx_myexample_myplugin` to define settings - for the plugin etc. -* As :ref:`record type ` in TCA. It can therefore be used to - define which fields should be visible in the TYPO3 backend. +* the database field `tt_content.CType` +* when defining a :ref:`FlexForm ` to be used for the plugin in + :php:`addPiFlexFormValue()` +* in TypoScript, :typoscript:`plugin.tx_myexample_myplugin` to define settings + for the plugin etc. +* As :ref:`record type ` in TCA. It can therefore be used to + define which fields should be visible in the TYPO3 backend. +.. _naming-conventions-plugin-signature-non-extbase: Example register and configure a non-Extbase plugin: ---------------------------------------------------- -.. code-block:: php - :caption: EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php +.. code-block:: php + :caption: EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php - use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; - $pluginSignature = 'examples_pi1'; - $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang_db.xlf:tt_content.list_type_pi1'; - $extensionKey = 'examples'; + $pluginSignature = 'examples_pi1'; + $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang_db.xlf:tt_content.list_type_pi1'; + $extensionKey = 'examples'; - // Add the plugins to the list of plugins - ExtensionManagementUtility::addPlugin ( - [ $pluginTitle, $pluginSignature,],'list_type', $extensionKey - ); + // Add the plugins to the list of plugins + ExtensionManagementUtility::addPlugin ( + [ $pluginTitle, $pluginSignature,],'CType', $extensionKey + ); - // Disable the display of layout and select_key fields for the plugin - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] - = 'layout,select_key,pages'; + ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + '--div--;Configuration,pi_flexform,', + $pluginSignature, + 'after:header', + ); - // Activate the display of the plug-in flexform field and set FlexForm definition - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['examples_pi1'] = 'pi_flexform'; - ExtensionManagementUtility::addPiFlexFormValue( - $pluginSignature, 'FILE:EXT:examples/Configuration/Flexforms/flexform_ds1.xml' - ); + ExtensionManagementUtility::addPiFlexFormValue( + '*', + 'FILE:EXT:example/Configuration/FlexForms/Registration.xml', + $pluginSignature, + ); .. code-block:: typoscript :caption: EXT:examples/Configuration/setup.typoscript @@ -412,6 +421,8 @@ Example register and configure a non-Extbase plugin: settings.pageId = 42 } +.. _naming-conventions-plugin-key: + Plugin key (Extbase only) ========================= @@ -438,49 +449,57 @@ The plugin key used in :php:`registerPlugin()` and :php:`configurePlugin()` Example register and configure an Extbase plugin: ------------------------------------------------- -.. code-block:: php - :caption: EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php +.. code-block:: php + :caption: EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php - use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; - use TYPO3\CMS\Extbase\Utility\ExtensionUtility; + use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + use TYPO3\CMS\Extbase\Utility\ExtensionUtility; - $extensionKey = 'Examples'; - $pluginName = 'HtmlParser'; - $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang.xlf:htmlparser_plugin_title'; + $extensionKey = 'Examples'; + $pluginName = 'HtmlParser'; + $pluginTitle = 'LLL:EXT:examples/Resources/Private/Language/locallang.xlf:htmlparser_plugin_title'; - $pluginSignature = ExtensionUtility::registerPlugin($extensionKey, $pluginName, - $pluginTitle); + $pluginSignature = ExtensionUtility::registerPlugin( + $extensionKey, + $pluginName, + $pluginTitle + ); - // $pluginSignature == "examples_htmlparser" + // $pluginSignature == "examples_htmlparser" - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] - = 'layout,select_key,pages'; - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] - = 'pi_flexform'; + ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + '--div--;Configuration,pi_flexform,', + $pluginSignature, + 'after:subheader', + ); - ExtensionManagementUtility::addPiFlexFormValue( - $pluginSignature, 'FILE:EXT:examples/Configuration/Flexforms/HtmlParser.xml' - ); + ExtensionManagementUtility::addPiFlexFormValue( + '*', + 'FILE:EXT:example/Configuration/FlexForms/Registration.xml', + $pluginSignature, + ); -.. code-block:: php - :caption: EXT:examples/ext_localconf.php +.. code-block:: php + :caption: EXT:examples/ext_localconf.php - use TYPO3\CMS\Extbase\Utility\ExtensionUtility; + use TYPO3\CMS\Extbase\Utility\ExtensionUtility; - ExtensionUtility::configurePlugin( - 'Examples', - 'HtmlParser', - [ - \T3docs\Examples\Controller\HtmlParserController::class => 'index', - ] - ); + ExtensionUtility::configurePlugin( + 'Examples', + 'HtmlParser', + [ + \T3docs\Examples\Controller\HtmlParserController::class => 'index', + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT, + ); -.. code-block:: typoscript - :caption: EXT:examples/Configuration/setup.typoscript +.. code-block:: typoscript + :caption: EXT:examples/Configuration/setup.typoscript - plugin.tx_examples_htmlparser { + plugin.tx_examples_htmlparser { settings.pageId = 42 - } + } Class name ========== diff --git a/Documentation/ExtensionArchitecture/Extbase/Reference/FrontendPlugins.rst b/Documentation/ExtensionArchitecture/Extbase/Reference/FrontendPlugins.rst index b6d320c29a..e7e7c57fa4 100644 --- a/Documentation/ExtensionArchitecture/Extbase/Reference/FrontendPlugins.rst +++ b/Documentation/ExtensionArchitecture/Extbase/Reference/FrontendPlugins.rst @@ -43,10 +43,16 @@ Use the following steps to add the plugin as content element: Use the following parameters: - #. Extension key :php:`'blog_example'` or name :php:`BlogExample`. - #. A unique identifier for your plugin in UpperCamelCase: :php:`'PostSingle'` - #. An array of allowed combinations of controllers and actions stored in an array - #. (Optional) an array of controller name and action names which should not be cached + #. Extension key :php:`'blog_example'` or name :php:`BlogExample`. + #. A unique identifier for your plugin in UpperCamelCase: :php:`'PostSingle'` + #. An array of allowed combinations of controllers and actions stored in an array + #. (Optional) an array of controller name and action names which should not be cached + #. Using any value but `ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT` is + deprecated in TYPO3 v13.4. + + .. deprecated:: 13.4 + Setting the fifth parameter to any value but `ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT` + is deprecated. See :ref:`plugins-list_type-migration`. :php:`TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin()` generates the necessary TypoScript to display the plugin in the frontend. @@ -66,16 +72,16 @@ Use the following steps to add the plugin as content element: the lists of allowed and non-cacheable actions have been added to the according global variables. -#. :php:`registerPlugin()`: Add to :sql:`list_type` :sql:`tt_content`. - - Make the plugin available in the field - :guilabel:`Plugin > Selected Plugin`, :sql:`list_type` of the table - :sql:`tt_content`. +#. :php:`registerPlugin()`: Add the plugin as option to the field "Type" of + the content element (column :sql:`CType` of table :sql:`tt_content`). - .. figure:: /Images/ManualScreenshots/Extbase/ListType.png - :class: with-shadow + This makes the plugin available in the field + :guilabel:`Type` of the content elements and automatically registers it for + the :ref:`New Content Element Wizard `. - The new plugin in the content record at :guilabel:`Plugin > Selected Plugin` + .. versionchanged:: 13.0 + In TYPO3 13 this is now automatically registered by the TCA from the step above. + See :ref:`changelog:feature-102834-1705256634` .. literalinclude:: _FrontendPlugin/_tt_content.php :language: php @@ -92,11 +98,6 @@ Use the following steps to add the plugin as content element: with :php:`LLL:`. #. (Optional) the :ref:`icon identifier ` or file path prepended with "EXT:" -#. Add to the :guilabel:`New Content Element` wizard (automatic) - - .. versionchanged:: 13.0 - In TYPO3 13 this is now automatically registered by the TCA from the step above. - See :ref:`changelog:feature-102834-1705256634` .. _extbase_frontend_plugin_typoscript: @@ -120,8 +121,8 @@ Frontend plugin as pure TypoScript #. Display the plugin via TypoScript - The TypoScript :ref:`USER ` object saved at - :typoscript:`tt_content.list.20.blogexample_postlistrss` can now be used + The TypoScript :ref:`EXTBASEPLUGIN ` object saved at + :typoscript:`tt_content.blogexample_postlistrss` can now be used to display the frontend plugin. In this example we create a special page type for the RSS feed and display the plugin via TypoScript there: diff --git a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf.php b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf.php index 3170282a63..de1b5b4f2c 100644 --- a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf.php +++ b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf.php @@ -17,4 +17,5 @@ [PostController::class => 'show', CommentController::class => 'create'], // non-cacheable actions [CommentController::class => 'create'], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT, ); diff --git a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf_rss.php b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf_rss.php index e7c497f74a..b2444dec29 100644 --- a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf_rss.php +++ b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_ext_localconf_rss.php @@ -12,4 +12,6 @@ 'BlogExample', 'PostListRss', [PostController::class => 'displayRssList'], + [], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT, ); diff --git a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_setup.typoscript b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_setup.typoscript index a414be6461..2f6ab9f0a4 100644 --- a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_setup.typoscript +++ b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_setup.typoscript @@ -2,7 +2,7 @@ tx_blogexample_rss = PAGE tx_blogexample_rss { typeNum = {$plugin.tx_blogexample.settings.rssPageType} - 10 < tt_content.list.20.blogexample_postlistrss + 10 < tt_content.blogexample_postlistrss config { disableAllHeaderCode = 1 diff --git a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_tt_content.php b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_tt_content.php index 3d4fc51915..e5316a1300 100644 --- a/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_tt_content.php +++ b/Documentation/ExtensionArchitecture/Extbase/Reference/_FrontendPlugin/_tt_content.php @@ -1,9 +1,11 @@ HJxwh)nE4Quy0D!A%&z|T4 z0O#M+?#C~kr=8Df=ZpWncYg04Chu z<6Ng}fh{fby4O=!JleR9_#yBtk0Oca*by$ zyEjEQ@qa-||3-qESl(g5=PZrizWY$s3X71nc=rtOj*sqQ4rAz-(n}x2BW7QJyA$1( zBOMVl7RJ(D5t__kQBA6GzV-6z3d6aF0_ykUGQketGQGyqzbD*}Tvjm1JS!|3HlOLU z-Xc&QE59^SX35E%4Ccfo!VXXv z`0cujcqzDK-?`Y@LF);gr{K^yRLl-*cds1_4n3y5Vi(i?hL8I*`A5GjsMBv_-=4DV zyWxg)4*c^ut< z8fo$j2qJt^GaA*m@h5cG3P0zUy3*BaT!F=p-O;~+lM+YBcVXA}1oPJ)-_y*Jot}!f za%P@wy~LYc=-_51{}g;uGiE_r{Luu_1}aHV66J{fGh!}4P3(KnX(c+y+huRt{t$0v zbJ*5c&^1qFJ(zSPPT-GE8Q;zZ^!SW9t6ncja${ob|BY5PF_wV~4L4<0y0n)nem;H`# z(W@g+e^Mi+WyHszQKrm0i zjrGTyySq3>7qm0RTYV-|()F{pHZeR3YL>amirY_3slis&2M5E13Uym34%+aw?SD;n zI;be7{aSw<+mMf3alX#IjV&26#lLF!>;f)`XT5Do6#c3^K%RRrhP6|>*OElCT|Um- zPbh*FXRHp84cDAa_(y;2xmeasKT`UZ{>qZtw2|}u1^eF4^_f!Po~nbj>r)l1j44$c zN;cr!E?$xh#_GZ5Y!s*VV9P-mbML(>tAcONLiwb%{;QO@ZCyC@%cqU;?6$1idw? zp=YslzLN*OdSOEUal3!s^zdUyz;*}s!K+rd`NqOnt8(6^QTiEWYCGBlmL(i}LrM+- z{ljaaaqCOQ&JQ?+O!wo{3wPk$j5E|J&Iv-Fv6=Dtp_DTSN5H6(VTQZs8+3phB?GJB z_Ujt2rc)f&1B>jt(uR?Y`Og1pKCBe~Rru^c7cI&l5iNhiz97&G*emSf|y zeJ>$!%B5MsVNBL?a_+Z4Z^7t)FT*)8y;lk(tk9ISXtU@|qbpF@>b(HA0^@Z4~wk?kn8x;f<3YH*C*W zY2X|{E&)_cbHVs&W61>EmY=)d?qdJw(|{ibF;+O2g8ODt^I$wJAY9jlwcz9+j5NG* z@<>xtlzbn9M^ss z`{E8VFz59&cujVX@arP0=pDf&%LGoOi*VW34*QAYm3%Ray<*a=S(WQVPq%BcETn2^ zl7)hlOBl`_QtX~ML6NH&l4Sy0Fq{p-t)R6T35tsElLY;s;dYh7@@93#kY;+xsU);mzkMs2FO?CLyDE#!ca-f2-+woNf z8&h9r5zS4S`1RBs#v`paDVkCPb{3K~*@aJ%fe>e@!QJbA@sL^gwjop8?6q$^vwa9`+W>+;_=M|gR*|iG49ux~9 zt+v8)t2~JsiFyuGTPds6>l$hGU0Pd{g?Jdh=+dmW#ZSxji661Os%pWB>81XeRSj+g zPR9&kxy@U2#hD_^ma3w$Dj8h!`g}Fm{c``dxTntl!h1r6$K%0f<~CCEd-XMW)0PkO zTKA{Mz3x;H=h>!En{+NlFAR0< z_fBwP?h|#O)Yc!Sig7f>Dt&rarE`*?zYYh zWYwIo0{sipfOz7uf8<*7Pw)8Be{3G9QMPO*lA?D~BH7y9ykq)S^s@2FaI{HRnVuF; zcABLV$^s_3`s=EUi<0KGz{Z~7r7sn!>lJmaJ8^?E(~e5H2iEn&`;i3lULMsrjp}CR zY3D+H6f~;_q)RH=cA79VVzbyWxQV0FXc*v?=D7lm(R~Qns})Z}C&f)7%_%M89VEL! zF3PQi5E!iP_duj(5m^*{LMfvs*%{tTBaMzmut$XL6k>FYXs*osb>syyVtkUUXq zsFjk3W#O(#nVFq!6oAIsGYeaT3UtH3_~rDnT98iyDNz38!t+6V^$b)>+C31~670D! zF|u2@xR*hAgCI^Jf*uSiZk>Cis#?d4WV0}yhTIVJn0s=I53!SNh%CB|D1PIt(g-Cl z7vN+6Xs7m9w2iU)f)EpX44?V@SZU!wH&QGH>6L32l)@61fh3MzYOK=?q+^+#|szwe-F z12z*i*mOtHT|o(siMxP=+)#}mkfzhr^hItOX@Z~#&*|>Mk_wIr5u$hhs%PhJz7Ai# zZaJ|O>GY37Wix-;BGek|8}yq=@$+oJS|(}3*?7NdvS|*#V%x;UCO^mh0AaJy4!Eyx z1q_*-HkQ#_9`QTUpu?7G>@PE_w{50mEJ1$T%$=~Pca#TlJUW4D9^@TS8J^yY$}C+f zV@eg98UKfKxMy6-sBH#j|5#-^x3}Y4PuiQj%r`xPx3{*)pwlxs6V71KK7-8p~lmLLMzRQ}!dlpz$){IcNd{?)1^`W;Gyo*>Vt zkr}^r9opR@rTWZOld`=!-(yPtL>r{fW?U=Bscd2^b2pqn%SsnR_*mP6pA@@i9~JVQ zMUIX3tjENKs&!CPL(}P-YwCP*hwGZ(mvl^fi+J!S+12>I;F>6 z=?_Ol5)Ny(iyWPQlARIM+;X<*fA}f;%||>H+<2e0uaoaM1($jmG8Xe@Hn3b7>GEqn zYyoxQ3=Gxg9n-6*8A2sDD6?<0|qXc&y13%t(YG@ot z?&=9-sSm(Dm@X)=N3jLJ5aK&wv*z1Q7=g>ApBhE5Ne`DU1#iG7Y}JFShnz%U-^X>~ zi)9ZvV<`4UQ`6Rl+(HNn4e{eU)Vun9JU!PMC432#L7Mz_mz6Nt`8g~s_x&?PS@<+qRgCj<# zX6tO5Nh3e|PLD%!Cxe@-`~p50v5)GFdP4NWE8ND#q5E*L4RpEI_WzfmV@v`2xctUYIK^5bqEI9MsJq=75+Kbkgp&IyB8ln2io zjUz|XKh5ZiUO6z(;~!;PFeqq!N5_<-7)794>Tfvx{`?Ol7zwpRgxdY!pZV3hJav~> z%cEH>UdfdX5C#&nwwR9%bnI+edO5FWGtunx2(iAl+1~taaTdwFbMW;Xmq{Cq7hq>Q zNG40j)&n28fvndBs+DKLcbSsaQ3%0UB4l!ZUWtzksrwu!;M=_KX^&0$p53fA&lG+Tpy6s={Ey5-J}Ox2XmpT1{>-m z4$4ml{s_kD&_ugb^U&0#roi`fMHuhW;riMKXY7P6Z`Yc>kSGboptJ8sVsrS`+>|2Kxgf5V25@&caim4$eRoCCa#h(B@P3Qpy@lhIV>0;0YLptjEN9H40V`a@VarUiG9&2B#3B@LHtbZj_Y#{nc~72wX>cvCWy zPX=HB(pMbIwx>R3(l^5bd>ALL+OZ9nK&1B zE$AbUd@uGpUD>X!>~^Ab2^ECPbns&DT3X|$6UbIvtH`0%vRD?OWpoiU)fsXYlfJZt zjV9cKDTM?*(3%HZ`yNEI;u`1%6u-sSp;!y)Xr1ybb6K&;3#Qcn9njyQulOBC(DI(#ZUaQsRA4d!G^gBQb)_;uUICqRQ*oKoy5 zm$~Z*wtOdNeW(*MHFBWIL|M#6XJ?tKi^HV0N;dp!QyXu|2nU*%KE8S1B^oqFEs-1k zP$BCs)7ima^UJ4hgJ9JOZhZc;B;O`aA|TVYe?30KvXpyHgQ#xHzbN)ejGgh|9ycEt z4~L7wVu=a657aEf)>1qTzjxlivYmaDH92VT1RChf+#GHA5+5}IIZ=Mf=q*?mHvft_ zo#?Ndn0U1;n)}y0N(9evMO9Qr>@lGi_WS0s%xc&`Bavt5&6pw)UcMb<%x|iOmHIg| zp~LJm`9>rK0dHPQDnxV7X3v)jC+aS*NW!nrBL%A zGQ?!HoV}`^%r~86E%!W-y|epZrn$X8g*#`cBf^R!f#2G=vlQz!HV-EC1eOcT1@k!n z0rhhgG~pJvu6cRL#s=TEUI1OoU>r|iQ9K{SA9@**!-xzk()41Vk`99F3pZe*mJx~q zQG1;1IwH)b>y-M~{>xK5`W9k3PuZgcijyX?2H*7D9195Bm{t6unm^(to_>=|H|Tyj zMfD&s1cG)w3`Y=gPDknjQV7fVM~Nph9*TKAJ&Ojk@JBls;qn%bD+TH+2NyEMDj@Q` zgz5~`Jrk(Zz4Y1Q%Jxb={n<-k5?jy{;afPXHgZ0QsZwgC>s6u@L)>S0JZ8|nSMg75 zLa+agdhx=HX@A+K;If~ZDu#98siuqJb}g|^NfYMno;W7<_SQYsIH-43kJR24)l79c z6>(S^@pwESZ0_K5Ygj{9S~`a3qp12mKQ>acLg=aLoKOYxlg_8&2jfgl}cWDvsKOiw#*@TK~s>r)?kGML$Xx(0rLfH(OJYhw_$6H-tM=0t&gB#UqPADZ^W_b7<#v2i< zQHN)Klh@dsu~qv)w{|mdW@T$~IX;E}d^$!CK|k!WlHksLE85EmmR0c-+yxel4v@F?qk9 zeD-}}79KlmEq9D~_U%>p1Ildq{A{a!U!n_G%aEVC^rYFKn=~4%$!4njsICS2aYTlN zliggzFY3WUG7<_r?JtpOUQS%aMcLJU-8eRf_&(f6>*8&;vFl%Ug#QM^6iBAGUl7)$ zhyVT&kDp+uQpqz#b}t_fdSOL(Vmt@hv_*TSsi0bxHwrVsiucR(#{7@NYN)iaVg9Wz zCw9!Nbl>`Rt2=W*(`&B9f2mKy=BbZaOwHt$;c;$(IPAZMl9|?R!SfCkSJE!`yS|w7 z!ldR7=va3+RYDA=NOpglEI8&)%;V zW~^=ggx>BJ)VRAaAD8J0t-3L}W0O#HeLrLpT4GomUuzf~33~i8TUXIg_DsK%Hm!Kt zHs3*Pfg9;oe6`VoC4Mmf3T*X92MicV3434=Z32m;P- zT+Z*$cvN}wlQ`9p|6uBbMMrtld2Qfc*zfoVg7QC2-4&E4TneF_3H4+{c0faIDT@w4L<9`sP0dZ-*+6Jp$;}uqhI#S zH69k40syiT`^vzOV8UPMTw5s*Zj|=| zGH~o4(Q`WYn6m$wNQ$ze;#*#n8tv&AK|K#TQr?^ga6R|#Ebl3j7)6B$#t6fy4!K6} zjzCPPC!xMfV_!lg0=6D&8-_+VB{d@-L9?hwOgOXt_Xwjwkr$6C;6lnzC$)wya`mW43)l9lcZA<9R-6 zAAQsm4m5uKBZHV>4Ue`uDj~@sttS*KEZZe%O4}86l8J#Pvji;gqc>X?EoU#0(@ovQ zjx}5Y^n^!rG#Nwo2SVP|7PB|)Y8c7-=^II#S<`476SBc(VmbU3-2=}Yksav>!fnFP z$|$_5KDAqczfjp<(J9;bhAVYQtg8OYX|7G3FMp7AottM+684tNIigM7VU*AJVk>t@ zj4kJC-uW&$Z*b-Tx%zkRz+U!6Fz|PIff53{yRB{4qmg~Ht;*h7Mc|`v^7r%NJZY04 zpRHso4{)QtC@W6aq{JEMFs|eU6ogYiU_{>xK8*NUTz$=0dSF|JJ>{pEmx~02lUCxj zqDKg&AfmjLb41D~irFmv<-|UH@pR2l6e6rwnI^y`E`?)n&4z=WSC-SCYpTcI#Hn$g z?7{9K_+DxN>10Km;;gv&yD;UaLt||Q`EZ+N_FI*d ztWUcjOK zo$Fpaf4lcK;-KoJXB%?|8_a!Tz5VedsiN7=0y&14M*V!1>kQ0iU;JCZThJ`Lvdm}$ zc99OS&-&k89{u0L_Gj}4Lik4GPXAev!8#iNz;%A3i9%N84f_7IFU;(+ zbZ2?@Z&Ngkng=V|&@ZFzqnoK`06_GUu!u@5HfU!_i`|2t)|;!6Bm7pS`oKACz`;TD zSr9Ye?F%Xy-b|sXA{iMOv!6-NJ%Pn1o@&Oz23JRe4#+bUDFEP`ZhanVML%yMS~wIm zrZ0WE8Lm{ob0Q1?@E!G7o__Usl6x7f@Q@Q%#9(fD9w4D$?fc}fya^Pdd15bU%f)ZE zSa5uIX@B4a`Rm}5s%$E0^g7A{yPEa<{9kc2_ac%|T3Q;iu{Pd(Dox9ounAW{So)rX zEuT(%iYrp$qX?O2Rh|nW_-C%zkTvRVz7n-!Ys}Pj^ahUC%y+YeF@xM+PCoe)$uQ99 z(K$1%H8W2nUZ z*>N{(P-k&ji%lXY?00$RCiCeUbMVPr$cfLI3n$K1tTAoY8F==i0E*b#dcfbgn$=OB zeZG2dDZPcYk6|u>Uh|#?G^^7w09LctyOEpzD`Mk6ZrP!hP}|efNG2g=`|r?3`V>zqAX4%ZpMoVc zP4e!*=!;B=p3x+fQFW48f$fwSvz8Ob)NA&MA&9JTj9A$- z^L@IPwaCe1i2H17Y$ceQ71yZF8%Z$o(ulUDR<7N^Itm(B8KWY=*KGv%c`Gu~F#9>r z;ER+N$V(E6AJYR&Hjqf<81xouH@8Yp_|?OBD@oXfVX)?@lK)bY(NHj}aNMq^tjZSk z9SFM8bVl81K3***YsdQQyTjFGhrlNxVclhj&^K*~pHq9^WpaJ?9}+p9?pO6O@JW~G z=@Y;|ymQ7Bv3;B+WDhAF&rUoDK_30QvCrz|qrG(^$4cle9#7j3^?e2trofNA>WOYO zx>HJzsbQ}&>!CWlLu+bTu=9MA)hLxA^E81QRF^{jre!*(=dGs?*K`89n9ExOH#f2p;uByc6BiBp{~1P0<=^(5C!#pvIV0@(EH?ZLsV8)NA8W&BbAiwOX7Aq=l<`tvM1xL`*FVa zNfs|erao{PRQkRhnQA@l-F0iYT%jO^C(alvGIoxDi>b3=c&OosdCM`*zdPJ|^uDu( zT7@^FrwV@6d_wew&8=;_Czg@vYXKGirOM?GBUSrfExvBw6z40VMi`Pm4u=<*-dzrJ zi`@)wfYuHxn01(8T!rwG8(;1tr;TJXjt4nq@Si5*qK(fHt^} zd0eTmk1olu$dXYdc|`gDlosUHs>tz$(^GGwIidP_n6{PsPRORUD^JH?;@MiRP(4HC z*i#1AMWcSm?TX-sUyDPSCL3Vtb3pQYjA(PL5f}M!_F&& ze=_WgWi$3ozW(G7lK)U&7?qFby0_jFI-Ed+m3r;kinMCKxrgG-=x?uMQ|F}z={)+H zige5lEhO+Q+OM;&+gFLcg0K_C>UHc_`FMpU%^!o6oZ7#x=|?r(mAOgu*Y9eT^e6lP zCGUNrawSJ@FdyF;+>u=w87-eD12?i9+_J7JNlhjliIU&SLn~aE0B47An7WEsT{|PQ#uh>GCRv& z+dq50S~dg206{u-NcARd;# zz|9L*xck@AYzk$^(a1@xMytsr=yWIhj3jp*@Rm@>^}=zRjM-@TD4eqdI^D!EyUsK$ zww6PM5CPa(8U-1N1kyOfbB+>^Kf^jo)Rk{>|6w3-jCoSqYPvDKRg6;^0Py?T#Kc6M z-%ehX%ztPMjsMvFH`J6+ByC6A^LUyii~76E?E