How to modify or override the /node/add (Add content) page in modern Drupal 8, 9, 10 to reorder, remove, or hide content type links

Hi!

Well first of all if it weren’t for me you could do this easily by disabling the menu item! https://www.drupal.org/node/1009982

Second of all, does this page use the admin theme? If so, are you using a custom admin theme / subtheme? If not… you probably are not, and should not be, putting this file anywhere it could have any effect on your admin theme!

Fortunately, moments before i made a module to fix this myself (feeling somewhat responsible) i finally hit upon the right keywords to bring up a module that already fixes it:

https://www.drupal.org/project/custom_add_content

I highly recommend that over doing anything in the theme, especially the admin theme. They don’t do it quite how i would do it, including they change the path from /node/add to /custom_node_add but you should be able to add a path alias to set it back, or to something more sensible like /add-content

Does that work?

[ * drupal 8 alter theme function from module ]

If you need to customize the look much beyond the order and what content types or other items are shown in the list, you can make your twig template work from a custom module by adding an additional preprocesser like this:

https://drupal.stackexchange.com/a/274784/4195

best,

ben

https://agaric.coop

UPDATE: The module has a major bug, All users, including superuser / admin, get access denied on /custom_node_add [#3381183] | Drupal.org

On 8/10/23 21:02, a client wrote:

Hi Ben,

I would like authors who log into the site be able to create new albums only through an artist and not have the option available on the add content page:

So I want to remove the button on the left. This page is controlled by node-add-list-html.twig I tried to add the lines (in blue) to that list and have been playing around with the conditional in any combination I can imagine, with no impact on hiding that button.

What am I missing here: is node type not available on this twig despite the node_type that I see in the body? If not, how can I bring that into the twig? Am I missing the syntax? any quick pointers?

{#
/**
  * @file
  * Default theme implementation to list node types available for adding 
content.
  *
  * This list is displayed on the Add content admin page.
  *
  * Available variables:
  * - types: A list of content types, each with the following properties:
  *   - add_link: Link to create a piece of content of this type.
  *   - description: Description of this type of content.
  *
  * @see template_preprocess_node_add_list()
  *
  * @ingroup themeable
  */
#}
{# MK changed the article links from dl to a card #}
{% if types is not empty %}
{#     <dl>
     {% for type in types %}
       <dt>{{ type.add_link }}</dt>
       <dd>{{ type.description }}</dd>
     {% endfor %}
   </dl> #}
   {# MK changed div to a, added the whole href bit and striptagging the h3 #}
   {% for type in types %}
     <a href="{{ path('node.add',{'node_type': type.type}) }}" 
id="tps-add-content"  class="w3-card w3-hover-shadow w3-center w3-col l3">
       <h3>{{ type.add_link|striptags }}</h3>
       <p>{{ type.description}}</p>
     </a>
     {% if type.type == "album" %}
     <a href="{{ path('node.add',{'node_type': type.type}) }}" 
id="tps-add-content"  class="w3-hide">
     {% endif %}
   {% endfor %}
{% else %}
   <p>
     {% set create_content = path('node.type_add') %}
     {% trans %}
       You have not created any content types yet. Go to the <a href="{{ 
create_content }}">content type creation page</a> to add a new content type.
     {% endtrans %}
   </p>
{% endif %}

Thanks

This is what needs to be overridden entirely:

  /**
   * Displays add content links for available content types.
   *
   * Redirects to node/add/[type] if only one content type is available.
   *
   * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
   *   A render array for a list of the node types that can be added; however,
   *   if there is only one node type defined for the site, the function
   *   will return a RedirectResponse to the node add page for that one node
   *   type.
   */
  public function addPage() {
    $definition = $this->entityTypeManager()->getDefinition('node_type');
    $build = [
      '#theme' => 'node_add_list',
      '#cache' => [
        'tags' => $this->entityTypeManager()->getDefinition('node_type')->getListCacheTags(),
      ],
    ];

    $content = [];

    $types = $this->entityTypeManager()->getStorage('node_type')->loadMultiple();
    uasort($types, [$definition->getClass(), 'sort']);
    // Only use node types the user has access to.
    foreach ($types as $type) {
      $access = $this->entityTypeManager()->getAccessControlHandler('node')->createAccess($type->id(), NULL, [], TRUE);
      if ($access->isAllowed()) {
        $content[$type->id()] = $type;
      }
      $this->renderer->addCacheableDependency($build, $access);
    }

    // Bypass the node/add listing if only one content type is available.
    if (count($content) == 1) {
      $type = array_shift($content);
      return $this->redirect('node.add', ['node_type' => $type->id()]);
    }

    $build['#content'] = $content;

    return $build;
  }

Rather than changing the path to do this the way Custom Add Content module does, i would have altered the route to change only the controller.

take over a route from a module Drupal 8

In web/core/modules/node/node.routing.yml we find:

node.add_page:
  path: '/node/add'
  defaults:
    _title: 'Add content'
    _controller: '\Drupal\node\Controller\NodeController::addPage'
  options:
    _node_operation_route: TRUE
  requirements:
    _entity_create_any_access: 'node'

Official documentation on altering, overriding, or removing routes is pretty good:

https://www.drupal.org/docs/drupal-apis/routing-system/altering-existing-routes-and-adding-new-routes-based-on-dynamic-ones

The self-answer here for “How do I alter the route defined by another module?” in Drupal 8 is nice for having an example of overriding the controller specifically.

  protected function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('node.add')) {
      $route->setDefault('_controller', '\Drupal\ben_makes_amends_feature_not_a_bug_world_tour\Controller\NodeController::addPage');
    }
  }

Fortunately, despite feeling responsible for disconnecting the content adding menu from the content adding page, before i got into figuring out how to allow re-ordering and hiding content add link items in a way that surely would have been more difficult than re-using the menu system!

Oh and the answer about Drupal 7 from wizonesolutions is where i first saw the finger pointing back at me and was put on that trail.