From 7184e9458cee2feac3c061a41ec37e28478c8e6c Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 20 Mar 2024 08:21:30 +0000
Subject: [PATCH] Issue #3420999 by sorlov, larowlan, smustgrave, mstrelan:
 Convert ViewsRow plugin discovery to attributes

---
 .../comment/src/Plugin/views/row/Rss.php      | 21 ++++---
 .../node/src/Plugin/views/row/NodeRow.php     |  6 +-
 .../modules/node/src/Plugin/views/row/Rss.php | 21 ++++---
 .../src/Plugin/views/row/DataEntityRow.php    | 15 ++---
 .../src/Plugin/views/row/DataFieldRow.php     | 15 ++---
 .../search/src/Plugin/views/row/SearchRow.php | 13 ++--
 .../user/src/Plugin/views/row/UserRow.php     |  6 +-
 core/modules/views/src/Attribute/ViewsRow.php | 63 +++++++++++++++++++
 .../src/Plugin/views/row/EntityReference.php  | 19 +++---
 .../views/src/Plugin/views/row/EntityRow.php  | 11 ++--
 .../views/src/Plugin/views/row/Fields.php     | 17 ++---
 .../views/src/Plugin/views/row/OpmlFields.php | 17 ++---
 .../src/Plugin/views/row/RowPluginBase.php    |  2 +-
 .../views/src/Plugin/views/row/RssFields.php  | 17 ++---
 .../src/Plugin/views/row/RowTest.php          | 17 ++---
 15 files changed, 165 insertions(+), 95 deletions(-)
 create mode 100644 core/modules/views/src/Attribute/ViewsRow.php

diff --git a/core/modules/comment/src/Plugin/views/row/Rss.php b/core/modules/comment/src/Plugin/views/row/Rss.php
index 5be3a680a4f3..78f9083280fe 100644
--- a/core/modules/comment/src/Plugin/views/row/Rss.php
+++ b/core/modules/comment/src/Plugin/views/row/Rss.php
@@ -2,21 +2,22 @@
 
 namespace Drupal\comment\Plugin\views\row;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\RssPluginBase;
 
 /**
  * Plugin which formats the comments as RSS items.
- *
- * @ViewsRow(
- *   id = "comment_rss",
- *   title = @Translation("Comment"),
- *   help = @Translation("Display the comment as RSS."),
- *   theme = "views_view_row_rss",
- *   register_theme = FALSE,
- *   base = {"comment_field_data"},
- *   display_types = {"feed"}
- * )
  */
