- Issue created by @nicxvan
- πΊπΈUnited States nicxvan
For local testing @bbrala mentioned this in slack:
The test structure has a configured rule file which configures which rules to run. Check out the other tests for the structure. This is the same default structure as rector has itself.
For locally, you could copy the default rector.php file and remove the sets and only add your rule. - πΊπΈUnited States nicxvan
I'm having trouble running this locally against announcements_feed.
I am running this:
./vendor/bin/phpcbf \ --standard="Drupal,DrupalPractice" -n \ --extensions="php,module,inc,install,test,profile,theme" \ core/modules/announcements_feed
Here is a draft pr: https://github.com/palantirnet/drupal-rector/pull/308/files
Locally I commented out the other rules just to test this one.
- πΊπΈUnited States nicxvan
Ok I got this working with the PR here:
I need some advice on how to include this since it's not a deprecation yet, we can use this just for core for now.
My Draft comments out all of the drupal deprecations just for the hook convert rule.I had to read the README here: https://github.com/palantirnet/drupal-rector-sandbox
How I got this working locally in ddev:
First require drupal-rector
ddev composer require --dev palantirnet/drupal-rector
I then copied my changes from the PR: https://github.com/palantirnet/drupal-rector/pull/308/fileCreate rector web command in ddev:
#!/bin/bash ## Description: Fix code ## Usage: rector ## Example: rector ./vendor/bin/rector \ --config ./vendor/palantirnet/drupal-rector/rector.php \ process \ core/modules/announcements_feed
Run
ddev rector
Create codefix command in ddev:
#!/bin/bash ## Description: Sniff code ## Usage: cdf ## Example: cdf ./vendor/bin/phpcbf \ --standard="Drupal,DrupalPractice" -n \ --extensions="php,module,inc,install,test,profile,theme" \ core/modules/announcements_feed/src/Hook
BEFORE
announcements_feed.module<?php /** * @file * Fetch community announcements from www.drupal.org feed. */ use Drupal\announcements_feed\RenderCallbacks; use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; /** * Implements hook_help(). */ function announcements_feed_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.announcements_feed': $output = ''; $output .= '<h2>' . t('About') . '</h2>'; $output .= '<p>' . t('The Announcements module displays announcements from the Drupal community. For more information, see the <a href=":documentation">online documentation for the Announcements module</a>.', [':documentation' => 'https://www.drupal.org/docs/core-modules-and-themes/core-modules/announcements-feed']) . '</p>'; $output .= '<h2>' . t('Uses') . '</h2>'; $output .= '<dl><dt>' . t('Accessing announcements') . '</dt>'; $output .= '<dd>' . t('Users with the "View drupal.org announcements" permission may click on the "Announcements" item in the administration toolbar, or access @link, to see all announcements relevant to the Drupal version of your site.', [ '@link' => Link::createFromRoute(t('Announcements'), 'announcements_feed.announcement')->toString(), ]) . '</dd>'; $output .= '</dl>'; return $output; } } /** * Implements hook_toolbar(). */ function announcements_feed_toolbar() { if (!\Drupal::currentUser()->hasPermission('access announcements')) { return [ '#cache' => ['contexts' => ['user.permissions']], ]; } $items['announcement'] = [ '#type' => 'toolbar_item', 'tab' => [ '#lazy_builder' => [ 'announcements_feed.lazy_builders:renderAnnouncements', [], ], '#create_placeholder' => TRUE, '#cache' => [ 'tags' => [ 'announcements_feed:feed', ], ], ], '#wrapper_attributes' => [ 'class' => ['announce-toolbar-tab'], ], '#cache' => ['contexts' => ['user.permissions']], '#weight' => 3399, ]; // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an // #attributes property to each toolbar item's tab child automatically. // Lazy builders don't support an #attributes property so we need to // add another render callback to remove the #attributes property. We start by // adding the defaults, and then we append our own pre render callback. $items['announcement'] += \Drupal::service('plugin.manager.element_info')->getInfo('toolbar_item'); $items['announcement']['#pre_render'][] = [RenderCallbacks::class, 'removeTabAttributes']; return $items; } /** * Implements hook_toolbar_alter(). */ function announcements_feed_toolbar_alter(&$items) { // As the "Announcements" link is shown already in the top toolbar bar, we // don't need it again in the administration menu tray, so hide it. if (!empty($items['administration']['tray'])) { $callable = function (array $element) { unset($element['administration_menu']['#items']['announcements_feed.announcement']); return $element; }; $items['administration']['tray']['toolbar_administration']['#pre_render'][] = $callable; } } /** * Implements hook_theme(). */ function announcements_feed_theme($existing, $type, $theme, $path) { return [ 'announcements_feed' => [ 'variables' => [ 'featured' => NULL, 'standard' => NULL, 'count' => 0, 'feed_link' => '', ], ], 'announcements_feed_admin' => [ 'variables' => [ 'featured' => NULL, 'standard' => NULL, 'count' => 0, 'feed_link' => '', ], ], ]; } /** * Implements hook_cron(). */ function announcements_feed_cron() { $config = \Drupal::config('announcements_feed.settings'); $interval = $config->get('cron_interval'); $last_check = \Drupal::state()->get('announcements_feed.last_fetch', 0); $time = \Drupal::time()->getRequestTime(); if (($time - $last_check) > $interval) { \Drupal::service('announcements_feed.fetcher')->fetch(TRUE); \Drupal::state()->set('announcements_feed.last_fetch', $time); } }
AFTER
announcements_feed.module<?php /** * @file * Fetch community announcements from www.drupal.org feed. */ use Drupal\Core\Hook\LegacyHook; use Drupal\announcements_feed\Hook\AnnouncementsFeedHooks; use Drupal\announcements_feed\RenderCallbacks; use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; /** * Implements hook_help(). */ #[LegacyHook] function announcements_feed_help($route_name, RouteMatchInterface $route_match) { \Drupal::service(AnnouncementsFeedHooks::CLASS)->announcementsFeedHelp($route_name, $route_match); } /** * Implements hook_toolbar(). */ #[LegacyHook] function announcements_feed_toolbar() { \Drupal::service(AnnouncementsFeedHooks::CLASS)->announcementsFeedToolbar(); } /** * Implements hook_toolbar_alter(). */ #[LegacyHook] function announcements_feed_toolbar_alter(&$items) { \Drupal::service(AnnouncementsFeedHooks::CLASS)->announcementsFeedToolbarAlter($items); } /** * Implements hook_theme(). */ #[LegacyHook] function announcements_feed_theme($existing, $type, $theme, $path) { \Drupal::service(AnnouncementsFeedHooks::CLASS)->announcementsFeedTheme($existing, $type, $theme, $path); } /** * Implements hook_cron(). */ #[LegacyHook] function announcements_feed_cron() { \Drupal::service(AnnouncementsFeedHooks::CLASS)->announcementsFeedCron(); }
AnnouncementsFeedHooks.php
<?php namespace Drupal\announcements_feed\Hook; use Drupal\announcements_feed\RenderCallbacks; use Drupal\Core\Hook\Hook; use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; /** * */ class AnnouncementsFeedHooks { /** * Implements hook_help(). */ #[Hook('help')] public function announcementsFeedHelp($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.announcements_feed': $output = ''; $output .= '<h2>' . \t('About') . '</h2>'; $output .= '<p>' . \t('The Announcements module displays announcements from the Drupal community. For more information, see the <a href=":documentation">online documentation for the Announcements module</a>.', [':documentation' => 'https://www.drupal.org/docs/core-modules-and-themes/core-modules/announcements-feed']) . '</p>'; $output .= '<h2>' . \t('Uses') . '</h2>'; $output .= '<dl><dt>' . \t('Accessing announcements') . '</dt>'; $output .= '<dd>' . \t('Users with the "View drupal.org announcements" permission may click on the "Announcements" item in the administration toolbar, or access @link, to see all announcements relevant to the Drupal version of your site.', ['@link' => Link::createFromRoute(\t('Announcements'), 'announcements_feed.announcement')->toString()]) . '</dd>'; $output .= '</dl>'; return $output; } } /** * Implements hook_toolbar(). */ #[Hook('toolbar')] public function announcementsFeedToolbar() { if (!\Drupal::currentUser()->hasPermission('access announcements')) { return ['#cache' => ['contexts' => ['user.permissions']]]; } $items['announcement'] = ['#type' => 'toolbar_item', 'tab' => ['#lazy_builder' => ['announcements_feed.lazy_builders:renderAnnouncements', []], '#create_placeholder' => \TRUE, '#cache' => ['tags' => ['announcements_feed:feed']]], '#wrapper_attributes' => ['class' => ['announce-toolbar-tab']], '#cache' => ['contexts' => ['user.permissions']], '#weight' => 3399]; // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an // #attributes property to each toolbar item's tab child automatically. // Lazy builders don't support an #attributes property so we need to // add another render callback to remove the #attributes property. We start by // adding the defaults, and then we append our own pre render callback. $items['announcement'] += \Drupal::service('plugin.manager.element_info')->getInfo('toolbar_item'); $items['announcement']['#pre_render'][] = [RenderCallbacks::class, 'removeTabAttributes']; return $items; } /** * Implements hook_toolbar_alter(). */ #[Hook('toolbar_alter')] public function announcementsFeedToolbarAlter(&$items) { // As the "Announcements" link is shown already in the top toolbar bar, we // don't need it again in the administration menu tray, so hide it. if (!empty($items['administration']['tray'])) { $callable = function (array $element) { unset($element['administration_menu']['#items']['announcements_feed.announcement']); return $element; }; $items['administration']['tray']['toolbar_administration']['#pre_render'][] = $callable; } } /** * Implements hook_theme(). */ #[Hook('theme')] public function announcementsFeedTheme($existing, $type, $theme, $path) { return ['announcements_feed' => ['variables' => ['featured' => \NULL, 'standard' => \NULL, 'count' => 0, 'feed_link' => '']], 'announcements_feed_admin' => ['variables' => ['featured' => \NULL, 'standard' => \NULL, 'count' => 0, 'feed_link' => '']]]; } /** * Implements hook_cron(). */ #[Hook('cron')] public function announcementsFeedCron() { $config = \Drupal::config('announcements_feed.settings'); $interval = $config->get('cron_interval'); $last_check = \Drupal::state()->get('announcements_feed.last_fetch', 0); $time = \Drupal::time()->getRequestTime(); if ($time - $last_check > $interval) { \Drupal::service('announcements_feed.fetcher')->fetch(\TRUE); \Drupal::state()->set('announcements_feed.last_fetch', $time); } } }
- πΊπΈUnited States nicxvan
You can also see a more full test against the user module here: π Test issue please ignore: Rector on user Active
- πΊπΈUnited States nicxvan
I could use some advice on how to inject this as a separate convert command for core.
We will likely want to add this for contrib too once procedural hooks are deprecated, but we have some time for that.
It's obviously not ready for merging, but it's probably close enough for an approach review.
There are several warnings in the rule that I can look at separately.
- πΊπΈUnited States nicxvan
Thanks for taking a look, the extra changes were so I could run it locally until I got your advice.
If you want to see it run against the user module you can see it here: https://git.drupalcode.org/project/drupal/-/merge_requests/9890/diffs