+#[ViewsRow(
+  id: "comment_rss",
+  title: new TranslatableMarkup("Comment"),
+  help: new TranslatableMarkup("Display the comment as RSS."),
+  theme: "views_view_row_rss",
+  register_theme: FALSE,
+  base: ["comment_field_data"],
+  display_types: ["feed"]
+)]
 class Rss extends RssPluginBase {
 
   /**
diff --git a/core/modules/node/src/Plugin/views/row/NodeRow.php b/core/modules/node/src/Plugin/views/row/NodeRow.php
index 7f061bae5cc8..082db0752d54 100644
--- a/core/modules/node/src/Plugin/views/row/NodeRow.php
+++ b/core/modules/node/src/Plugin/views/row/NodeRow.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\node\Plugin\views\row;
 
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\EntityRow;
 
 /**
@@ -10,11 +11,8 @@
  * Most of the code on this object is in the theme function.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "entity:node",
- * )
  */
+#[ViewsRow("entity:node")]
 class NodeRow extends EntityRow {
 
   /**
diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php
index 4a74d4da167f..3189a47f441e 100644
--- a/core/modules/node/src/Plugin/views/row/Rss.php
+++ b/core/modules/node/src/Plugin/views/row/Rss.php
@@ -2,21 +2,22 @@
 
 namespace Drupal\node\Plugin\views\row;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\RssPluginBase;
 
 /**
  * Performs a node_view on the resulting object and formats it as an RSS item.
- *
- * @ViewsRow(
- *   id = "node_rss",
- *   title = @Translation("Content"),
- *   help = @Translation("Display the content with standard node view."),
- *   theme = "views_view_row_rss",
- *   register_theme = FALSE,
- *   base = {"node_field_data"},
- *   display_types = {"feed"}
- * )
  */
+#[ViewsRow(
+  id: "node_rss",
+  title: new TranslatableMarkup("Content"),
+  help: new TranslatableMarkup("Display the content with standard node view."),
+  theme: "views_view_row_rss",
+  register_theme: FALSE,
+  base: ["node_field_data"],
+  display_types: ["feed"]
+)]
 class Rss extends RssPluginBase {
 
   /**
diff --git a/core/modules/rest/src/Plugin/views/row/DataEntityRow.php b/core/modules/rest/src/Plugin/views/row/DataEntityRow.php
index f583687a82eb..e77ba33c7d8a 100644
--- a/core/modules/rest/src/Plugin/views/row/DataEntityRow.php
+++ b/core/modules/rest/src/Plugin/views/row/DataEntityRow.php
@@ -5,6 +5,8 @@
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
 use Drupal\views\Plugin\views\row\RowPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -13,14 +15,13 @@
  * Plugin which displays entities as raw data.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "data_entity",
- *   title = @Translation("Entity"),
- *   help = @Translation("Use entities as row data."),
- *   display_types = {"data"}
- * )
  */
+#[ViewsRow(
+  id: "data_entity",
+  title: new TranslatableMarkup("Entity"),
+  help: new TranslatableMarkup("Use entities as row data."),
+  display_types: ["data"]
+)]
 class DataEntityRow extends RowPluginBase {
 
   use EntityTranslationRenderTrait;
diff --git a/core/modules/rest/src/Plugin/views/row/DataFieldRow.php b/core/modules/rest/src/Plugin/views/row/DataFieldRow.php
index b328e452e03e..e0efc39d8dde 100644
--- a/core/modules/rest/src/Plugin/views/row/DataFieldRow.php
+++ b/core/modules/rest/src/Plugin/views/row/DataFieldRow.php
@@ -3,6 +3,8 @@
 namespace Drupal\rest\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\row\RowPluginBase;
@@ -11,14 +13,13 @@
  * Plugin which displays fields as raw data.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "data_field",
- *   title = @Translation("Fields"),
- *   help = @Translation("Use fields as row data."),
- *   display_types = {"data"}
- * )
  */
+#[ViewsRow(
+  id: "data_field",
+  title: new TranslatableMarkup("Fields"),
+  help: new TranslatableMarkup("Use fields as row data."),
+  display_types: ["data"]
+)]
 class DataFieldRow extends RowPluginBase {
 
   /**
diff --git a/core/modules/search/src/Plugin/views/row/SearchRow.php b/core/modules/search/src/Plugin/views/row/SearchRow.php
index 29e150d557ba..1ea7dca8174a 100644
--- a/core/modules/search/src/Plugin/views/row/SearchRow.php
+++ b/core/modules/search/src/Plugin/views/row/SearchRow.php
@@ -3,17 +3,18 @@
 namespace Drupal\search\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\RowPluginBase;
 
 /**
  * Row handler plugin for displaying search results.
- *
- * @ViewsRow(
- *   id = "search_view",
- *   title = @Translation("Search results"),
- *   help = @Translation("Provides a row plugin to display search results.")
- * )
  */
+#[ViewsRow(
+  id: "search_view",
+  title: new TranslatableMarkup("Search results"),
+  help: new TranslatableMarkup("Provides a row plugin to display search results.")
+)]
 class SearchRow extends RowPluginBase {
 
   /**
diff --git a/core/modules/user/src/Plugin/views/row/UserRow.php b/core/modules/user/src/Plugin/views/row/UserRow.php
index 38991493f11c..751156b39156 100644
--- a/core/modules/user/src/Plugin/views/row/UserRow.php
+++ b/core/modules/user/src/Plugin/views/row/UserRow.php
@@ -2,17 +2,15 @@
 
 namespace Drupal\user\Plugin\views\row;
 
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\EntityRow;
 
 /**
  * A row plugin which renders a user.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "entity:user",
- * )
  */
+#[ViewsRow("entity:user")]
 class UserRow extends EntityRow {
 
   /**
diff --git a/core/modules/views/src/Attribute/ViewsRow.php b/core/modules/views/src/Attribute/ViewsRow.php
new file mode 100644
index 000000000000..378188c6b23c
--- /dev/null
+++ b/core/modules/views/src/Attribute/ViewsRow.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\views\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a ViewsRow attribute for plugin discovery.
+ *
+ * @see \Drupal\views\Plugin\views\style\StylePluginBase
+ *
+ * @ingroup views_row_plugins
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class ViewsRow extends Plugin {
+
+  /**
+   * Constructs an ViewsRow attribute.
+   *
+   * @param string $id
+   *   The plugin ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $title
+   *   (optional) The plugin title used in the views UI.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $short_title
+   *   (optional) The short title used in the views UI.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $help
+   *   (optional) A short help string; this is displayed in the views UI.
+   * @param string[] $display_types
+   *   (optional) The types of the display this plugin can be used with.
+   *   For example the Feed display defines the type 'feed', so only rss style
+   *   and row plugins can be used in the views UI.
+   * @param string[] $base
+   *   (optional) The base tables on which this style plugin can be used.
+   *   If no base table is specified the plugin can be used with all tables.
+   * @param string|null $theme
+   *   (optional) The theme function used to render the style output.
+   * @param bool $no_ui
+   *   (optional) Whether the plugin should be not selectable in the UI.
+   *   If set to TRUE, you can still use it via the API in config files.
+   *   Defaults to FALSE.
+   * @param bool $register_theme
+   *   (optional) Whether to register a theme function automatically. Defaults
+   *   to TRUE.
+   * @param class-string|null $deriver
+   *   (optional) The deriver class.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly ?TranslatableMarkup $title = NULL,
+    public readonly ?TranslatableMarkup $short_title = NULL,
+    public readonly ?TranslatableMarkup $help = NULL,
+    public readonly array $display_types = [],
+    public readonly array $base = [],
+    public readonly ?string $theme = NULL,
+    public readonly bool $no_ui = FALSE,
+    public readonly bool $register_theme = TRUE,
+    public readonly ?string $deriver = NULL
+  ) {}
+
+}
diff --git a/core/modules/views/src/Plugin/views/row/EntityReference.php b/core/modules/views/src/Plugin/views/row/EntityReference.php
index 29661e3e08ea..222edef47352 100644
--- a/core/modules/views/src/Plugin/views/row/EntityReference.php
+++ b/core/modules/views/src/Plugin/views/row/EntityReference.php
@@ -3,21 +3,22 @@
 namespace Drupal\views\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 
 /**
  * EntityReference row plugin.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "entity_reference",
- *   title = @Translation("Entity Reference inline fields"),
- *   help = @Translation("Displays the fields with an optional template."),
- *   theme = "views_view_fields",
- *   register_theme = FALSE,
- *   display_types = {"entity_reference"}
- * )
  */
+#[ViewsRow(
+  id: "entity_reference",
+  title: new TranslatableMarkup("Entity Reference inline fields"),
+  help: new TranslatableMarkup("Displays the fields with an optional template."),
+  theme: "views_view_fields",
+  register_theme: FALSE,
+  display_types: ["entity_reference"]
+)]
 class EntityReference extends Fields {
 
   /**
diff --git a/core/modules/views/src/Plugin/views/row/EntityRow.php b/core/modules/views/src/Plugin/views/row/EntityRow.php
index bbcdee8983aa..210e92caa736 100644
--- a/core/modules/views/src/Plugin/views/row/EntityRow.php
+++ b/core/modules/views/src/Plugin/views/row/EntityRow.php
@@ -7,19 +7,20 @@
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
+use Drupal\views\Plugin\Derivative\ViewsEntityRow;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ViewExecutable;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Generic entity row plugin to provide a common base for all entity types.
- *
- * @ViewsRow(
- *   id = "entity",
- *   deriver = "Drupal\views\Plugin\Derivative\ViewsEntityRow"
- * )
  */
+#[ViewsRow(
+  id: "entity",
+  deriver: ViewsEntityRow::class
+)]
 class EntityRow extends RowPluginBase {
   use EntityTranslationRenderTrait;
 
diff --git a/core/modules/views/src/Plugin/views/row/Fields.php b/core/modules/views/src/Plugin/views/row/Fields.php
index 647ddce89fc6..2687b34ab79a 100644
--- a/core/modules/views/src/Plugin/views/row/Fields.php
+++ b/core/modules/views/src/Plugin/views/row/Fields.php
@@ -3,6 +3,8 @@
 namespace Drupal\views\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 
 /**
  * The basic 'fields' row plugin.
@@ -11,15 +13,14 @@
  * or not.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "fields",
- *   title = @Translation("Fields"),
- *   help = @Translation("Displays the fields with an optional template."),
- *   theme = "views_view_fields",
- *   display_types = {"normal"}
- * )
  */
+#[ViewsRow(
+  id: "fields",
+  title: new TranslatableMarkup("Fields"),
+  help: new TranslatableMarkup("Displays the fields with an optional template."),
+  theme: "views_view_fields",
+  display_types: ["normal"]
+)]
 class Fields extends RowPluginBase {
 
   /**
diff --git a/core/modules/views/src/Plugin/views/row/OpmlFields.php b/core/modules/views/src/Plugin/views/row/OpmlFields.php
index c5116f3fbd49..ca88dbc6d826 100644
--- a/core/modules/views/src/Plugin/views/row/OpmlFields.php
+++ b/core/modules/views/src/Plugin/views/row/OpmlFields.php
@@ -3,18 +3,19 @@
 namespace Drupal\views\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 
 /**
  * Renders an OPML item based on fields.
- *
- * @ViewsRow(
- *   id = "opml_fields",
- *   title = @Translation("OPML fields"),
- *   help = @Translation("Display fields as OPML items."),
- *   theme = "views_view_row_opml",
- *   display_types = {"feed"}
- * )
  */
+#[ViewsRow(
+  id: "opml_fields",
+  title: new TranslatableMarkup("OPML fields"),
+  help: new TranslatableMarkup("Display fields as OPML items."),
+  theme: "views_view_row_opml",
+  display_types: ["feed"]
+)]
 class OpmlFields extends RowPluginBase {
 
   /**
diff --git a/core/modules/views/src/Plugin/views/row/RowPluginBase.php b/core/modules/views/src/Plugin/views/row/RowPluginBase.php
index d3deb5b94e2a..445aa54e2489 100644
--- a/core/modules/views/src/Plugin/views/row/RowPluginBase.php
+++ b/core/modules/views/src/Plugin/views/row/RowPluginBase.php
@@ -21,7 +21,7 @@
  * more information.
  *
  * Row plugins extend \Drupal\views\Plugin\views\row\RowPluginBase. They must
- * be annotated with \Drupal\views\Annotation\ViewsRow annotation, and
+ * be attributed with \Drupal\views\Attribute\ViewsRow attribute, and
  * they must be in namespace directory Plugin\views\row.
  *
  * @ingroup views_plugins
diff --git a/core/modules/views/src/Plugin/views/row/RssFields.php b/core/modules/views/src/Plugin/views/row/RssFields.php
index 924cd7b6e287..64cb267b5c28 100644
--- a/core/modules/views/src/Plugin/views/row/RssFields.php
+++ b/core/modules/views/src/Plugin/views/row/RssFields.php
@@ -3,19 +3,20 @@
 namespace Drupal\views\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Url;
+use Drupal\views\Attribute\ViewsRow;
 
 /**
  * Renders an RSS item based on fields.
- *
- * @ViewsRow(
- *   id = "rss_fields",
- *   title = @Translation("Fields"),
- *   help = @Translation("Display fields as RSS items."),
- *   theme = "views_view_row_rss",
- *   display_types = {"feed"}
- * )
  */
+#[ViewsRow(
+  id: "rss_fields",
+  title: new TranslatableMarkup("Fields"),
+  help: new TranslatableMarkup("Display fields as RSS items."),
+  theme: "views_view_row_rss",
+  display_types: ["feed"]
+)]
 class RssFields extends RowPluginBase {
 
   /**
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/row/RowTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/row/RowTest.php
index 6af493b1e778..248abc13afd1 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/row/RowTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/row/RowTest.php
@@ -3,21 +3,22 @@
 namespace Drupal\views_test_data\Plugin\views\row;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\views\Attribute\ViewsRow;
 use Drupal\views\Plugin\views\row\RowPluginBase;
 
 /**
  * Provides a general test row plugin.
  *
  * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "test_row",
- *   title = @Translation("Test row plugin"),
- *   help = @Translation("Provides a generic row test plugin."),
- *   theme = "views_view_row_test",
- *   display_types = {"normal", "test"}
- * )
  */
+#[ViewsRow(
+  id: "test_row",
+  title: new TranslatableMarkup("Test row plugin"),
+  help: new TranslatableMarkup("Provides a generic row test plugin."),
+  theme: "views_view_row_test",
+  display_types: ["normal", "test"]
+)]
 class RowTest extends RowPluginBase {
 
   /**
-- 
GitLab