diff --git a/README.md b/README.md
index 881d156aa9aeb8caf8c17ff317ff327c2f7e4947..6c2e2b004dad4c35106dc55385a8a943ae094127 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,57 @@
-# Summary
+# Advertisement
 
-This module provides a flexible and extensible advertising system allowing
-display ADs via blocks.
+The Advertisement module provides a flexible and extensible advertising system
+allowing to display ads via the block layout.
 
-This AD system supports enabling multiple AD sources and tracking systems: by
-default both AD data and AD tracking data are stored in the local database, but
-alternative AD sources or trackers can be provided via plugins. For instance a
-module could define a plugin to track AD events via Google Analytics.
+The advertisement system is built dynamically so that both content providers
+(advertisement buckets) and statistics trackers can be extended by custom
+modules via the plugin API. A native provider and tracker are also provided as
+submodules.
 
-The native `AD Tracker` module implements two trackers:
-- The `Local AD event tracker` tracks impressions/clicks immediately, which on
-  high traffic sites can result in significant additional load for the web
-  servers.
-- The `Queue-based local AD event tracker` tracks data via a queue, so events
-  will only actually appear in the statistics display section after running
-  cron or after manually processing `ad_track_queue`.
+The native tracker module implements two trackers:
+- The *"Local advertisement event tracker"* tracks impressions/clicks
+immediately, which on high traffic sites can result in significant additional
+load for the web servers.
+- The *"Queue-based local advertisement event tracker"* tracks data via a queue,
+so events will only actually appear in the statistics display section after
+running cron or after manually processing `ad_track_queue`.
 
+For a full description of the module, visit the
+[project page](https://www.drupal.org/project/ad).
 
-# Installation
+Submit bug reports and feature suggestions, or track changes in the
+[issue queue](https://www.drupal.org/project/issues/ad).
 
-Enable the main `AD` module, together with at least one AD source and one
-tracker, typically the `AD Content` and `AD Tracker` modules.
 
+## Requirements
 
-# Usage
+This module requires the following modules:
 
-- After enabling all the required modules, configure a tracker for every enabled
-AD source at `/admin/ad/settings`.
-- Place one or more AD blocks as needed.
+- [**Scheduler**](https://www.drupal.org/project/scheduler) - Required when
+using the *"Advertisement scheduler"* submodule.
+
+
+## Installation
+
+Install as you would normally install a contributed Drupal module. For further
+information, see
+[Installing Drupal Modules](https://www.drupal.org/docs/extending-drupal/installing-drupal-modules).
+
+
+## Configuration
+
+After the installation, you need to install an advertisement content provider
+(e.g., the *"Advertisement content"* submodule) in order for the system to work.
+
+If you install the native *"Advertisement track"* tracker module, the tracker
+will work out of the box. In case you want to install a custom statistics
+tracker, perform the following steps to enable it:
+
+1. Go to *Configuration » Content authoring » Advertisement settings*.
+1. Select the tracker that you want to use for each advertisement content
+provider that you installed (the native tracker module will enable itself
+automatically here).
+
+After that, you can place one or more advertisement blocks in your block layout,
+which will then display the randomly chosen ads. *In order for ads to be shown
+in a block, their selected size must match that of the block!*
diff --git a/ad.config_translation.yml b/ad.config_translation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..64cb1233ca50f8a6bdf0aa8e4fa11233331c2155
--- /dev/null
+++ b/ad.config_translation.yml
@@ -0,0 +1,5 @@
+ad.settings:
+  title: 'Advertisement settings'
+  base_route_name: ad.settings
+  names:
+    - ad.settings
diff --git a/ad.info.yml b/ad.info.yml
index d689954a662c3b841cb9dae3a7fea5cd1758f339..0206c510702682ec6e523a1dfb7e88b08e82b162 100644
--- a/ad.info.yml
+++ b/ad.info.yml
@@ -1,4 +1,4 @@
-name: AD
+name: Advertisement
 type: module
 description: 'A flexible and extensible advertising system.'
 package: Advertisement
diff --git a/ad.links.menu.yml b/ad.links.menu.yml
index 10f53fc904deeb98fb4f450d3b85982cfd8724b5..f1ad3627e35b7f8e8038fcdf24f8c07d416baac8 100644
--- a/ad.links.menu.yml
+++ b/ad.links.menu.yml
@@ -1,6 +1,5 @@
-ad.admin:
-  title: 'Advertisement'
-  description: 'Configure and manage all advertisement settings.'
+ad.settings:
+  title: 'Advertisement settings'
+  description: 'Configure and manage advertisement settings.'
   route_name: ad.settings
-  parent: system.admin
-  weight: 4
+  parent: system.admin_config_content
diff --git a/ad.links.task.yml b/ad.links.task.yml
new file mode 100644
index 0000000000000000000000000000000000000000..042be817aad089cec7a2212a22a82f94b68ed6d0
--- /dev/null
+++ b/ad.links.task.yml
@@ -0,0 +1,4 @@
+ad.settings:
+  route_name: ad.settings
+  title: 'Advertisement settings'
+  base_route: ad.settings
diff --git a/ad.module b/ad.module
index 4b13575190fee7be348ae7856b452aad62fa700d..3a49052e7725859adb1a7cf1e2dc79ff2c1fb6f0 100644
--- a/ad.module
+++ b/ad.module
@@ -2,13 +2,13 @@
 
 /**
  * @file
- * Main file for the "AD" module.
+ * Main file for the ad module.
  */
 
 use Drupal\ad\Size\SizeInterface;
 
 /**
- * Returns the available AD sizes.
+ * Returns the available ad sizes.
  *
  * @return string[]
  *   An array of array size IDs.
diff --git a/ad.permissions.yml b/ad.permissions.yml
index d016025b3aa38174dcd04608f6d23a1887b38daf..8fb4c06be19085f3b636024cc58a9108e3704992 100644
--- a/ad.permissions.yml
+++ b/ad.permissions.yml
@@ -1,8 +1,8 @@
 administer ads:
-  title: 'Administer ADs'
-  description: 'Manage all advertisements.'
+  title: 'Administer advertisements'
+  description: 'Manage advertisements.'
   restrict access: true
 administer ad settings:
-  title: 'Administer AD settings'
-  description: 'Configure and manage all advertisement settings.'
+  title: 'Administer advertisement settings'
+  description: 'Configure and manage advertisement settings.'
   restrict access: true
diff --git a/ad.routing.yml b/ad.routing.yml
index 1f7e692cf097a2782dec227cf14ad753d6bbc2e5..13985c56cca5eb0ea85f2d916bdc1e24fe4bc103 100644
--- a/ad.routing.yml
+++ b/ad.routing.yml
@@ -1,7 +1,7 @@
 ad.settings:
-  path: '/admin/ad/settings'
+  path: '/admin/config/content/ad'
   defaults:
     _form: '\Drupal\ad\Form\SettingsForm'
-    _title: 'Settings'
+    _title: 'Advertisement settings'
   requirements:
     _permission: 'administer ad settings'
diff --git a/ad.services.yml b/ad.services.yml
index 8aaab9ec10ed191d88a668fac49ff96c72476a18..2a92575598e5f4b5c2485559cc5fc0e67249349e 100644
--- a/ad.services.yml
+++ b/ad.services.yml
@@ -1,7 +1,8 @@
 services:
   ad.bucket_factory:
     class: Drupal\ad\Bucket\BucketFactory
-    arguments: ['@ad.bucket_plugin_manager', '@config.factory', '@request_stack']
+    arguments:
+      ['@ad.bucket_plugin_manager', '@config.factory', '@request_stack']
   ad.bucket_plugin_manager:
     class: Drupal\ad\Bucket\BucketPluginManager
     arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
diff --git a/config/install/ad.settings.yml b/config/install/ad.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c7b13c5e9f256560c18fe51e646cba733ff912e3
--- /dev/null
+++ b/config/install/ad.settings.yml
@@ -0,0 +1,2 @@
+trackers: {}
+advertisement_indicator: 'Advertisement'
diff --git a/config/schema/ad.schema.yml b/config/schema/ad.schema.yml
index a5d0fac7dd2db53878f0d03172a53eb0da49e6cd..6a54e2898c54b23497434003fdf9e8b1108c1ec2 100644
--- a/config/schema/ad.schema.yml
+++ b/config/schema/ad.schema.yml
@@ -1,8 +1,8 @@
-# Schema for the configuration files of the "AD" module.
+# Schema for the configuration files of the ad module.
 
 ad.settings:
   type: config_object
-  label: 'AD Settings'
+  label: 'Advertisement settings'
   mapping:
     trackers:
       type: sequence
@@ -10,3 +10,6 @@ ad.settings:
       sequence:
         type: string
         label: 'Tracker'
+    advertisement_indicator:
+      type: label
+      label: 'Advertisement indicator'
diff --git a/modules/ad_content/ad_content.info.yml b/modules/ad_content/ad_content.info.yml
index 7e7df846591a252cc2d3f9968ad80fea6456ad60..bd76d67c1cb6c7d70b2f19eb0a526d5296b01042 100644
--- a/modules/ad_content/ad_content.info.yml
+++ b/modules/ad_content/ad_content.info.yml
@@ -1,11 +1,13 @@
-name: AD Content
-description: 'AD source allowing to create ADs via the administrative UI.'
+name: 'Advertisement content'
+description: 'Advertisement source allowing to create advertisements via the administrative UI.'
 type: module
 package: Advertisement
 core_version_requirement: ^10.3 || ^11
 dependencies:
   - ad:ad
   - ad:ad_track
+  - drupal:image
+  - drupal:link
   - drupal:options
   - drupal:text
-configure: 'entity.ad_content.collection'
+configure: 'ad.settings'
diff --git a/modules/ad_content/ad_content.links.action.yml b/modules/ad_content/ad_content.links.action.yml
index 1aafc8e98abccd6f4335d9b76f2721d65c3e7731..ce4c0eb0001ba86157157e02d458df5c5bc2e520 100644
--- a/modules/ad_content/ad_content.links.action.yml
+++ b/modules/ad_content/ad_content.links.action.yml
@@ -1,5 +1,11 @@
 ad_content.add_form:
-  route_name: entity.ad_content.add_form
-  title: 'Add content AD'
+  route_name: entity.ad_content.add_page
+  title: 'Add advertisement'
   appears_on:
     - entity.ad_content.collection
+
+ad_content_type.add_form:
+  route_name: entity.ad_content_type.add_form
+  title: 'Add advertisement type'
+  appears_on:
+    - entity.ad_content_type.collection
diff --git a/modules/ad_content/ad_content.links.menu.yml b/modules/ad_content/ad_content.links.menu.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8a73308a6eca09179c7b2dfce89e23b16e664b98
--- /dev/null
+++ b/modules/ad_content/ad_content.links.menu.yml
@@ -0,0 +1,13 @@
+entity.ad_content.collection:
+  route_name: entity.ad_content.collection
+  title: 'Advertisements'
+  description: 'Manage advertisements.'
+  parent: system.admin_content
+  # Place at bottom in the menu by default:
+  weight: 100
+
+entity.ad_content_type.collection:
+  title: 'Advertisement types'
+  parent: system.admin_structure
+  description: 'Manage advertisement types.'
+  route_name: entity.ad_content_type.collection
diff --git a/modules/ad_content/ad_content.links.task.yml b/modules/ad_content/ad_content.links.task.yml
index 563bddea275ca36565f4c4e2e17926849f349b60..b83a73046b42c30bcfa938696f59231fbfa0704c 100644
--- a/modules/ad_content/ad_content.links.task.yml
+++ b/modules/ad_content/ad_content.links.task.yml
@@ -1,10 +1,9 @@
-entity.ad_content.collection:
+ad_content.admin:
+  title: 'Advertisements'
   route_name: entity.ad_content.collection
-  title: 'Content'
-  base_route: entity.ad_content.collection
+  base_route: system.admin_content
 
-ad.admin.settings:
-  route_name: ad.settings
-  title: 'Settings'
-  base_route: entity.ad_content.collection
-  weight: 1
+entity.ad_content_type.edit_form:
+  title: 'Edit'
+  route_name: entity.ad_content_type.edit_form
+  base_route: entity.ad_content_type.edit_form
diff --git a/modules/ad_content/ad_content.module b/modules/ad_content/ad_content.module
index 7f4fc552ab00f9f9dcc41e62dc3da42c7f79991c..d397259c7c300e1071e6df1d935426c22a03b8ac 100644
--- a/modules/ad_content/ad_content.module
+++ b/modules/ad_content/ad_content.module
@@ -2,12 +2,58 @@
 
 /**
  * @file
- * Main file for the "AD Content" module.
+ * Main file for the ad module.
  */
 
+use Drupal\ad_content\Entity\AdContentType;
+use Drupal\Core\Database\Query\AlterableInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+
+/**
+ * Implements hook_query_alter().
+ */
+function ad_content_query_order_random_alter(AlterableInterface $query) {
+  /** @var \Drupal\Core\Database\Query\Select $query */
+  $query->orderRandom();
+}
+
+/**
+ * Implements hook_entity_extra_field_info().
+ */
+function ad_content_entity_extra_field_info() {
+  $extra = [];
+  foreach (AdContentType::loadMultiple() as $bundle) {
+    $extra['ad_content'][$bundle->id()]['display']['ad_content_advertisement_indicator'] = [
+      'label' => t('Advertisement indicator'),
+      'description' => t('Displays the advertisement indicator, configurable in the ad settings.'),
+      'weight' => 99,
+      'visible' => TRUE,
+    ];
+  }
+  return $extra;
+}
+
 /**
- * Implements hook_menu_links_discovered_alter().
+ * Implements hook_ENTITY_TYPE_view().
  */
-function ad_content_menu_links_discovered_alter(&$links) {
-  $links['ad.admin']['route_name'] = 'entity.ad_content.collection';
+function ad_content_ad_content_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
+  if ($display->getComponent('ad_content_advertisement_indicator')) {
+    // This hack is still needed to retrieve the translated config string:
+    $languageManager = \Drupal::languageManager();
+    $originalLanguage = $languageManager->getConfigOverrideLanguage();
+    $languageManager->setConfigOverrideLanguage(new Language(['id' => $languageManager->getCurrentLanguage()->getId()]));
+    $advertisementIndicator = trim(\Drupal::config('ad.settings')->get('advertisement_indicator'));
+    $languageManager->setConfigOverrideLanguage($originalLanguage);
+    if (!empty($advertisementIndicator)) {
+      $build['ad_content_advertisement_indicator'] = [
+        '#type' => 'inline_template',
+        '#template' => '<small class="ad-indicator">{{ indicator_text }}</small>',
+        '#context' => [
+          'indicator_text' => $advertisementIndicator,
+        ],
+      ];
+    }
+  }
 }
diff --git a/modules/ad_content/ad_content.routing.yml b/modules/ad_content/ad_content.routing.yml
index 971a1eb4e680a5ad7249e06f7750bdd9dc7166fa..dab8ed64a2840171156d1815659c675028263442 100644
--- a/modules/ad_content/ad_content.routing.yml
+++ b/modules/ad_content/ad_content.routing.yml
@@ -2,6 +2,13 @@ ad_content.render_ad:
   path: '/ad/content/render'
   defaults:
     _controller: '\Drupal\ad_content\Controller\ImpressionController::renderAds'
-    _title: 'Render AD'
+    _title: 'Render advertisement'
   requirements:
     _permission: 'access content'
+
+entity.ad_content.canonical:
+  path: '/admin/content/ad/{ad_content}'
+  defaults:
+    _entity_view: 'ad_content.admin'
+  requirements:
+    _permission: 'administer ads'
diff --git a/modules/ad_content/ad_content.services.yml b/modules/ad_content/ad_content.services.yml
deleted file mode 100644
index 8b1a26c8aa446b9181280a9a51db392e3d7b3dcb..0000000000000000000000000000000000000000
--- a/modules/ad_content/ad_content.services.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-services:
-  ad_content.route_subscriber:
-    class: Drupal\ad_content\Routing\RouteSubscriber
-    tags:
-      - { name: event_subscriber }
diff --git a/modules/ad_content/config/install/ad_content.ad_content_type.image_ad.yml b/modules/ad_content/config/install/ad_content.ad_content_type.image_ad.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bc11c002fcec18f85c3f46ea2c769ed0304e0e78
--- /dev/null
+++ b/modules/ad_content/config/install/ad_content.ad_content_type.image_ad.yml
@@ -0,0 +1,6 @@
+langcode: en
+status: true
+dependencies: {}
+id: image_ad
+label: 'Image advertisement'
+description: 'An advertisement that contains an image.'
diff --git a/modules/ad_content/config/install/ad_content.ad_content_type.javascript_ad.yml b/modules/ad_content/config/install/ad_content.ad_content_type.javascript_ad.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e1b291622d3807286c015073d71dcbbf3b0b868d
--- /dev/null
+++ b/modules/ad_content/config/install/ad_content.ad_content_type.javascript_ad.yml
@@ -0,0 +1,6 @@
+langcode: en
+status: true
+dependencies: {}
+id: javascript_ad
+label: 'JavaScript advertisement'
+description: 'An advertisement that contains JavaScript.'
diff --git a/modules/ad_content/config/install/ad_content.ad_content_type.text_ad.yml b/modules/ad_content/config/install/ad_content.ad_content_type.text_ad.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5fab9f61cf7560ee2a6b7a1cc6d33e5f6b6b94e3
--- /dev/null
+++ b/modules/ad_content/config/install/ad_content.ad_content_type.text_ad.yml
@@ -0,0 +1,6 @@
+langcode: en
+status: true
+dependencies: {}
+id: text_ad
+label: 'Text advertisement'
+description: 'An advertisement that contains text.'
diff --git a/modules/ad_content/config/install/core.entity_form_display.ad_content.ad_content.default.yml b/modules/ad_content/config/install/core.entity_form_display.ad_content.image_ad.default.yml
similarity index 61%
rename from modules/ad_content/config/install/core.entity_form_display.ad_content.ad_content.default.yml
rename to modules/ad_content/config/install/core.entity_form_display.ad_content.image_ad.default.yml
index c02eb7d69031082f56a1b2b554e02110e557ec0a..9e61761057c6fdd1234f29b0300ad2c99312b62e 100644
--- a/modules/ad_content/config/install/core.entity_form_display.ad_content.ad_content.default.yml
+++ b/modules/ad_content/config/install/core.entity_form_display.ad_content.image_ad.default.yml
@@ -2,45 +2,59 @@ langcode: en
 status: true
 dependencies:
   config:
-    - field.field.ad_content.ad_content.field_image
+    - ad_content.ad_content_type.image_ad
+    - field.field.ad_content.image_ad.field_ad_image
     - image.style.thumbnail
   module:
-    - ad_content
     - image
     - link
-id: ad_content.ad_content.default
+id: ad_content.image_ad.default
 targetEntityType: ad_content
-bundle: ad_content
+bundle: image_ad
 mode: default
 content:
   created:
     type: datetime_timestamp
-    weight: 5
+    weight: 6
+    region: content
+    settings: {}
+    third_party_settings: {}
+  field_ad_image:
+    type: image_image
+    weight: 2
     region: content
-    settings: {  }
-    third_party_settings: {  }
-  field_image:
-    weight: 3
     settings:
       progress_indicator: throbber
       preview_image_style: thumbnail
-    third_party_settings: {  }
-    type: image_image
+    third_party_settings: {}
+  langcode:
+    type: language_select
+    weight: 4
     region: content
+    settings:
+      include_locked: true
+    third_party_settings: {}
   size:
     type: options_select
     weight: 1
     region: content
-    settings: {  }
-    third_party_settings: {  }
+    settings: {}
+    third_party_settings: {}
+  status:
+    type: boolean_checkbox
+    weight: 7
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {}
   target_url:
     type: link_default
-    weight: 2
+    weight: 3
     region: content
     settings:
       placeholder_url: ''
       placeholder_title: ''
-    third_party_settings: {  }
+    third_party_settings: {}
   title:
     type: string_textfield
     weight: 0
@@ -48,21 +62,15 @@ content:
     settings:
       size: 60
       placeholder: ''
-    third_party_settings: {  }
+    third_party_settings: {}
   uid:
     type: entity_reference_autocomplete
-    weight: 4
+    weight: 5
+    region: content
     settings:
       match_operator: CONTAINS
+      match_limit: 10
       size: 60
       placeholder: ''
-      match_limit: 10
-    region: content
-    third_party_settings: {  }
-  status:
-    type: boolean_checkbox
-    weight: 6
-    region: content
-    settings: {  }
-    third_party_settings: {  }
-hidden: {  }
+    third_party_settings: {}
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_form_display.ad_content.javascript_ad.default.yml b/modules/ad_content/config/install/core.entity_form_display.ad_content.javascript_ad.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c4a23dd50e650d2f654236202cab7e4763338085
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_form_display.ad_content.javascript_ad.default.yml
@@ -0,0 +1,75 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.javascript_ad
+    - field.field.ad_content.javascript_ad.field_javascript
+  module:
+    - link
+    - text
+id: ad_content.javascript_ad.default
+targetEntityType: ad_content
+bundle: javascript_ad
+mode: default
+content:
+  created:
+    type: datetime_timestamp
+    weight: 6
+    region: content
+    settings: {}
+    third_party_settings: {}
+  field_javascript:
+    type: text_textarea
+    weight: 2
+    region: content
+    settings:
+      rows: 5
+      placeholder: ''
+    third_party_settings: {}
+  langcode:
+    type: language_select
+    weight: 4
+    region: content
+    settings:
+      include_locked: true
+    third_party_settings: {}
+  size:
+    type: options_select
+    weight: 1
+    region: content
+    settings: {}
+    third_party_settings: {}
+  status:
+    type: boolean_checkbox
+    weight: 7
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {}
+  target_url:
+    type: link_default
+    weight: 3
+    region: content
+    settings:
+      placeholder_url: ''
+      placeholder_title: ''
+    third_party_settings: {}
+  title:
+    type: string_textfield
+    weight: 0
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {}
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    region: content
+    settings:
+      match_operator: CONTAINS
+      match_limit: 10
+      size: 60
+      placeholder: ''
+    third_party_settings: {}
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_form_display.ad_content.text_ad.default.yml b/modules/ad_content/config/install/core.entity_form_display.ad_content.text_ad.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c5bb8bc9a861960d8758cb9cb25e23c8a4e54432
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_form_display.ad_content.text_ad.default.yml
@@ -0,0 +1,77 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.text_ad
+    - field.field.ad_content.text_ad.field_ad_text
+  module:
+    - link
+    - text
+id: ad_content.text_ad.default
+targetEntityType: ad_content
+bundle: text_ad
+mode: default
+content:
+  created:
+    type: datetime_timestamp
+    weight: 6
+    region: content
+    settings: {}
+    third_party_settings: {}
+  field_ad_text:
+    type: text_textarea_with_summary
+    weight: 2
+    region: content
+    settings:
+      rows: 9
+      summary_rows: 3
+      placeholder: ''
+      show_summary: false
+    third_party_settings: {}
+  langcode:
+    type: language_select
+    weight: 4
+    region: content
+    settings:
+      include_locked: true
+    third_party_settings: {}
+  size:
+    type: options_select
+    weight: 1
+    region: content
+    settings: {}
+    third_party_settings: {}
+  status:
+    type: boolean_checkbox
+    weight: 7
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {}
+  target_url:
+    type: link_default
+    weight: 3
+    region: content
+    settings:
+      placeholder_url: ''
+      placeholder_title: ''
+    third_party_settings: {}
+  title:
+    type: string_textfield
+    weight: 0
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {}
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    region: content
+    settings:
+      match_operator: CONTAINS
+      match_limit: 10
+      size: 60
+      placeholder: ''
+    third_party_settings: {}
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.default.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.default.yml
deleted file mode 100644
index c700efe00edfa13f2023bc51c2b14a28a691b51d..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.default.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.field.ad_content.ad_content.field_image
-  module:
-    - ad_content
-    - link
-    - options
-id: ad_content.ad_content.default
-targetEntityType: ad_content
-bundle: ad_content
-mode: default
-content:
-  created:
-    type: timestamp
-    weight: 5
-    region: content
-    label: inline
-    settings:
-      date_format: medium
-      custom_date_format: ''
-      timezone: ''
-    third_party_settings: {  }
-  field_image:
-    weight: 7
-    label: hidden
-    settings: {  }
-    third_party_settings: {  }
-    type: ad_content_image
-    region: content
-  size:
-    type: list_default
-    weight: 1
-    region: content
-    label: inline
-    settings: {  }
-    third_party_settings: {  }
-  status:
-    type: boolean
-    weight: 0
-    region: content
-    label: inline
-    settings:
-      format: yes-no
-      format_custom_true: ''
-      format_custom_false: ''
-    third_party_settings: {  }
-  target_url:
-    type: link
-    weight: 2
-    region: content
-    label: inline
-    settings:
-      trim_length: 80
-      url_only: false
-      url_plain: false
-      rel: ''
-      target: ''
-    third_party_settings: {  }
-  total_click:
-    type: number_integer
-    weight: 4
-    region: content
-    label: inline
-    settings:
-      thousand_separator: ''
-      prefix_suffix: true
-    third_party_settings: {  }
-  total_impression:
-    type: number_integer
-    weight: 3
-    region: content
-    label: inline
-    settings:
-      thousand_separator: ''
-      prefix_suffix: true
-    third_party_settings: {  }
-  uid:
-    type: entity_reference_label
-    weight: 6
-    region: content
-    label: inline
-    settings:
-      link: true
-    third_party_settings: {  }
-hidden:
-  title: true
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.full.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.full.yml
deleted file mode 100644
index d4d071472792601b921b4bed93820d50224ef753..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.full.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-langcode: en
-status: false
-dependencies:
-  config:
-    - core.entity_view_mode.ad_content.full
-    - field.field.ad_content.ad_content.field_image
-  module:
-    - ad_content
-    - ad_track
-id: ad_content.ad_content.full
-targetEntityType: ad_content
-bundle: ad_content
-mode: full
-content:
-  field_image:
-    weight: 2
-    label: hidden
-    settings: {  }
-    third_party_settings: {  }
-    type: ad_content_image_click_track
-    region: content
-  total_click:
-    type: number_integer
-    weight: 10
-    region: content
-    label: above
-    settings:
-      thousand_separator: ''
-      prefix_suffix: true
-    third_party_settings: {  }
-  total_impression:
-    type: number_integer
-    weight: 10
-    region: content
-    label: above
-    settings:
-      thousand_separator: ''
-      prefix_suffix: true
-    third_party_settings: {  }
-hidden:
-  created: true
-  size: true
-  status: true
-  target_url: true
-  title: true
-  uid: true
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.admin.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.admin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5d72257be8004d478dcb86a8868a248f70cb201a
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.admin.yml
@@ -0,0 +1,119 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.image_ad
+    - core.entity_view_mode.ad_content.admin
+    - field.field.ad_content.image_ad.field_ad_image
+  module:
+    - image
+    - link
+    - options
+id: ad_content.image_ad.admin
+targetEntityType: ad_content
+bundle: image_ad
+mode: admin
+content:
+  created:
+    type: timestamp
+    label: above
+    settings:
+      date_format: medium
+      custom_date_format: ''
+      timezone: ''
+      tooltip:
+        date_format: long
+        custom_date_format: ''
+      time_diff:
+        enabled: false
+        future_format: '@interval hence'
+        past_format: '@interval ago'
+        granularity: 2
+        refresh: 60
+    third_party_settings: {}
+    weight: 4
+    region: content
+  field_ad_image:
+    type: image
+    label: above
+    settings:
+      image_link: ''
+      image_style: ''
+      image_loading:
+        attribute: lazy
+    third_party_settings: {}
+    weight: 9
+    region: content
+  langcode:
+    type: language
+    label: above
+    settings:
+      link_to_entity: false
+      native_language: false
+    third_party_settings: {}
+    weight: 1
+    region: content
+  size:
+    type: list_default
+    label: above
+    settings: {}
+    third_party_settings: {}
+    weight: 2
+    region: content
+  status:
+    type: boolean
+    label: above
+    settings:
+      format: default
+      format_custom_false: ''
+      format_custom_true: ''
+    third_party_settings: {}
+    weight: 5
+    region: content
+  target_url:
+    type: link
+    label: above
+    settings:
+      trim_length: 80
+      url_only: false
+      url_plain: false
+      rel: ''
+      target: ''
+    third_party_settings: {}
+    weight: 8
+    region: content
+  title:
+    type: string
+    label: hidden
+    settings:
+      link_to_entity: false
+    third_party_settings: {}
+    weight: 0
+    region: content
+  total_click:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 7
+    region: content
+  total_impression:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 6
+    region: content
+  uid:
+    type: entity_reference_label
+    label: above
+    settings:
+      link: true
+    third_party_settings: {}
+    weight: 3
+    region: content
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.impression.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.default.yml
similarity index 51%
rename from modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.impression.yml
rename to modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.default.yml
index 9e153a0b6286155038c0f029587e82d967a78b44..3fe9319d41f3e176539801274ab3e195ce54bc37 100644
--- a/modules/ad_content/config/install/core.entity_view_display.ad_content.ad_content.impression.yml
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.image_ad.default.yml
@@ -2,25 +2,25 @@ langcode: en
 status: true
 dependencies:
   config:
-    - core.entity_view_mode.ad_content.impression
-    - field.field.ad_content.ad_content.field_image
+    - ad_content.ad_content_type.image_ad
+    - field.field.ad_content.image_ad.field_ad_image
   module:
     - ad_content
-    - ad_track
-id: ad_content.ad_content.impression
+id: ad_content.image_ad.default
 targetEntityType: ad_content
-bundle: ad_content
-mode: impression
+bundle: image_ad
+mode: default
 content:
-  field_image:
-    weight: 0
+  field_ad_image:
+    type: ad_content_image
     label: hidden
-    settings: {  }
-    third_party_settings: {  }
-    type: ad_content_image_click_track
+    settings: {}
+    third_party_settings: {}
+    weight: 0
     region: content
 hidden:
   created: true
+  langcode: true
   size: true
   status: true
   target_url: true
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.admin.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.admin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..89fea2cff1ea7bfc170a72c00db14aa3f5bce94b
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.admin.yml
@@ -0,0 +1,115 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.javascript_ad
+    - core.entity_view_mode.ad_content.admin
+    - field.field.ad_content.javascript_ad.field_javascript
+  module:
+    - link
+    - options
+    - text
+id: ad_content.javascript_ad.admin
+targetEntityType: ad_content
+bundle: javascript_ad
+mode: admin
+content:
+  created:
+    type: timestamp
+    label: above
+    settings:
+      date_format: medium
+      custom_date_format: ''
+      timezone: ''
+      tooltip:
+        date_format: long
+        custom_date_format: ''
+      time_diff:
+        enabled: false
+        future_format: '@interval hence'
+        past_format: '@interval ago'
+        granularity: 2
+        refresh: 60
+    third_party_settings: {}
+    weight: 4
+    region: content
+  field_javascript:
+    type: text_default
+    label: above
+    settings: {}
+    third_party_settings: {}
+    weight: 9
+    region: content
+  langcode:
+    type: language
+    label: above
+    settings:
+      link_to_entity: false
+      native_language: false
+    third_party_settings: {}
+    weight: 1
+    region: content
+  size:
+    type: list_default
+    label: above
+    settings: {}
+    third_party_settings: {}
+    weight: 2
+    region: content
+  status:
+    type: boolean
+    label: above
+    settings:
+      format: default
+      format_custom_false: ''
+      format_custom_true: ''
+    third_party_settings: {}
+    weight: 5
+    region: content
+  target_url:
+    type: link
+    label: above
+    settings:
+      trim_length: 80
+      url_only: false
+      url_plain: false
+      rel: ''
+      target: ''
+    third_party_settings: {}
+    weight: 8
+    region: content
+  title:
+    type: string
+    label: hidden
+    settings:
+      link_to_entity: false
+    third_party_settings: {}
+    weight: 0
+    region: content
+  total_click:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 7
+    region: content
+  total_impression:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 6
+    region: content
+  uid:
+    type: entity_reference_label
+    label: above
+    settings:
+      link: true
+    third_party_settings: {}
+    weight: 3
+    region: content
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.default.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..afeb2f115688b2482d0a8b4459cdf63e700be257
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.javascript_ad.default.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.javascript_ad
+    - field.field.ad_content.javascript_ad.field_javascript
+id: ad_content.javascript_ad.default
+targetEntityType: ad_content
+bundle: javascript_ad
+mode: default
+content: {}
+hidden:
+  created: true
+  field_javascript: true
+  langcode: true
+  size: true
+  status: true
+  target_url: true
+  title: true
+  total_click: true
+  total_impression: true
+  uid: true
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.admin.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.admin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bbf9c5da32ab405366b48200d521fe36e559c7be
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.admin.yml
@@ -0,0 +1,115 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.text_ad
+    - core.entity_view_mode.ad_content.admin
+    - field.field.ad_content.text_ad.field_ad_text
+  module:
+    - link
+    - options
+    - text
+id: ad_content.text_ad.admin
+targetEntityType: ad_content
+bundle: text_ad
+mode: admin
+content:
+  created:
+    type: timestamp
+    label: above
+    settings:
+      date_format: medium
+      custom_date_format: ''
+      timezone: ''
+      tooltip:
+        date_format: long
+        custom_date_format: ''
+      time_diff:
+        enabled: false
+        future_format: '@interval hence'
+        past_format: '@interval ago'
+        granularity: 2
+        refresh: 60
+    third_party_settings: {}
+    weight: 4
+    region: content
+  field_ad_text:
+    type: text_default
+    label: above
+    settings: {}
+    third_party_settings: {}
+    weight: 9
+    region: content
+  langcode:
+    type: language
+    label: above
+    settings:
+      link_to_entity: false
+      native_language: false
+    third_party_settings: {}
+    weight: 1
+    region: content
+  size:
+    type: list_default
+    label: above
+    settings: {}
+    third_party_settings: {}
+    weight: 2
+    region: content
+  status:
+    type: boolean
+    label: above
+    settings:
+      format: default
+      format_custom_false: ''
+      format_custom_true: ''
+    third_party_settings: {}
+    weight: 5
+    region: content
+  target_url:
+    type: link
+    label: above
+    settings:
+      trim_length: 80
+      url_only: false
+      url_plain: false
+      rel: ''
+      target: ''
+    third_party_settings: {}
+    weight: 8
+    region: content
+  title:
+    type: string
+    label: hidden
+    settings:
+      link_to_entity: false
+    third_party_settings: {}
+    weight: 0
+    region: content
+  total_click:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 7
+    region: content
+  total_impression:
+    type: number_integer
+    label: above
+    settings:
+      thousand_separator: ''
+      prefix_suffix: true
+    third_party_settings: {}
+    weight: 6
+    region: content
+  uid:
+    type: entity_reference_label
+    label: above
+    settings:
+      link: true
+    third_party_settings: {}
+    weight: 3
+    region: content
+hidden: {}
diff --git a/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.default.yml b/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a0b8998c29ebefbcc7a00d2e1d3e3c7f2e819414
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_display.ad_content.text_ad.default.yml
@@ -0,0 +1,30 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.text_ad
+    - field.field.ad_content.text_ad.field_ad_text
+  module:
+    - text
+id: ad_content.text_ad.default
+targetEntityType: ad_content
+bundle: text_ad
+mode: default
+content:
+  field_ad_text:
+    type: text_default
+    label: hidden
+    settings: {}
+    third_party_settings: {}
+    weight: 0
+    region: content
+hidden:
+  created: true
+  langcode: true
+  size: true
+  status: true
+  target_url: true
+  title: true
+  total_click: true
+  total_impression: true
+  uid: true
diff --git a/modules/ad_content/config/install/core.entity_view_mode.ad_content.admin.yml b/modules/ad_content/config/install/core.entity_view_mode.ad_content.admin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f6658b508e8f54f6d30b261bf3de6efc88d3147e
--- /dev/null
+++ b/modules/ad_content/config/install/core.entity_view_mode.ad_content.admin.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - ad_content
+id: ad_content.admin
+label: Admin
+description: 'The way that this advertisement is displayed on admin pages (showing additional info about the advertisement).'
+targetEntityType: ad_content
+cache: true
diff --git a/modules/ad_content/config/install/core.entity_view_mode.ad_content.full.yml b/modules/ad_content/config/install/core.entity_view_mode.ad_content.full.yml
deleted file mode 100644
index 30554617748c2eda57012f166219867a7a2130f4..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/install/core.entity_view_mode.ad_content.full.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - ad_content
-id: ad_content.full
-label: Preview
-targetEntityType: ad_content
-cache: true
diff --git a/modules/ad_content/config/install/core.entity_view_mode.ad_content.impression.yml b/modules/ad_content/config/install/core.entity_view_mode.ad_content.impression.yml
deleted file mode 100644
index ac111bfe64ea4ca181270aa2aa79616ee0c97662..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/install/core.entity_view_mode.ad_content.impression.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - ad_content
-id: ad_content.impression
-label: Impression
-targetEntityType: ad_content
-cache: true
diff --git a/modules/ad_content/config/install/field.field.ad_content.ad_content.field_image.yml b/modules/ad_content/config/install/field.field.ad_content.ad_content.field_image.yml
deleted file mode 100644
index e3f1000d4e94e3c629726cf192c3a1f552e482ec..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/install/field.field.ad_content.ad_content.field_image.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.ad_content.field_image
-  module:
-    - ad_content
-    - image
-id: ad_content.ad_content.field_image
-field_name: field_image
-entity_type: ad_content
-bundle: ad_content
-label: Image
-description: ''
-required: true
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings:
-  file_directory: 'ad/[date:custom:Y]-[date:custom:m]'
-  file_extensions: 'png gif jpg jpeg'
-  max_filesize: ''
-  max_resolution: ''
-  min_resolution: ''
-  alt_field: true
-  alt_field_required: false
-  title_field: false
-  title_field_required: false
-  default_image:
-    uuid: ''
-    alt: ''
-    title: ''
-    width: null
-    height: null
-  handler: 'default:file'
-  handler_settings: {  }
-field_type: image
diff --git a/modules/ad_content/config/install/field.field.ad_content.image_ad.field_ad_image.yml b/modules/ad_content/config/install/field.field.ad_content.image_ad.field_ad_image.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d1a69522cfa35b4324bd6050317119208dc8d072
--- /dev/null
+++ b/modules/ad_content/config/install/field.field.ad_content.image_ad.field_ad_image.yml
@@ -0,0 +1,37 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.image_ad
+    - field.storage.ad_content.field_ad_image
+  module:
+    - image
+id: ad_content.image_ad.field_ad_image
+field_name: field_ad_image
+entity_type: ad_content
+bundle: image_ad
+label: 'Advertisement image'
+description: 'The image that the advertisement should display.'
+required: true
+translatable: false
+default_value: {}
+default_value_callback: ''
+settings:
+  handler: 'default:file'
+  handler_settings: {}
+  file_directory: '[date:custom:Y]-[date:custom:m]'
+  file_extensions: 'png gif jpg jpeg webp'
+  max_filesize: ''
+  max_resolution: ''
+  min_resolution: ''
+  alt_field: true
+  alt_field_required: true
+  title_field: true
+  title_field_required: false
+  default_image:
+    uuid: ''
+    alt: ''
+    title: ''
+    width: null
+    height: null
+field_type: image
diff --git a/modules/ad_content/config/install/field.field.ad_content.javascript_ad.field_javascript.yml b/modules/ad_content/config/install/field.field.ad_content.javascript_ad.field_javascript.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5097be2360ea73b03704650b3ef5d6a5102b113a
--- /dev/null
+++ b/modules/ad_content/config/install/field.field.ad_content.javascript_ad.field_javascript.yml
@@ -0,0 +1,21 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.javascript_ad
+    - field.storage.ad_content.field_javascript
+  module:
+    - text
+id: ad_content.javascript_ad.field_javascript
+field_name: field_javascript
+entity_type: ad_content
+bundle: javascript_ad
+label: 'JavaScript code'
+description: 'The JavaScript code that the advertisement should run in order to display.'
+required: true
+translatable: false
+default_value: {}
+default_value_callback: ''
+settings:
+  allowed_formats: {}
+field_type: text_long
diff --git a/modules/ad_content/config/install/field.field.ad_content.text_ad.field_ad_text.yml b/modules/ad_content/config/install/field.field.ad_content.text_ad.field_ad_text.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b96a63d3387d2278fa8a2329db5f1cec7b7c4a83
--- /dev/null
+++ b/modules/ad_content/config/install/field.field.ad_content.text_ad.field_ad_text.yml
@@ -0,0 +1,23 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - ad_content.ad_content_type.text_ad
+    - field.storage.ad_content.field_ad_text
+  module:
+    - text
+id: ad_content.text_ad.field_ad_text
+field_name: field_ad_text
+entity_type: ad_content
+bundle: text_ad
+label: 'Advertisement text'
+description: 'The text that the advertisement should display.'
+required: true
+translatable: false
+default_value: {}
+default_value_callback: ''
+settings:
+  display_summary: true
+  required_summary: false
+  allowed_formats: {}
+field_type: text_with_summary
diff --git a/modules/ad_content/config/install/field.storage.ad_content.field_image.yml b/modules/ad_content/config/install/field.storage.ad_content.field_ad_image.yml
similarity index 81%
rename from modules/ad_content/config/install/field.storage.ad_content.field_image.yml
rename to modules/ad_content/config/install/field.storage.ad_content.field_ad_image.yml
index 34735373912f5ab0cf339c35bd21b38128136e8f..062d74f278e2fd9a6a05ffb9da0da35d5e97b898 100644
--- a/modules/ad_content/config/install/field.storage.ad_content.field_image.yml
+++ b/modules/ad_content/config/install/field.storage.ad_content.field_ad_image.yml
@@ -5,11 +5,14 @@ dependencies:
     - ad_content
     - file
     - image
-id: ad_content.field_image
-field_name: field_image
+id: ad_content.field_ad_image
+field_name: field_ad_image
 entity_type: ad_content
 type: image
 settings:
+  target_type: file
+  display_field: false
+  display_default: true
   uri_scheme: public
   default_image:
     uuid: ''
@@ -17,13 +20,10 @@ settings:
     title: ''
     width: null
     height: null
-  target_type: file
-  display_field: false
-  display_default: false
 module: image
 locked: false
 cardinality: 1
 translatable: true
-indexes: {  }
+indexes: {}
 persist_with_no_fields: false
 custom_storage: false
diff --git a/modules/ad_content/config/install/field.storage.ad_content.field_ad_text.yml b/modules/ad_content/config/install/field.storage.ad_content.field_ad_text.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3ba0fe607be561c531d160cf0b1ca9ad2d964b57
--- /dev/null
+++ b/modules/ad_content/config/install/field.storage.ad_content.field_ad_text.yml
@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - ad_content
+    - text
+id: ad_content.field_ad_text
+field_name: field_ad_text
+entity_type: ad_content
+type: text_with_summary
+settings: {}
+module: text
+locked: false
+cardinality: 1
+translatable: true
+indexes: {}
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/ad_content/config/install/field.storage.ad_content.field_javascript.yml b/modules/ad_content/config/install/field.storage.ad_content.field_javascript.yml
new file mode 100644
index 0000000000000000000000000000000000000000..63da3f8a6dada911e6ecab8ef15a140b64305655
--- /dev/null
+++ b/modules/ad_content/config/install/field.storage.ad_content.field_javascript.yml
@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - ad_content
+    - text
+id: ad_content.field_javascript
+field_name: field_javascript
+entity_type: ad_content
+type: text_long
+settings: {}
+module: text
+locked: false
+cardinality: 1
+translatable: true
+indexes: {}
+persist_with_no_fields: false
+custom_storage: false
diff --git a/modules/ad_content/config/optional/image.style.mini_thumbnail.yml b/modules/ad_content/config/optional/image.style.mini_thumbnail.yml
index 4b9334c38f72d30b7d39aa05f37b6317cc298fb0..035d9476f744dcb24ae032e6a16457348d66cdcb 100644
--- a/modules/ad_content/config/optional/image.style.mini_thumbnail.yml
+++ b/modules/ad_content/config/optional/image.style.mini_thumbnail.yml
@@ -1,6 +1,6 @@
 langcode: en
 status: true
-dependencies: {  }
+dependencies: {}
 name: mini_thumbnail
 label: 'Mini thumbnail'
 effects:
diff --git a/modules/ad_content/config/optional/views.view.ad_administration.yml b/modules/ad_content/config/optional/views.view.ad_administration.yml
deleted file mode 100644
index c67734c68af25055ba1c2c8fe1a698c4c74c02e6..0000000000000000000000000000000000000000
--- a/modules/ad_content/config/optional/views.view.ad_administration.yml
+++ /dev/null
@@ -1,1172 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.ad_content.field_image
-    - image.style.mini_thumbnail
-  module:
-    - ad_content
-    - ad_track
-    - image
-    - link
-    - options
-    - user
-id: ad_administration
-label: 'AD Administration'
-module: views
-description: ''
-tag: ''
-base_table: ad_content
-base_field: id
-display:
-  default:
-    id: default
-    display_title: Master
-    display_plugin: default
-    position: 0
-    display_options:
-      title: Advertisement
-      fields:
-        id:
-          id: id
-          table: ad_content
-          field: id
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: id
-          plugin_id: field
-          label: ID
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: number_integer
-          settings:
-            thousand_separator: ''
-            prefix_suffix: true
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        field_image:
-          id: field_image
-          table: ad_content__field_image
-          field: field_image
-          relationship: none
-          group_type: group
-          admin_label: ''
-          plugin_id: field
-          label: Preview
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: false
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: target_id
-          type: image
-          settings:
-            image_link: content
-            image_style: mini_thumbnail
-            image_loading:
-              attribute: lazy
-          group_column: ''
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        title:
-          id: title
-          table: ad_content
-          field: title
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: title
-          plugin_id: field
-          label: Title
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: string
-          settings:
-            link_to_entity: true
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        status:
-          id: status
-          table: ad_content
-          field: status
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: status
-          plugin_id: field
-          label: Status
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: boolean
-          settings:
-            format: custom
-            format_custom_false: Unpublished
-            format_custom_true: Published
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        size:
-          id: size
-          table: ad_content
-          field: size
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: size
-          plugin_id: field
-          label: Size
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: list_default
-          settings: {  }
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        target_url__uri:
-          id: target_url__uri
-          table: ad_content
-          field: target_url__uri
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: target_url
-          plugin_id: field
-          label: 'Target URL'
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: uri
-          type: link
-          settings:
-            trim_length: 80
-            url_only: false
-            url_plain: false
-            rel: '0'
-            target: _blank
-          group_column: ''
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        impression:
-          id: impression
-          table: ad_track_total
-          field: impression
-          relationship: uuid
-          group_type: group
-          admin_label: ''
-          plugin_id: numeric
-          label: Impressions
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: true
-            path: '/admin/ad/statistics/{{ id }}'
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          set_precision: false
-          precision: 0
-          decimal: .
-          separator: ','
-          format_plural: false
-          format_plural_string: !!binary MQNAY291bnQ=
-          prefix: ''
-          suffix: ''
-        click:
-          id: click
-          table: ad_track_total
-          field: click
-          relationship: uuid
-          group_type: group
-          admin_label: ''
-          plugin_id: numeric
-          label: Clicks
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: true
-            path: '/admin/ad/statistics/{{ id }}'
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          set_precision: false
-          precision: 0
-          decimal: .
-          separator: ','
-          format_plural: false
-          format_plural_string: !!binary MQNAY291bnQ=
-          prefix: ''
-          suffix: ''
-        click_through:
-          id: click_through
-          table: ad_track_total
-          field: click_through
-          relationship: uuid
-          group_type: group
-          admin_label: ''
-          plugin_id: ad_track_click_through
-          label: 'Click through'
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: true
-            path: '/admin/ad/statistics/{{ id }}'
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-        created:
-          id: created
-          table: ad_content
-          field: created
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: created
-          plugin_id: field
-          label: Created
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: timestamp
-          settings:
-            date_format: short
-            custom_date_format: ''
-            timezone: ''
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        changed:
-          id: changed
-          table: ad_content
-          field: changed
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: changed
-          plugin_id: field
-          label: Updated
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          click_sort_column: value
-          type: timestamp
-          settings:
-            date_format: short
-            custom_date_format: ''
-            timezone: ''
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-        operations:
-          id: operations
-          table: ad_content
-          field: operations
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          plugin_id: entity_operations
-          label: Operations
-          exclude: false
-          alter:
-            alter_text: false
-            text: ''
-            make_link: false
-            path: ''
-            absolute: false
-            external: false
-            replace_spaces: false
-            path_case: none
-            trim_whitespace: false
-            alt: ''
-            rel: ''
-            link_class: ''
-            prefix: ''
-            suffix: ''
-            target: ''
-            nl2br: false
-            max_length: 0
-            word_boundary: true
-            ellipsis: true
-            more_link: false
-            more_link_text: ''
-            more_link_path: ''
-            strip_tags: false
-            trim: false
-            preserve_tags: ''
-            html: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_empty: false
-          empty_zero: false
-          hide_alter_empty: true
-          destination: true
-      pager:
-        type: full
-        options:
-          offset: 0
-          items_per_page: 15
-          total_pages: null
-          id: 0
-          tags:
-            next: ››
-            previous: ‹‹
-            first: '« First'
-            last: 'Last »'
-          expose:
-            items_per_page: false
-            items_per_page_label: 'Items per page'
-            items_per_page_options: '5, 10, 25, 50'
-            items_per_page_options_all: false
-            items_per_page_options_all_label: '- All -'
-            offset: false
-            offset_label: Offset
-          quantity: 9
-      exposed_form:
-        type: basic
-        options:
-          submit_button: Apply
-          reset_button: false
-          reset_button_label: Reset
-          exposed_sorts_label: 'Sort by'
-          expose_sort_order: true
-          sort_asc_label: Asc
-          sort_desc_label: Desc
-      access:
-        type: perm
-        options:
-          perm: 'administer ads'
-      cache:
-        type: none
-        options: {  }
-      empty: {  }
-      sorts: {  }
-      arguments: {  }
-      filters:
-        status:
-          id: status
-          table: ad_content
-          field: status
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: status
-          plugin_id: boolean
-          operator: '='
-          value: '1'
-          group: 1
-          exposed: true
-          expose:
-            operator_id: ''
-            label: Published
-            description: ''
-            use_operator: false
-            operator: status_op
-            operator_limit_selection: false
-            operator_list: {  }
-            identifier: status
-            required: true
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-              anonymous: '0'
-              verified: '0'
-              administrator: '0'
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-        size:
-          id: size
-          table: ad_content
-          field: size
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: size
-          plugin_id: string
-          operator: contains
-          value: ''
-          group: 1
-          exposed: true
-          expose:
-            operator_id: size_op
-            label: Size
-            description: ''
-            use_operator: false
-            operator: size_op
-            operator_limit_selection: false
-            operator_list: {  }
-            identifier: size
-            required: false
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-              anonymous: '0'
-              verified: '0'
-              administrator: '0'
-            placeholder: ''
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-        title:
-          id: title
-          table: ad_content
-          field: title
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: title
-          plugin_id: string
-          operator: contains
-          value: ''
-          group: 1
-          exposed: true
-          expose:
-            operator_id: title_op
-            label: Title
-            description: ''
-            use_operator: false
-            operator: title_op
-            operator_limit_selection: false
-            operator_list: {  }
-            identifier: title
-            required: false
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-              anonymous: '0'
-              verified: '0'
-              administrator: '0'
-            placeholder: ''
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-        target_url__uri:
-          id: target_url__uri
-          table: ad_content
-          field: target_url__uri
-          relationship: none
-          group_type: group
-          admin_label: ''
-          entity_type: ad_content
-          entity_field: target_url
-          plugin_id: string
-          operator: contains
-          value: ''
-          group: 1
-          exposed: true
-          expose:
-            operator_id: target_url__uri_op
-            label: 'Target URL'
-            description: ''
-            use_operator: false
-            operator: target_url__uri_op
-            operator_limit_selection: false
-            operator_list: {  }
-            identifier: target_url__uri
-            required: false
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-              anonymous: '0'
-              verified: '0'
-              administrator: '0'
-            placeholder: ''
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-      filter_groups:
-        operator: AND
-        groups:
-          1: AND
-      style:
-        type: table
-        options:
-          grouping: {  }
-          row_class: ''
-          default_row_class: true
-          columns:
-            id: id
-            title: title
-            size: size
-            field_image: field_image
-            target_url__uri: target_url__uri
-            impression: impression
-            click: click
-            click_through: click_through
-            status: status
-            created: created
-            changed: changed
-            operations: operations
-          default: created
-          info:
-            id:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            title:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            size:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            field_image:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            target_url__uri:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            impression:
-              sortable: true
-              default_sort_order: asc
-              align: views-align-center
-              separator: ''
-              empty_column: false
-              responsive: ''
-            click:
-              sortable: true
-              default_sort_order: asc
-              align: views-align-center
-              separator: ''
-              empty_column: false
-              responsive: ''
-            click_through:
-              sortable: false
-              default_sort_order: asc
-              align: views-align-center
-              separator: ''
-              empty_column: false
-              responsive: ''
-            status:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            created:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            changed:
-              sortable: true
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            operations:
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-          override: true
-          sticky: false
-          summary: ''
-          empty_table: false
-          caption: ''
-          description: ''
-      row:
-        type: fields
-      query:
-        type: views_query
-        options:
-          query_comment: ''
-          disable_sql_rewrite: false
-          distinct: false
-          replica: false
-          query_tags: {  }
-      relationships:
-        uuid:
-          id: uuid
-          table: ad_content
-          field: uuid
-          relationship: none
-          group_type: group
-          admin_label: Totals
-          entity_type: ad_content
-          entity_field: uuid
-          plugin_id: standard
-          required: false
-      use_ajax: true
-      header: {  }
-      footer: {  }
-      display_extenders: {  }
-    cache_metadata:
-      max-age: -1
-      contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - url
-        - url.query_args
-        - user.permissions
-      tags:
-        - 'config:field.storage.ad_content.field_image'
-  page:
-    id: page
-    display_title: Page
-    display_plugin: page
-    position: 1
-    display_options:
-      display_extenders: {  }
-      path: admin/ad
-      menu:
-        type: none
-        title: List
-        description: ''
-        weight: 0
-        expanded: false
-        menu_name: main
-        parent: ''
-        context: '0'
-      tab_options:
-        type: normal
-        title: Content
-        description: ''
-        weight: 0
-    cache_metadata:
-      max-age: -1
-      contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - url
-        - url.query_args
-        - user.permissions
-      tags:
-        - 'config:field.storage.ad_content.field_image'
diff --git a/modules/ad_content/config/optional/views.view.ad_statistics.yml b/modules/ad_content/config/optional/views.view.ad_statistics.yml
index 496d3b1b1f89937c105ab6676632483b57bc59d7..5f438f411863b58344a9d4364382fd40ba730d1c 100644
--- a/modules/ad_content/config/optional/views.view.ad_statistics.yml
+++ b/modules/ad_content/config/optional/views.view.ad_statistics.yml
@@ -5,7 +5,7 @@ dependencies:
     - ad_content
     - ad_track
 id: ad_statistics
-label: 'AD Statistics'
+label: 'Advertisement Statistics'
 module: views
 description: ''
 tag: ''
@@ -20,10 +20,10 @@ display:
     display_options:
       access:
         type: none
-        options: {  }
+        options: {}
       cache:
         type: tag
-        options: {  }
+        options: {}
       query:
         type: views_query
         options:
@@ -31,7 +31,7 @@ display:
           distinct: false
           replica: false
           query_comment: ''
-          query_tags: {  }
+          query_tags: {}
       exposed_form:
         type: basic
         options:
@@ -66,7 +66,7 @@ display:
       style:
         type: table
         options:
-          grouping: {  }
+          grouping: {}
           row_class: ''
           default_row_class: true
           override: true
@@ -215,7 +215,7 @@ display:
             thousand_separator: ''
             prefix_suffix: true
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -280,7 +280,7 @@ display:
           settings:
             link_to_entity: false
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -345,7 +345,7 @@ display:
           settings:
             link_to_entity: false
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -411,7 +411,7 @@ display:
             thousand_separator: ''
             prefix_suffix: true
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -430,13 +430,13 @@ display:
           relationship: ad_id
           group_type: group
           admin_label: ''
-          label: AD
+          label: Advertisement
           exclude: false
           alter:
             alter_text: false
             text: ''
             make_link: true
-            path: '/admin/ad/statistics/{{ id_1 }}'
+            path: '/admin/content/ad/statistics/{{ id_1 }}'
             absolute: false
             external: false
             replace_spaces: false
@@ -476,7 +476,7 @@ display:
           settings:
             link_to_entity: false
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -541,7 +541,7 @@ display:
           settings:
             link: true
           group_column: target_id
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -603,9 +603,9 @@ display:
           hide_alter_empty: true
           click_sort_column: value
           type: basic_string
-          settings: {  }
+          settings: {}
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -670,7 +670,7 @@ display:
           settings:
             link_to_entity: false
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -732,9 +732,9 @@ display:
           hide_alter_empty: true
           click_sort_column: value
           type: basic_string
-          settings: {  }
+          settings: {}
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -796,9 +796,9 @@ display:
           hide_alter_empty: true
           click_sort_column: value
           type: basic_string
-          settings: {  }
+          settings: {}
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -865,7 +865,7 @@ display:
             custom_date_format: ''
             timezone: ''
           group_column: value
-          group_columns: {  }
+          group_columns: {}
           group_rows: true
           delta_limit: 0
           delta_offset: 0
@@ -896,7 +896,7 @@ display:
             use_operator: false
             operator: type_op
             operator_limit_selection: false
-            operator_list: {  }
+            operator_list: {}
             identifier: type
             required: false
             remember: false
@@ -917,8 +917,8 @@ display:
             multiple: false
             remember: false
             default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
+            default_group_multiple: {}
+            group_items: {}
           entity_type: ad_track_event
           entity_field: type
           plugin_id: string
@@ -940,7 +940,7 @@ display:
             use_operator: false
             operator: user_agent_op
             operator_limit_selection: false
-            operator_list: {  }
+            operator_list: {}
             identifier: user_agent
             required: false
             remember: false
@@ -961,8 +961,8 @@ display:
             multiple: false
             remember: false
             default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
+            default_group_multiple: {}
+            group_items: {}
           entity_type: ad_track_event
           entity_field: user_agent
           plugin_id: string
@@ -984,7 +984,7 @@ display:
             use_operator: false
             operator: ip_address_op
             operator_limit_selection: false
-            operator_list: {  }
+            operator_list: {}
             identifier: ip_address
             required: false
             remember: false
@@ -1005,8 +1005,8 @@ display:
             multiple: false
             remember: false
             default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
+            default_group_multiple: {}
+            group_items: {}
           entity_type: ad_track_event
           entity_field: ip_address
           plugin_id: string
@@ -1028,7 +1028,7 @@ display:
             use_operator: false
             operator: page_title_op
             operator_limit_selection: false
-            operator_list: {  }
+            operator_list: {}
             identifier: page_title
             required: false
             remember: false
@@ -1049,8 +1049,8 @@ display:
             multiple: false
             remember: false
             default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
+            default_group_multiple: {}
+            group_items: {}
           entity_type: ad_track_event
           entity_field: page_title
           plugin_id: string
@@ -1072,7 +1072,7 @@ display:
             use_operator: false
             operator: url_op
             operator_limit_selection: false
-            operator_list: {  }
+            operator_list: {}
             identifier: url
             required: false
             remember: false
@@ -1093,16 +1093,16 @@ display:
             multiple: false
             remember: false
             default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
+            default_group_multiple: {}
+            group_items: {}
           entity_type: ad_track_event
           entity_field: url
           plugin_id: string
-      sorts: {  }
-      title: 'Advertisement Statistics'
-      header: {  }
-      footer: {  }
-      empty: {  }
+      sorts: {}
+      title: 'Advertisement statistics'
+      header: {}
+      footer: {}
+      empty: {}
       relationships:
         ad_id:
           id: ad_id
@@ -1110,7 +1110,7 @@ display:
           field: ad_id
           relationship: none
           group_type: group
-          admin_label: 'Content AD'
+          admin_label: 'Advertisement'
           required: false
           entity_type: ad_track_event
           plugin_id: standard
@@ -1149,13 +1149,13 @@ display:
             access: true
             operation: update
             multiple: 0
-            bundles: {  }
+            bundles: {}
           break_phrase: false
           not: false
           entity_type: ad_content
           entity_field: id
           plugin_id: numeric
-      display_extenders: {  }
+      display_extenders: {}
       use_ajax: true
     cache_metadata:
       max-age: -1
@@ -1164,15 +1164,15 @@ display:
         - 'languages:language_interface'
         - url
         - url.query_args
-      tags: {  }
+      tags: {}
   page:
     display_plugin: page
     id: page
     display_title: Page
     position: 1
     display_options:
-      display_extenders: {  }
-      path: admin/ad/statistics
+      display_extenders: {}
+      path: admin/content/ad/statistics
     cache_metadata:
       max-age: -1
       contexts:
@@ -1180,4 +1180,4 @@ display:
         - 'languages:language_interface'
         - url
         - url.query_args
-      tags: {  }
+      tags: {}
diff --git a/modules/ad_content/config/schema/ad_content.schema.yml b/modules/ad_content/config/schema/ad_content.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..67f0e52efe0e8301035190a309de71a7cd6d673c
--- /dev/null
+++ b/modules/ad_content/config/schema/ad_content.schema.yml
@@ -0,0 +1,15 @@
+# Schema for the configuration files of the ad_content module.
+
+ad_content.ad_content_type.*:
+  type: config_entity
+  label: 'Advertisement type'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Name'
+    description:
+      type: text
+      label: 'Description'
diff --git a/modules/ad_content/js/ad-content.render.js b/modules/ad_content/js/ad-content.render.js
index 3d7903a0724e417224be59de72cd24685ab5990d..2c55e093fc3bfd1f43abd6ae60dba20e3e32337d 100644
--- a/modules/ad_content/js/ad-content.render.js
+++ b/modules/ad_content/js/ad-content.render.js
@@ -1,19 +1,16 @@
 (function (document, $, Drupal, once) {
-
   Drupal.behaviors.ad = {
+    attach(context, settings) {
+      const query = { ads: {} };
 
-    attach: function (context, settings) {
-      var query = { ads: {} };
-
-      $(once('ad-content', 'ad-content', context))
-        .each(function () {
-          var $placeholder = $(this);
-          query.ads[$placeholder.attr('id')] = {
-            size: $placeholder.attr('size'),
-            bucket: $placeholder.attr('bucket'),
-            arguments: $placeholder.attr('arguments') || {}
-          };
-        });
+      $(once('ad-content', 'ad-content', context)).each(function () {
+        const $placeholder = $(this);
+        query.ads[$placeholder.attr('id')] = {
+          size: $placeholder.attr('size'),
+          bucket: $placeholder.attr('bucket'),
+          arguments: $placeholder.attr('arguments') || {},
+        };
+      });
 
       if (Object.keys(query.ads).length > 0) {
         query.uid = settings.user.uid;
@@ -21,22 +18,20 @@
         query.page_title = document.title;
         query.referrer = document.referrer;
 
-        var url = Drupal.url('ad/content/render');
+        const url = Drupal.url('ad/content/render');
         $.get(
           url,
           query,
           function (responseData) {
-            for (var id in responseData) {
+            Object.values(responseData).forEach((id) => {
               if (responseData.hasOwnProperty(id)) {
-                $('#' + id).html(responseData[id]);
+                $(`#${id}`).html(responseData[id]);
               }
-            }
+            });
           },
-          'json'
+          'json',
         );
       }
-    }
-
+    },
   };
-
 })(document, jQuery, Drupal, once);
diff --git a/modules/ad_content/src/Controller/ImpressionController.php b/modules/ad_content/src/Controller/ImpressionController.php
index 79f3374931cbc4ab1e0e4bc115a287285ee14499..5e5112ae8c190eff92303c66ca6d0cedf0b536dc 100644
--- a/modules/ad_content/src/Controller/ImpressionController.php
+++ b/modules/ad_content/src/Controller/ImpressionController.php
@@ -12,19 +12,19 @@ use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
- * AD impression controller.
+ * Ad impression controller.
  */
 class ImpressionController extends ControllerBase implements ContainerInjectionInterface {
 
   /**
-   * The AD size factory.
+   * The ad size factory.
    *
    * @var \Drupal\ad\Size\SizeFactory
    */
   protected SizeFactory $sizeFactory;
 
   /**
-   * The AD bucket factory.
+   * The ad bucket factory.
    *
    * @var \Drupal\ad\Bucket\BucketFactoryInterface
    */
@@ -41,9 +41,9 @@ class ImpressionController extends ControllerBase implements ContainerInjectionI
    * Impression constructor.
    *
    * @param \Drupal\ad\Size\SizeFactory $ad_size_factory
-   *   The AD size factory.
+   *   The ad size factory.
    * @param \Drupal\ad\Bucket\BucketFactoryInterface $bucket_factory
-   *   The AD bucket factory.
+   *   The ad bucket factory.
    * @param \Drupal\Core\Render\RendererInterface $renderer
    *   The renderer service.
    */
@@ -69,7 +69,7 @@ class ImpressionController extends ControllerBase implements ContainerInjectionI
   }
 
   /**
-   * Renders the specified ADs.
+   * Renders the specified ads.
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The HTTP request.
diff --git a/modules/ad_content/src/Entity/AdContent.php b/modules/ad_content/src/Entity/AdContent.php
index 62d7249359984aeeeba04cf242d5856343b1f558..086228958afb3c46b8804217de2e0b1172897451 100644
--- a/modules/ad_content/src/Entity/AdContent.php
+++ b/modules/ad_content/src/Entity/AdContent.php
@@ -2,70 +2,84 @@
 
 namespace Drupal\ad_content\Entity;
 
-use Drupal\Core\Entity\EditorialContentEntityBase;
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Field\BaseFieldDefinition;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\ad_content\Form\AdContentEntityForm;
 use Drupal\Core\Url;
 use Drupal\user\EntityOwnerTrait;
+use Drupal\views\EntityViewsData;
+use Drupal\Core\Entity\EntityViewBuilder;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\ContentEntityDeleteForm;
+use Drupal\Core\Entity\Attribute\ContentEntityType;
+use Drupal\Core\Entity\EditorialContentEntityBase;
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
 
 /**
- * Defines the AD content entity class.
- *
- * @ContentEntityType(
- *   id = "ad_content",
- *   label = @Translation("AD Content"),
- *   label_collection = @Translation("Advertisement"),
- *   label_singular = @Translation("content AD"),
- *   label_plural = @Translation("content ADs"),
- *   label_count = @PluralTranslation(
- *     singular = "@count content AD",
- *     plural = "@count content ADs",
- *   ),
- *   handlers = {
- *     "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
- *     "access" = "Drupal\Core\Entity\EntityAccessControlHandler",
- *     "list_builder" = "Drupal\ad_content\Entity\AdContentListBuilder",
- *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
- *     "views_data" = "Drupal\views\EntityViewsData",
- *     "form" = {
- *       "add" = "Drupal\Core\Entity\ContentEntityForm",
- *       "edit" = "Drupal\Core\Entity\ContentEntityForm",
- *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
- *       "default" = "Drupal\Core\Entity\ContentEntityForm"
- *     },
- *    "route_provider" = {
- *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
- *     },
- *   },
- *   base_table = "ad_content",
- *   revision_table = "ad_content_revision",
- *   admin_permission="administer ads",
- *   field_ui_base_route = "entity.ad_content.collection",
- *   links = {
- *     "add-form" = "/admin/ad/content/add",
- *     "create" = "/admin/ad/content/add",
- *     "canonical" = "/admin/ad/content/{ad_content}",
- *     "collection" = "/admin/ad",
- *     "delete-form" = "/admin/ad/content/{ad_content}/delete",
- *     "edit-form" = "/admin/ad/content/{ad_content}/edit",
- *   },
- *   entity_keys = {
- *     "id" = "id",
- *     "revision" = "revision_id",
- *     "label" = "title",
- *     "uuid" = "uuid",
- *     "published" = "status",
- *     "owner" = "uid",
- *   },
- *   revision_metadata_keys = {
- *     "revision_user" = "revision_user",
- *     "revision_created" = "revision_created",
- *     "revision_log_message" = "revision_log"
- *   },
- *   render_cache = TRUE,
- * )
+ * Defines the advertisement entity class.
  */
+#[ContentEntityType(
+  id:'ad_content',
+  label: new TranslatableMarkup('Advertisement'),
+  label_collection: new TranslatableMarkup('Advertisements'),
+  label_singular: new TranslatableMarkup('Advertisement'),
+  label_plural: new TranslatableMarkup('Advertisements'),
+  label_count: [
+    'singular' => '@count advertisement',
+    'plural' => '@count advertisements',
+  ],
+  handlers: [
+    'storage' => SqlContentEntityStorage::class,
+    'access' => EntityAccessControlHandler::class,
+    'list_builder' => AdContentListBuilder::class,
+    'view_builder' => EntityViewBuilder::class,
+    'views_data' => EntityViewsData::class,
+    'form' => [
+      'add' => AdContentEntityForm::class,
+      'edit' => AdContentEntityForm::class,
+      'delete' => ContentEntityDeleteForm::class,
+      'default' => AdContentEntityForm::class,
+    ],
+    'route_provider' => [
+      'html' => AdminHtmlRouteProvider::class,
+    ],
+  ],
+  base_table: 'ad_content',
+  data_table: 'ad_content_field_data',
+  revision_table: 'ad_content_revision',
+  permission_granularity: 'bundle',
+  admin_permission: 'administer ads',
+  bundle_entity_type: 'ad_content_type',
+  bundle_label: new TranslatableMarkup('Advertisement type'),
+  field_ui_base_route: 'entity.ad_content_type.edit_form',
+  links: [
+    'add-page' => '/admin/content/ad/add',
+    'add-form' => '/admin/content/ad/add/{ad_content_type}',
+    'canonical' => '/admin/content/ad/{ad_content}',
+    'collection' => '/admin/content/ad',
+    'delete-form' => '/admin/content/ad/{ad_content}/delete',
+    'edit-form' => '/admin/content/ad/{ad_content}/edit',
+  ],
+  entity_keys: [
+    'id' => 'id',
+    'bundle' => 'bundle',
+    'revision' => 'revision_id',
+    'label' => 'title',
+    'uuid' => 'uuid',
+    'published' => 'status',
+    'owner' => 'uid',
+    'langcode' => 'langcode',
+  ],
+  revision_metadata_keys: [
+    'revision_user' => 'revision_user',
+    'revision_created' => 'revision_created',
+    'revision_log_message' => 'revision_log',
+  ],
+  render_cache: TRUE,
+  translatable: TRUE,
+)]
 class AdContent extends EditorialContentEntityBase implements AdContentInterface {
 
   use EntityOwnerTrait;
@@ -85,10 +99,10 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
   }
 
   /**
-   * Returns the AD size.
+   * Returns the ad size.
    *
    * @return string
-   *   The AD size ID.
+   *   The ad size ID.
    */
   public function getSizeId(): string {
     return $this->get('size')->value;
@@ -111,15 +125,17 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
     $fields = parent::baseFieldDefinitions($entity_type);
     $fields += static::ownerBaseFieldDefinitions($entity_type);
 
-    // @todo Implement AD bundles.
-    // $fields['type']->setDescription(t('The AD Content type.'));
     $fields['status']
       ->setDisplayConfigurable('form', TRUE)
-      ->setDisplayConfigurable('view', TRUE);
+      ->setDisplayConfigurable('view', TRUE)
+      ->setDisplayOptions('form', [
+        'type' => 'boolean_checkbox',
+        'weight' => 15,
+      ]);
 
     $fields['title'] = BaseFieldDefinition::create('string')
       ->setLabel(new TranslatableMarkup('Title'))
-      ->setDescription(new TranslatableMarkup('A brief description of the AD.'))
+      ->setDescription(new TranslatableMarkup('A brief description of the advertisement.'))
       ->setRevisionable(TRUE)
       ->setRequired(TRUE)
       ->addConstraint('UniqueField', [])
@@ -133,7 +149,8 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
         'type' => 'string',
         'weight' => -5,
       ])
-      ->setDisplayConfigurable('view', TRUE);
+      ->setDisplayConfigurable('view', TRUE)
+      ->setTranslatable(TRUE);
 
     $fields['uid']
       ->setLabel(new TranslatableMarkup('Authored by'))
@@ -159,7 +176,7 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
 
     $fields['size'] = BaseFieldDefinition::create('list_string')
       ->setLabel(new TranslatableMarkup('Size'))
-      ->setDescription(new TranslatableMarkup('The AD size.'))
+      ->setDescription(new TranslatableMarkup("The advertisement's size."))
       ->setRevisionable(TRUE)
       ->setRequired(TRUE)
       ->setSettings([
@@ -178,7 +195,7 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
 
     $fields['target_url'] = BaseFieldDefinition::create('link')
       ->setLabel(new TranslatableMarkup('Target URL'))
-      ->setDescription(new TranslatableMarkup('The URL to be taken to when clicking on the AD.'))
+      ->setDescription(new TranslatableMarkup('The URL to be taken to when clicking on the advertisement.'))
       ->setRevisionable(TRUE)
       ->setRequired(TRUE)
       ->setDisplayOptions('form', [
@@ -190,11 +207,12 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
         'type' => 'link',
         'weight' => -3,
       ])
-      ->setDisplayConfigurable('view', TRUE);
+      ->setDisplayConfigurable('view', TRUE)
+      ->setTranslatable(TRUE);
 
     $fields['created'] = BaseFieldDefinition::create('created')
       ->setLabel(new TranslatableMarkup('Authored on'))
-      ->setDescription(t('The time that the AD was created.'))
+      ->setDescription(t('The time that the advertisement was created.'))
       ->setRevisionable(TRUE)
       ->setDisplayOptions('form', [
         'type' => 'datetime_timestamp',
@@ -210,7 +228,7 @@ class AdContent extends EditorialContentEntityBase implements AdContentInterface
 
     $fields['changed'] = BaseFieldDefinition::create('changed')
       ->setLabel(t('Changed'))
-      ->setDescription(t('The time that the AD was last edited.'));
+      ->setDescription(t('The time that the advertisement was last edited.'));
 
     return $fields;
   }
diff --git a/modules/ad_content/src/Entity/AdContentInterface.php b/modules/ad_content/src/Entity/AdContentInterface.php
index 3253f775ba7387390fb5b3cc7be70ae651a4fe84..dafd5dbbb6294afd85fb8a2a3576eef1312de941 100644
--- a/modules/ad_content/src/Entity/AdContentInterface.php
+++ b/modules/ad_content/src/Entity/AdContentInterface.php
@@ -10,7 +10,7 @@ use Drupal\Core\Entity\RevisionLogInterface;
 use Drupal\user\EntityOwnerInterface;
 
 /**
- * Common interface for AD content entities.
+ * Common interface for advertisement entities.
  */
 interface AdContentInterface extends AdInterface, ContentEntityInterface, EntityOwnerInterface, EntityChangedInterface, EntityPublishedInterface, RevisionLogInterface {
 
diff --git a/modules/ad_content/src/Entity/AdContentListBuilder.php b/modules/ad_content/src/Entity/AdContentListBuilder.php
index 271bb323843aca2f13e78b2adb26cef63a72da02..b5109a37f01648915616818ad3d4b84c5d99d2f5 100644
--- a/modules/ad_content/src/Entity/AdContentListBuilder.php
+++ b/modules/ad_content/src/Entity/AdContentListBuilder.php
@@ -4,12 +4,39 @@ namespace Drupal\ad_content\Entity;
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityListBuilder;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * AD content list builder.
+ * Advertisement list builder.
  */
 class AdContentListBuilder extends EntityListBuilder {
 
+  /**
+   * The entity type manager service.
+   *
+   * @var Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($entity_type, $storage);
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('entity_type.manager')->getStorage($entity_type->id()),
+      $container->get('entity_type.manager'),
+    );
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -18,6 +45,7 @@ class AdContentListBuilder extends EntityListBuilder {
       'id' => $this->t('ID'),
       'title' => $this->t('Title'),
       'size' => $this->t('Size'),
+      'bundle' => $this->t('Type'),
       'status' => $this->t('Status'),
     ];
 
@@ -40,10 +68,25 @@ class AdContentListBuilder extends EntityListBuilder {
         ],
       ],
       'size' => $ad_content->getSizeId(),
-      'status' => $ad_content->isPublished(),
+      'bundle' => $this->getBundleName($ad_content->bundle()),
+      'status' => $ad_content->isPublished() ? $this->t('Published') : $this->t('Not published'),
     ];
 
     return $row + parent::buildRow($ad_content);
   }
 
+  /**
+   * Returns the name of the bundle with the given ID.
+   *
+   * @param string $bundleId
+   *   The ID of the bundle.
+   *
+   * @return string
+   *   The name of the bundle.
+   */
+  private function getBundleName($bundleId): string {
+    $bundle = $this->entityTypeManager->getStorage('ad_content_type')->load($bundleId);
+    return $bundle !== NULL ? $bundle->label() : $this->t('@bundleId (Not found)', ['@bundleId' => $bundleId]);
+  }
+
 }
diff --git a/modules/ad_content/src/Entity/AdContentType.php b/modules/ad_content/src/Entity/AdContentType.php
new file mode 100644
index 0000000000000000000000000000000000000000..d73a6599ecc5451e9a88198035302a2accf3550e
--- /dev/null
+++ b/modules/ad_content/src/Entity/AdContentType.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Drupal\ad_content\Entity;
+
+use Drupal\Core\Entity\EntityDeleteForm;
+use Drupal\Core\Entity\Attribute\ConfigEntityType;
+use Drupal\ad_content\Form\AdContentTypeEntityForm;
+use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
+
+/**
+ * Defines the advertisement type bundle class.
+ */
+#[ConfigEntityType(
+  id: 'ad_content_type',
+  label: new TranslatableMarkup('Advertisement Type'),
+  label_singular: new TranslatableMarkup('Advertisement type'),
+  label_plural: new TranslatableMarkup('Advertisement types'),
+  label_count: [
+    'singular' => '@count advertisement type',
+    'plural' => '@count advertisement types',
+  ],
+  bundle_of: 'ad_content',
+  entity_keys: [
+    'id' => 'id',
+    'label' => 'label',
+    'uuid' => 'uuid',
+  ],
+  config_prefix: 'ad_content_type',
+  config_export: [
+    'id',
+    'label',
+    'description',
+  ],
+  handlers: [
+    'list_builder' => AdContentTypeListBuilder::class,
+    'form' => [
+      'default' => AdContentTypeEntityForm::class,
+      'add' => AdContentTypeEntityForm::class,
+      'edit' => AdContentTypeEntityForm::class,
+      'delete' => EntityDeleteForm::class,
+    ],
+    'route_provider' => [
+      'html' => AdminHtmlRouteProvider::class,
+    ],
+  ],
+  admin_permission: 'administer ads',
+  links: [
+    'canonical' => '/admin/structure/ad-content/{ad_content_type}',
+    'add-form' => '/admin/structure/ad-content/add',
+    'edit-form' => '/admin/structure/ad-content/manage/{ad_content_type}',
+    'delete-form' => '/admin/structure/ad-content/manage/{ad_content_type}/delete',
+    'collection' => '/admin/structure/ad-content',
+  ]
+)]
+class AdContentType extends ConfigEntityBundleBase implements AdContentTypeInterface {
+
+  /**
+   * A brief description of the advertisement type.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getDescription(): string {
+    return $this->description ?? '';
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function setDescription($description) {
+    return $this->set('description', $description);
+  }
+
+  /**
+   * Returns the UUID of the advertisement type.
+   */
+  public function getUuid(): string {
+    return $this->uuid();
+  }
+
+  /**
+   * Returns the id of the advertisement type.
+   */
+  public function getId(): string {
+    return $this->id();
+  }
+
+  /**
+   * Returns the label of the advertisement type.
+   */
+  public function getLabel(): string {
+    return $this->label();
+  }
+
+}
diff --git a/modules/ad_content/src/Entity/AdContentTypeInterface.php b/modules/ad_content/src/Entity/AdContentTypeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..864ae7ae2192addcef6737e17d0bddbdd2b5f636
--- /dev/null
+++ b/modules/ad_content/src/Entity/AdContentTypeInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\ad_content\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Common interface for advertisement type bundle entities.
+ */
+interface AdContentTypeInterface extends ConfigEntityInterface {
+
+  /**
+   * Returns the description of the advertisement type.
+   *
+   * @return string
+   *   The description of the advertisement type.
+   */
+  public function getDescription(): string;
+
+  /**
+   * Sets the description of the advertisement type.
+   *
+   * @param string $description
+   *   The description of the advertisement type.
+   */
+  public function setDescription($description);
+
+}
diff --git a/modules/ad_content/src/Entity/AdContentTypeListBuilder.php b/modules/ad_content/src/Entity/AdContentTypeListBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..ecb5384af01a5cd122c297afa89ab8efecd30018
--- /dev/null
+++ b/modules/ad_content/src/Entity/AdContentTypeListBuilder.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\ad_content\Entity;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListBuilder;
+
+/**
+ * Advertisement list builder.
+ */
+class AdContentTypeListBuilder extends EntityListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header = [
+      'title' => $this->t('Title'),
+      'description' => $this->t('Description'),
+    ];
+
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $ad_content_type) {
+    /** @var \Drupal\ad_content\Entity\AdContentTypeInterface $ad_content_type */
+
+    $row = [
+      'title' => $ad_content_type->label(),
+      'description' => $ad_content_type->getDescription(),
+    ];
+
+    return $row + parent::buildRow($ad_content_type);
+  }
+
+}
diff --git a/modules/ad_content/src/Entity/AdContentViewController.php b/modules/ad_content/src/Entity/AdContentViewController.php
index 7134f8c9846d37057bf33d8e888996c8a67db39e..71a44f0fce0beec63e97337cfcf7a89d816bf864 100644
--- a/modules/ad_content/src/Entity/AdContentViewController.php
+++ b/modules/ad_content/src/Entity/AdContentViewController.php
@@ -6,7 +6,7 @@ use Drupal\Core\Entity\Controller\EntityViewController;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
- * AD content view controller.
+ * Advertisement view controller.
  */
 class AdContentViewController extends EntityViewController {
 
@@ -14,7 +14,7 @@ class AdContentViewController extends EntityViewController {
    * The _title_callback for the page that renders a single node.
    *
    * @param \Drupal\Core\Entity\EntityInterface $ad_content
-   *   The current AD content.
+   *   The current advertisement.
    *
    * @return string
    *   The page title.
diff --git a/modules/ad_content/src/Field/AdContentImageFormatterBase.php b/modules/ad_content/src/Field/AdContentImageFormatterBase.php
index 1145796fb6b00e24a9e1ead152f1a967aa1af303..b07293e069531a961c3701c8c7f1cc7f229f036c 100644
--- a/modules/ad_content/src/Field/AdContentImageFormatterBase.php
+++ b/modules/ad_content/src/Field/AdContentImageFormatterBase.php
@@ -8,7 +8,7 @@ use Drupal\Core\Url;
 use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase;
 
 /**
- * Base class for AD image formatters.
+ * Base class for ad image formatters.
  */
 abstract class AdContentImageFormatterBase extends ImageFormatterBase {
 
@@ -53,7 +53,7 @@ abstract class AdContentImageFormatterBase extends ImageFormatterBase {
    * Returns the URL the image should be linked to.
    *
    * @param \Drupal\ad_content\Entity\AdContentInterface $ad_content
-   *   A content AD entity.
+   *   A content ad entity.
    *
    * @return \Drupal\Core\Url|null
    *   A URL object or NULL if none is available.
diff --git a/modules/ad_content/src/Form/AdContentEntityForm.php b/modules/ad_content/src/Form/AdContentEntityForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..90d31ebdd2cb38bdfdad8eb824f03f034ac67df7
--- /dev/null
+++ b/modules/ad_content/src/Form/AdContentEntityForm.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\ad_content\Form;
+
+use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Entity form for advertisement.
+ */
+class AdContentEntityForm extends ContentEntityForm {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    $status = parent::save($form, $form_state);
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
+    return $status;
+  }
+
+}
diff --git a/modules/ad_content/src/Form/AdContentTypeEntityForm.php b/modules/ad_content/src/Form/AdContentTypeEntityForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..3abf2f6e35f120ad5b25978c19ac36854b55fd9a
--- /dev/null
+++ b/modules/ad_content/src/Form/AdContentTypeEntityForm.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\ad_content\Form;
+
+use Drupal\Core\Entity\BundleEntityFormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Entity form for the advertisement type bundle.
+ */
+class AdContentTypeEntityForm extends BundleEntityFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+
+    /** @var \Drupal\ad_content\Entity\AdContentTypeInterface $entity_type */
+    $entity_type = $this->entity;
+
+    $form['label'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Title'),
+      '#maxlength' => 255,
+      '#default_value' => $entity_type->label(),
+      '#description' => $this->t('The title for the advertisement type (bundle).'),
+      '#required' => TRUE,
+    ];
+
+    $form['id'] = [
+      '#type' => 'machine_name',
+      '#default_value' => $entity_type->id(),
+      '#machine_name' => [
+        'exists' => '\Drupal\ad_content\Entity\AdContentType::load',
+      ],
+      '#disabled' => !$entity_type->isNew(),
+    ];
+
+    $form['description'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Description'),
+      '#default_value' => $entity_type->getDescription() ?? '',
+      '#description' => $this->t('A short description of the advertisement type.'),
+      '#required' => FALSE,
+    ];
+
+    return $this->protectBundleIdElement($form);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    $entity_type = $this->entity;
+    $status = $entity_type->save();
+    $message_params = [
+      '%label' => $entity_type->label(),
+      '%content_entity_id' => $entity_type->getEntityType()->getBundleOf(),
+    ];
+
+    // Provide a message for the user and redirect them back to the collection.
+    switch ($status) {
+      case SAVED_NEW:
+        $this->messenger()->addMessage($this->t('Created the %label advertisement type..', $message_params));
+        break;
+
+      default:
+        $this->messenger()->addMessage($this->t('Saved the %label advertisement type.', $message_params));
+    }
+
+    $form_state->setRedirectUrl($entity_type->toUrl('collection'));
+    return $status;
+  }
+
+}
diff --git a/modules/ad_content/src/Plugin/Ad/Bucket/AdContentBucket.php b/modules/ad_content/src/Plugin/Ad/Bucket/AdContentBucket.php
index 4a9f8900f76a102942afadf60236fe29d5d78abf..4d67cfc2baba6b7e0c56a4dcd7c44a9b8ea3a4b8 100644
--- a/modules/ad_content/src/Plugin/Ad/Bucket/AdContentBucket.php
+++ b/modules/ad_content/src/Plugin/Ad/Bucket/AdContentBucket.php
@@ -25,11 +25,11 @@ use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * Basic AD content bucket.
+ * Basic advertisement bucket.
  *
  * @Plugin(
  *   id = \Drupal\ad_content\Entity\AdContentInterface::BUCKET_ID,
- *   label = @Translation("AD Content"),
+ *   label = @Translation("Advertisement"),
  * )
  */
 class AdContentBucket extends PluginBase implements BucketInterface, ContainerFactoryPluginInterface, TrustedCallbackInterface {
@@ -49,7 +49,7 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
   protected EntityRepositoryInterface $entityRepository;
 
   /**
-   * The AD tracker factory.
+   * The ad tracker factory.
    *
    * @var \Drupal\ad\Track\TrackerFactoryInterface
    */
@@ -83,7 +83,7 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
    * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
    *   The entity repository.
    * @param \Drupal\ad\Track\TrackerFactoryInterface $tracker_factory
-   *   The AD tracker factory.
+   *   The ad tracker factory.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
    * @param \Psr\Log\LoggerInterface $logger
@@ -194,20 +194,20 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
   }
 
   /**
-   * Returns the AD to be rendered.
+   * Returns the ad to be rendered.
    *
    * @param \Drupal\ad\Size\SizeInterface $size
-   *   The AD size.
+   *   The ad size.
    *
    * @return \Drupal\ad_content\Entity\AdContentInterface
-   *   An AD content entity.
+   *   An advertisement entity.
    */
   protected function getAdContent(SizeInterface $size): ?AdContentInterface {
     return $this->getRandomAd($size);
   }
 
   /**
-   * Post render callback for the AD view builder.
+   * Post render callback for the ad view builder.
    */
   public static function postAdRender($markup, array $build) {
     $output = str_replace(TrackerInterface::PLACEHOLDER_IMPRESSION, $build['#ad_impression_id'], $markup);
@@ -215,13 +215,13 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
   }
 
   /**
-   * Retrieves a random AD of the specified size.
+   * Retrieves a random ad of the specified size.
    *
    * @param \Drupal\ad\Size\SizeInterface $size
-   *   The AD size.
+   *   The ad size.
    *
    * @return \Drupal\ad_content\Entity\AdContentInterface|null
-   *   An AD entity or NULL if none could be found.
+   *   An ad entity or NULL if none could be found.
    */
   protected function getRandomAd(SizeInterface $size): ?AdContentInterface {
     $size_id = $size->getId();
@@ -235,8 +235,8 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
         ->accessCheck(TRUE);
 
       $result = $query
+        ->addTag('order_random')
         ->range(0, 1)
-        ->sort('id')
         ->execute();
 
       if ($result) {
@@ -255,7 +255,7 @@ class AdContentBucket extends PluginBase implements BucketInterface, ContainerFa
    * Get the base query for finding an ad by size.
    *
    * @param string $size_id
-   *   The AD size ID.
+   *   The ad size ID.
    *
    * @return \Drupal\Core\Entity\Query\QueryInterface
    *   Ad base query.
diff --git a/modules/ad_content/src/Plugin/Field/FieldFormatter/AdContentImageFormatter.php b/modules/ad_content/src/Plugin/Field/FieldFormatter/AdContentImageFormatter.php
index 4ef0f3801c69e4edec3e808b66261522961f433a..fd083ec9c7cddd638a8172bb23b774230f2e07a6 100644
--- a/modules/ad_content/src/Plugin/Field/FieldFormatter/AdContentImageFormatter.php
+++ b/modules/ad_content/src/Plugin/Field/FieldFormatter/AdContentImageFormatter.php
@@ -5,11 +5,11 @@ namespace Drupal\ad_content\Plugin\Field\FieldFormatter;
 use Drupal\ad_content\Field\AdContentImageFormatterBase;
 
 /**
- * Plugin implementation of the AD Image formatter.
+ * Plugin implementation of the ad Image formatter.
  *
  * @FieldFormatter(
  *   id = "ad_content_image",
- *   label = @Translation("AD Image"),
+ *   label = @Translation("Ad Image"),
  *   field_types = {
  *     "image"
  *   }
diff --git a/modules/ad_content/src/Plugin/Field/FieldFormatter/TrackAdContentImageFormatter.php b/modules/ad_content/src/Plugin/Field/FieldFormatter/TrackAdContentImageFormatter.php
index 16c38cee982d4503f3dea58e32940aa54f022325..05e1bf17aa0ff5b4f97843298230d706e801733e 100644
--- a/modules/ad_content/src/Plugin/Field/FieldFormatter/TrackAdContentImageFormatter.php
+++ b/modules/ad_content/src/Plugin/Field/FieldFormatter/TrackAdContentImageFormatter.php
@@ -9,11 +9,11 @@ use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Url;
 
 /**
- * Plugin implementation of the AD image formatter with local tracking.
+ * Plugin implementation of the ad image formatter with local tracking.
  *
  * @FieldFormatter(
  *   id = "ad_content_image_click_track",
- *   label = @Translation("AD Image with local click tracking"),
+ *   label = @Translation("Ad Image with local click tracking"),
  *   field_types = {
  *     "image"
  *   }
diff --git a/modules/ad_content/src/Routing/RouteSubscriber.php b/modules/ad_content/src/Routing/RouteSubscriber.php
deleted file mode 100644
index 1daf545df6c523b8c9148c5074f1e302734f6790..0000000000000000000000000000000000000000
--- a/modules/ad_content/src/Routing/RouteSubscriber.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Drupal\ad_content\Routing;
-
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * AD content route subscriber.
- */
-class RouteSubscriber extends RouteSubscriberBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function alterRoutes(RouteCollection $collection) {
-    // Move AD settings to a dedicated route to make room for the AD content
-    // listing.
-    $collection->get('ad.settings')
-      ->setPath('/admin/ad/settings');
-  }
-
-}
diff --git a/modules/ad_content/tests/src/Functional/AdContentGenericTest.php b/modules/ad_content/tests/src/Functional/AdContentGenericTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a4f8213d4408ee2205ed4b2a627416111fb36075
--- /dev/null
+++ b/modules/ad_content/tests/src/Functional/AdContentGenericTest.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Tests\ad_content\Functional;
+
+use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
+
+/**
+ * Generic module test for ad_content.
+ *
+ * @group ad_content
+ */
+class AdContentGenericTest extends GenericModuleTestBase {
+
+  /**
+   * {@inheritDoc}
+   */
+  protected function assertHookHelp(string $module): void {}
+
+}
diff --git a/modules/ad_scheduler/ad_scheduler.info.yml b/modules/ad_scheduler/ad_scheduler.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..000289988e3bc9b433fff52605fefb1767656a8c
--- /dev/null
+++ b/modules/ad_scheduler/ad_scheduler.info.yml
@@ -0,0 +1,9 @@
+name: Advertisement scheduler
+description: 'Integrates advertisements with the scheduler module.'
+type: module
+package: Advertisement
+core_version_requirement: ^10.3 || ^11
+dependencies:
+  - ad:ad
+  - ad:ad_content
+  - scheduler:scheduler
diff --git a/modules/ad_scheduler/src/Event/SchedulerAdContentEvents.php b/modules/ad_scheduler/src/Event/SchedulerAdContentEvents.php
new file mode 100644
index 0000000000000000000000000000000000000000..679f5189b7ed531e6102d46db9a30fbac64f683f
--- /dev/null
+++ b/modules/ad_scheduler/src/Event/SchedulerAdContentEvents.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\ad_scheduler\Event;
+
+/**
+ * Lists the six events dispatched by Scheduler relating to ad content entities.
+ *
+ * The event names here are the original six, when only ad contents were
+ * supported. See SchedulerTaxonomyTermEvents for the generic naming convention
+ * to follow for any new entity plugin implementations.
+ */
+final class SchedulerAdContentEvents {
+
+  /**
+   * The event triggered after an ad content is published immediately.
+   *
+   * This event allows modules to react after an entity is published
+   * immediately when being saved after editing. The event listener method
+   * receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const PUBLISH_IMMEDIATELY = 'scheduler.publish_immediately';
+
+  /**
+   * The event triggered after an ad content is published by cron.
+   *
+   * This event allows modules to react after an entity is published by Cron.
+   * The event listener receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const PUBLISH = 'scheduler.publish';
+
+  /**
+   * The event triggered before an ad content is published immediately.
+   *
+   * This event allows modules to react before an entity is published
+   * immediately when being saved after editing. The event listener method
+   * receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const PRE_PUBLISH_IMMEDIATELY = 'scheduler.pre_publish_immediately';
+
+  /**
+   * The event triggered before an ad content is published by cron.
+   *
+   * This event allows modules to react before an entity is published by Cron.
+   * The event listener receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const PRE_PUBLISH = 'scheduler.pre_publish';
+
+  /**
+   * The event triggered before an ad content is unpublished by cron.
+   *
+   * This event allows modules to react before an entity is unpublished by Cron.
+   * The event listener receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const PRE_UNPUBLISH = 'scheduler.pre_unpublish';
+
+  /**
+   * The event triggered after an ad content is unpublished by cron.
+   *
+   * This event allows modules to react after an entity is unpublished by Cron.
+   * The event listener receives a \Drupal\Core\Entity\EntityInterface instance.
+   *
+   * @Event
+   *
+   * @see \Drupal\scheduler\Event\SchedulerEvent
+   *
+   * @var string
+   */
+  const UNPUBLISH = 'scheduler.unpublish';
+
+}
diff --git a/modules/ad_scheduler/src/Plugin/Scheduler/AdContentScheduler.php b/modules/ad_scheduler/src/Plugin/Scheduler/AdContentScheduler.php
new file mode 100644
index 0000000000000000000000000000000000000000..919f838efe47a483fd9b4d9cfd702a1b06ad4b46
--- /dev/null
+++ b/modules/ad_scheduler/src/Plugin/Scheduler/AdContentScheduler.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\ad_scheduler\Plugin\Scheduler;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\scheduler\SchedulerPluginBase;
+
+/**
+ * Advertisement plugin for the scheduler module.
+ *
+ * @package Drupal\ad_scheduler\Plugin\Scheduler
+ *
+ * @SchedulerPlugin(
+ *  id = "ad_content_scheduler",
+ *  label = @Translation("Advertisement scheduler plugin"),
+ *  description = @Translation("Support for scheduling advertisement entities"),
+ *  entityType = "ad_content",
+ *  dependency = "ad_content",
+ * )
+ */
+class AdContentScheduler extends SchedulerPluginBase implements ContainerFactoryPluginInterface {}
diff --git a/modules/ad_scheduler/tests/src/Functional/AdSchedulerGenericTest.php b/modules/ad_scheduler/tests/src/Functional/AdSchedulerGenericTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ddf4104b8728130f2a27edcc2576f9f260698ad
--- /dev/null
+++ b/modules/ad_scheduler/tests/src/Functional/AdSchedulerGenericTest.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Tests\ad_scheduler\Functional;
+
+use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
+
+/**
+ * Generic module test for ad_scheduler.
+ *
+ * @group ad_scheduler
+ */
+class AdSchedulerGenericTest extends GenericModuleTestBase {
+
+  /**
+   * {@inheritDoc}
+   */
+  protected function assertHookHelp(string $module): void {}
+
+}
diff --git a/modules/ad_track/ad_track.info.yml b/modules/ad_track/ad_track.info.yml
index 43cc5f3676ba970763fb03dbc055d134a57e5e0b..7a64dfa3b5ac9ada48db383c18a2317cca3eca3b 100644
--- a/modules/ad_track/ad_track.info.yml
+++ b/modules/ad_track/ad_track.info.yml
@@ -1,5 +1,5 @@
-name: AD Track
-description: 'AD statistics tracker allowing to count AD impressions and clicks locally.'
+name: Advertisement track
+description: 'Advertisement statistics tracker allowing to count advertisement impressions and clicks locally.'
 type: module
 package: Advertisement
 core_version_requirement: ^10.3 || ^11
diff --git a/modules/ad_track/ad_track.install b/modules/ad_track/ad_track.install
index c9556c1c12362922a0ea64c990f9165b28ddb240..89c63495abcf275599c2cdc4f89dbab6865acb16 100644
--- a/modules/ad_track/ad_track.install
+++ b/modules/ad_track/ad_track.install
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Installation file for the "AD Track" module.
+ * Installation file for the "Ad Track" module.
  */
 
 /**
@@ -12,29 +12,29 @@ function ad_track_schema() {
   $schema = [];
 
   $schema['ad_track_total'] = [
-    'description' => 'Stores AD total event counts.',
+    'description' => 'Stores advertisement total event counts.',
     'fields' => [
       'ad_id' => [
-        'description' => 'The AD identifier.',
+        'description' => 'The advertisement identifier.',
         'type' => 'varchar',
         'length' => 128,
         'not null' => TRUE,
       ],
       'bucket_id' => [
-        'description' => 'The identifier of the AD bucket.',
+        'description' => 'The identifier of the advertisement bucket.',
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
       ],
       'click' => [
-        'description' => 'Total clicks of an ad.',
+        'description' => 'Total clicks of an advertisement.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
       ],
       'impression' => [
-        'description' => 'Total impressions of an ad.',
+        'description' => 'Total impressions of an advertisement.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -46,3 +46,17 @@ function ad_track_schema() {
 
   return $schema;
 }
+
+/**
+ * Implements hook_install().
+ */
+function ad_track_install($is_syncing) : void {
+  $config = \Drupal::configFactory()->getEditable('ad.settings');
+  $trackerSettings = $config->get('trackers');
+  foreach ($trackerSettings as &$trackerSetting) {
+    if ($trackerSetting === 'null') {
+      $trackerSetting = 'local';
+    }
+  }
+  $config->set('trackers', $trackerSettings)->save();
+}
diff --git a/modules/ad_track/ad_track.module b/modules/ad_track/ad_track.module
index 8da0e09ddc7035232ef86a60681be9d986c402db..202cf326b5292bcadfb7ec4aa67445960cbb8531 100644
--- a/modules/ad_track/ad_track.module
+++ b/modules/ad_track/ad_track.module
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Main module file for the "AD Track" module.
+ * Main module file for the "Ad Track" module.
  */
 
 use Drupal\ad\Track\TrackerInterface;
diff --git a/modules/ad_track/ad_track.routing.yml b/modules/ad_track/ad_track.routing.yml
index a7f6dca864cdfb878536563cdef8891e9b1266c2..88d0dc832d59d9ae2a2df9215b70591bbbe2fc51 100644
--- a/modules/ad_track/ad_track.routing.yml
+++ b/modules/ad_track/ad_track.routing.yml
@@ -1,5 +1,5 @@
 ad_track.track:
-  path: '/ad/track/{bucket_id}/{ad_id}'
+  path: '/content/ad/track/{bucket_id}/{ad_id}'
   defaults:
     _controller: '\Drupal\ad_track\Controller\ClickTrackController::track'
     _title: 'Click Tracking'
diff --git a/modules/ad_track/ad_track.services.yml b/modules/ad_track/ad_track.services.yml
index ed55ac4b9dff625380ab161546b2ddada1d75b44..dc1a350c7de99088e6dc0ad20a6410148cc5d1b1 100644
--- a/modules/ad_track/ad_track.services.yml
+++ b/modules/ad_track/ad_track.services.yml
@@ -1,4 +1,4 @@
 services:
   ad_track.total_storage:
     class: Drupal\ad_track\TotalSqlStorage
-    arguments: ['@database']
+    arguments: ['@database', '@logger.factory']
diff --git a/modules/ad_track/ad_track.views.inc b/modules/ad_track/ad_track.views.inc
index 4ebf5d4c4da0a82f3c5441d97586e9404e5d0d74..6ddbff3927d158398739fc6730b4c03c71c6a79f 100644
--- a/modules/ad_track/ad_track.views.inc
+++ b/modules/ad_track/ad_track.views.inc
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Views integration file for the "AD Track" module.
+ * Views integration file for the "Ad Track" module.
  */
 
 use Drupal\ad\Track\TrackerInterface;
@@ -14,17 +14,17 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
 function ad_track_views_data() {
   $data = [];
 
-  $data['ad_track_total']['table']['group'] = new TranslatableMarkup('AD Track');
+  $data['ad_track_total']['table']['group'] = new TranslatableMarkup('Advertisement track');
   $data['ad_track_total']['table']['provider'] = 'ad_track';
 
   $data['ad_track_total']['table']['base'] = [
     'field' => 'ad_id',
-    'title' => new TranslatableMarkup('AD Track Totals'),
-    'help' => new TranslatableMarkup('Stores AD total event counts.'),
+    'title' => new TranslatableMarkup('Advertisement Track Totals'),
+    'help' => new TranslatableMarkup('Stores advertisement total event counts.'),
   ];
 
   $ids = [
-    'ad_id' => new TranslatableMarkup('AD ID'),
+    'ad_id' => new TranslatableMarkup('Advertisement ID'),
     'bucket_id' => new TranslatableMarkup('Bucket ID'),
   ];
 
@@ -78,7 +78,7 @@ function ad_track_views_data() {
 
   if (Drupal::moduleHandler()->moduleExists('ad_content')) {
     $data['ad_track_total']['ad_id']['relationship'] = [
-      'label' => new TranslatableMarkup('AD Content'),
+      'label' => new TranslatableMarkup('Advertisement'),
       'base' => 'ad_content',
       'base field' => 'uuid',
       'id' => 'standard',
@@ -94,7 +94,7 @@ function ad_track_views_data() {
 function ad_track_views_data_alter(array &$data) {
   if (Drupal::moduleHandler()->moduleExists('ad_content')) {
     $data['ad_content']['uuid']['relationship'] = [
-      'label' => new TranslatableMarkup('AD Track Totals'),
+      'label' => new TranslatableMarkup('Advertisement Track Totals'),
       'base' => 'ad_track_total',
       'base field' => 'ad_id',
       'id' => 'standard',
diff --git a/modules/ad_track/src/Controller/ClickTrackController.php b/modules/ad_track/src/Controller/ClickTrackController.php
index 401d0c851b7166ac2d65286517f6c244caa2f3c8..0c275adbc9672876fb0ab3fb9d50db5eca47c5e1 100644
--- a/modules/ad_track/src/Controller/ClickTrackController.php
+++ b/modules/ad_track/src/Controller/ClickTrackController.php
@@ -17,7 +17,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 class ClickTrackController extends ControllerBase {
 
   /**
-   * The AD bucket factory.
+   * The ad bucket factory.
    *
    * @var \Drupal\ad\Bucket\BucketFactoryInterface
    */
@@ -27,7 +27,7 @@ class ClickTrackController extends ControllerBase {
    * ClickTrackController constructor.
    *
    * @param \Drupal\ad\Bucket\BucketFactoryInterface $bucket_factory
-   *   The AD tracker factory.
+   *   The ad tracker factory.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
    */
@@ -47,17 +47,17 @@ class ClickTrackController extends ControllerBase {
   }
 
   /**
-   * Tracks an AD click event.
+   * Tracks an ad click event.
    *
    * @param string $bucket_id
    *   The bucket identifier.
    * @param string $ad_id
-   *   The AD identifier.
+   *   The ad identifier.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The current request.
    *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
-   *   A redirect response pointing to the AD target URL.
+   *   A redirect response pointing to the ad target URL.
    */
   public function track(string $bucket_id, string $ad_id, Request $request): RedirectResponse {
     // @todo Add CSRF protection.
diff --git a/modules/ad_track/src/Entity/AdTrackEvent.php b/modules/ad_track/src/Entity/AdTrackEvent.php
index b1e62f47ea232ef6b2269861fb879169ea4a822c..093f8c067145af4a55cf8a31503c5a2b75da8b62 100644
--- a/modules/ad_track/src/Entity/AdTrackEvent.php
+++ b/modules/ad_track/src/Entity/AdTrackEvent.php
@@ -8,11 +8,11 @@ use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
- * Defines the AD track event entity class.
+ * Defines the ad track event entity class.
  *
  * @ContentEntityType(
  *   id = "ad_track_event",
- *   label = @Translation("AD Track Event"),
+ *   label = @Translation("ad Track Event"),
  *   handlers = {
  *     "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
  *     "storage_schema" = "Drupal\ad_track\Entity\AdTrackEventStorageSchema",
@@ -50,8 +50,8 @@ class AdTrackEvent extends ContentEntityBase {
       ->setDescription(t('The event timestamp.'));
 
     $fields['ad_id'] = BaseFieldDefinition::create('string')
-      ->setLabel(new TranslatableMarkup('AD'))
-      ->setDescription(t('The identifier of the AD the events refers to.'));
+      ->setLabel(new TranslatableMarkup('Advertisement'))
+      ->setDescription(t('The identifier of the advertisement the events refers to.'));
 
     $fields['ip_address'] = BaseFieldDefinition::create('string')
       ->setLabel(new TranslatableMarkup('IP address'))
diff --git a/modules/ad_track/src/Entity/AdTrackEventStorageSchema.php b/modules/ad_track/src/Entity/AdTrackEventStorageSchema.php
index 33b166007098fa5459572fb3b3392fcabbd6128d..fb6f295c85360f59cf611e2b4bc5a45f081358dc 100644
--- a/modules/ad_track/src/Entity/AdTrackEventStorageSchema.php
+++ b/modules/ad_track/src/Entity/AdTrackEventStorageSchema.php
@@ -6,7 +6,7 @@ use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
 
 /**
- * Defines the AD track event schema handler.
+ * Defines the ad track event schema handler.
  */
 class AdTrackEventStorageSchema extends SqlContentEntityStorageSchema {
 
diff --git a/modules/ad_track/src/Entity/AdTrackEventViewsData.php b/modules/ad_track/src/Entity/AdTrackEventViewsData.php
index 774660837749e9674f6492d5b5fa1c1b3cfe2a07..40231fa7f7027cc62d6417deff915949dcc4a52d 100644
--- a/modules/ad_track/src/Entity/AdTrackEventViewsData.php
+++ b/modules/ad_track/src/Entity/AdTrackEventViewsData.php
@@ -6,7 +6,7 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\views\EntityViewsData;
 
 /**
- * Entity views data for the AD Track Event entity type.
+ * Entity views data for the ad Track Event entity type.
  */
 class AdTrackEventViewsData extends EntityViewsData {
 
@@ -18,7 +18,7 @@ class AdTrackEventViewsData extends EntityViewsData {
 
     if ($this->moduleHandler->moduleExists('ad_content')) {
       $data['ad_track_event']['ad_id']['relationship'] = [
-        'label' => new TranslatableMarkup('Content AD'),
+        'label' => new TranslatableMarkup('Advertisement'),
         'base' => 'ad_content',
         'base field' => 'uuid',
         'id' => 'standard',
diff --git a/modules/ad_track/src/Plugin/Ad/Track/DelayedLocalTracker.php b/modules/ad_track/src/Plugin/Ad/Track/DelayedLocalTracker.php
index e019ca3f54dfa284b54ddb703ffaa7e9f6f96150..fdef5cdd4e21dda82f045482d9d4a376e9e5db5a 100644
--- a/modules/ad_track/src/Plugin/Ad/Track/DelayedLocalTracker.php
+++ b/modules/ad_track/src/Plugin/Ad/Track/DelayedLocalTracker.php
@@ -11,7 +11,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *
  * @Plugin(
  *   id = \Drupal\ad_track\Plugin\Ad\Track\DelayedLocalTracker::TRACKER_ID,
- *   label = @Translation("Queue-based local AD event tracker"),
+ *   label = @Translation("Queue-based local ad event tracker"),
  * )
  *
  * @internal
@@ -52,10 +52,10 @@ class DelayedLocalTracker extends LocalTracker {
   }
 
   /**
-   * Actually saves the specified AD event.
+   * Actually saves the specified ad event.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD to be tracked.
+   *   The ad to be tracked.
    * @param array $values
    *   The event values to be stored.
    *
diff --git a/modules/ad_track/src/Plugin/Ad/Track/LocalTracker.php b/modules/ad_track/src/Plugin/Ad/Track/LocalTracker.php
index 1948d9702d9bc9473536a1c3617d6ab82a5b3328..42ab4092f91000f81fe51ad47d884cce5630177f 100644
--- a/modules/ad_track/src/Plugin/Ad/Track/LocalTracker.php
+++ b/modules/ad_track/src/Plugin/Ad/Track/LocalTracker.php
@@ -24,7 +24,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
  *
  * @Plugin(
  *   id = \Drupal\ad_track\Plugin\Ad\Track\LocalTracker::TRACKER_ID,
- *   label = @Translation("Local AD event tracker"),
+ *   label = @Translation("Local ad event tracker"),
  * )
  *
  * @internal
@@ -139,7 +139,7 @@ class LocalTracker implements TrackerInterface, ContainerFactoryPluginInterface
       $container->get('entity.repository'),
       $container->get('ad_track.total_storage'),
       $container->get('uuid'),
-      $container->get('logger.factory')->get('ad_content')
+      $container->get('logger.factory')->get('ad_track')
     );
   }
 
@@ -224,9 +224,9 @@ class LocalTracker implements TrackerInterface, ContainerFactoryPluginInterface
    * Tracks the specified event.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD to be tracked.
+   *   The ad to be tracked.
    * @param \Drupal\Core\Session\AccountInterface $user
-   *   The user triggering the AD event.
+   *   The user triggering the ad event.
    * @param array $values
    *   The event values to be stored.
    *
@@ -258,10 +258,10 @@ class LocalTracker implements TrackerInterface, ContainerFactoryPluginInterface
   }
 
   /**
-   * Saves the specified AD event.
+   * Saves the specified ad event.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD to be tracked.
+   *   The ad to be tracked.
    * @param array $values
    *   The event values to be stored.
    *
diff --git a/modules/ad_track/src/Plugin/QueueWorker/DelayedLocalTrackQueueWorker.php b/modules/ad_track/src/Plugin/QueueWorker/DelayedLocalTrackQueueWorker.php
index 28918129a087be085a4b29f58f7ae1e165d4d277..e41f5ca6f42d5d5f3db0463a57f29d8b80d256d8 100644
--- a/modules/ad_track/src/Plugin/QueueWorker/DelayedLocalTrackQueueWorker.php
+++ b/modules/ad_track/src/Plugin/QueueWorker/DelayedLocalTrackQueueWorker.php
@@ -23,14 +23,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class DelayedLocalTrackQueueWorker extends QueueWorkerBase implements ContainerFactoryPluginInterface {
 
   /**
-   * The AD bucket factory.
+   * The ad bucket factory.
    *
    * @var \Drupal\ad\Bucket\BucketFactoryInterface
    */
   protected BucketFactoryInterface $bucketFactory;
 
   /**
-   * The AD tracker factory.
+   * The ad tracker factory.
    *
    * @var \Drupal\ad\Track\TrackerFactoryInterface
    */
@@ -46,9 +46,9 @@ class DelayedLocalTrackQueueWorker extends QueueWorkerBase implements ContainerF
    * @param mixed $plugin_definition
    *   The plugin implementation definition.
    * @param \Drupal\ad\Bucket\BucketFactoryInterface $bucket_factory
-   *   The AD bucket factory.
+   *   The ad bucket factory.
    * @param \Drupal\ad\Track\TrackerFactoryInterface $tracker_factory
-   *   The AD tracker factory.
+   *   The ad tracker factory.
    */
   public function __construct(
     array $configuration,
diff --git a/modules/ad_track/src/TotalSqlStorage.php b/modules/ad_track/src/TotalSqlStorage.php
index b4f228d09a10d761c922b230146e863e17bb522d..f9a41addb4fb712be536a1308e4cc3818050a0e2 100644
--- a/modules/ad_track/src/TotalSqlStorage.php
+++ b/modules/ad_track/src/TotalSqlStorage.php
@@ -7,8 +7,7 @@ use Drupal\ad\AdInterface;
 use Drupal\ad\Track\TrackerInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
-use Psr\Log\LoggerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 
 /**
  * Track totals SQL storage backend.
@@ -32,33 +31,23 @@ class TotalSqlStorage implements TotalStorageInterface {
   protected array $transactions = [];
 
   /**
-   * The logger service.
+   * The logger factory service.
    *
-   * @var \Psr\Log\LoggerInterface
+   * @var \Psr\Log\LoggerChannelFactoryInterface
    */
-  protected LoggerInterface $logger;
+  protected LoggerChannelFactoryInterface $loggerFactory;
 
   /**
    * AdTrackTotalSqlStorage constructor.
    *
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection.
-   * @param \Psr\Log\LoggerInterface $logger
+   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerFactory
    *   The logger service.
    */
-  public function __construct(Connection $database, LoggerInterface $logger) {
+  public function __construct(Connection $database, LoggerChannelFactoryInterface $loggerFactory) {
     $this->database = $database;
-    $this->logger = $logger;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, Connection $database) {
-    return new static(
-      $database,
-      $container->get('logger.factory')->get('ad_content'),
-    );
+    $this->loggerFactory = $loggerFactory;
   }
 
   /**
@@ -84,7 +73,7 @@ class TotalSqlStorage implements TotalStorageInterface {
       }
     }
     catch (DatabaseExceptionWrapper $e) {
-      Error::logException($this->logger, $e);
+      Error::logException($this->loggerFactory->get('ad_track'), $e);
     }
 
     return $result;
@@ -94,7 +83,7 @@ class TotalSqlStorage implements TotalStorageInterface {
    * Returns a transaction ID.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD being tracked.
+   *   The ad being tracked.
    *
    * @return string
    *   A transaction ID.
@@ -112,7 +101,7 @@ class TotalSqlStorage implements TotalStorageInterface {
       $this->transactions[$id] = $this->database->startTransaction($id);
     }
     else {
-      $message = sprintf('A transaction already exists for AD %s', $id);
+      $message = sprintf('A transaction already exists for advertisement %s', $id);
       throw new \InvalidArgumentException($message);
     }
   }
diff --git a/modules/ad_track/src/TotalStorageInterface.php b/modules/ad_track/src/TotalStorageInterface.php
index 04eb362478f42fc229c3a69dae4af2525512e967..af64985da4bfa157f120afd47ff341251fcad66e 100644
--- a/modules/ad_track/src/TotalStorageInterface.php
+++ b/modules/ad_track/src/TotalStorageInterface.php
@@ -10,10 +10,10 @@ use Drupal\ad\AdInterface;
 interface TotalStorageInterface {
 
   /**
-   * Loads the totals for the specified AD.
+   * Loads the totals for the specified ad.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   An AD object.
+   *   An ad object.
    *
    * @return int[]
    *   An associative array of event totals keyed by event type.
@@ -21,14 +21,14 @@ interface TotalStorageInterface {
   public function loadTotals(AdInterface $ad): array;
 
   /**
-   * Increases the total impression count for the specified AD.
+   * Increases the total impression count for the specified ad.
    *
    * Always start a transaction before performing this.
    *
    * @param string $type
    *   The event type.
    * @param \Drupal\ad\AdInterface $ad
-   *   An AD object.
+   *   An ad object.
    *
    * @see TotalStorageInterface::startTransaction()
    * @see TotalStorageInterface::rollbackTransaction()
@@ -39,22 +39,22 @@ interface TotalStorageInterface {
   public function increaseTotal(string $type, AdInterface $ad): void;
 
   /**
-   * Starts a transaction before increasing totals for the specified AD.
+   * Starts a transaction before increasing totals for the specified ad.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   An AD object.
+   *   An ad object.
    *
    * @throws \InvalidArgumentException
-   *   If a transaction for the specified AD was already started, nested
+   *   If a transaction for the specified ad was already started, nested
    *   transactions are not supported.
    */
   public function startTransaction(AdInterface $ad): void;
 
   /**
-   * Rolls back a transaction the specified AD.
+   * Rolls back a transaction the specified ad.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   An AD object.
+   *   An ad object.
    */
   public function rollbackTransaction(AdInterface $ad): void;
 
diff --git a/modules/ad_track/tests/src/Functional/AdTrackGenericTest.php b/modules/ad_track/tests/src/Functional/AdTrackGenericTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b4a0a9178c27e38488fd5a9cbebc44a598ec5b7
--- /dev/null
+++ b/modules/ad_track/tests/src/Functional/AdTrackGenericTest.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Tests\ad_track\Functional;
+
+use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
+
+/**
+ * Generic module test for ad_track.
+ *
+ * @group ad_track
+ */
+class AdTrackGenericTest extends GenericModuleTestBase {
+
+  /**
+   * {@inheritDoc}
+   */
+  protected function assertHookHelp(string $module): void {}
+
+}
diff --git a/src/AdFactoryBase.php b/src/AdFactoryBase.php
index 4191b16bd77e706ea522db9b8a201ff1f55bc7a7..00542a196cffb755f5e1e5083d9f87604f083ddf 100644
--- a/src/AdFactoryBase.php
+++ b/src/AdFactoryBase.php
@@ -5,14 +5,14 @@ namespace Drupal\ad;
 use Drupal\Component\Plugin\PluginManagerInterface;
 
 /**
- * Base class for AD factories.
+ * Base class for ad factories.
  *
  * @internal
  */
 abstract class AdFactoryBase implements AdFactoryInterface {
 
   /**
-   * The AD bucket plugin manager.
+   * The ad bucket plugin manager.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
diff --git a/src/AdFactoryInterface.php b/src/AdFactoryInterface.php
index 046b3363468b522a8dee080f8f2cec16a90631a2..a996cc207dd76a18dc686c4340eef4a3cc634f7b 100644
--- a/src/AdFactoryInterface.php
+++ b/src/AdFactoryInterface.php
@@ -3,7 +3,7 @@
 namespace Drupal\ad;
 
 /**
- * Common interface for AD factories.
+ * Common interface for ad factories.
  */
 interface AdFactoryInterface {
 
@@ -11,7 +11,7 @@ interface AdFactoryInterface {
    * Returns a list of buckets.
    *
    * @return string[]
-   *   An associative array of AD bucket labels keyed by ID.
+   *   An associative array of ad bucket labels keyed by ID.
    */
   public function getList(): array;
 
diff --git a/src/AdInterface.php b/src/AdInterface.php
index feb20b75584e00b93228cff47ba1e9f930232277..c6c3db95d5582c9a475add53cf8a512f3218f53e 100644
--- a/src/AdInterface.php
+++ b/src/AdInterface.php
@@ -5,20 +5,20 @@ namespace Drupal\ad;
 use Drupal\Core\Url;
 
 /**
- * Common interface for AD domain objects.
+ * Common interface for ad domain objects.
  */
 interface AdInterface {
 
   /**
-   * Returns the AD identifier.
+   * Returns the ad identifier.
    *
    * @return string
-   *   An string uniquely identifying the AD.
+   *   An string uniquely identifying the ad.
    */
   public function getAdIdentifier(): string;
 
   /**
-   * Returns the ID of the bucket providing the AD.
+   * Returns the ID of the bucket providing the ad.
    *
    * @return string
    *   A bucket machine name.
@@ -26,10 +26,10 @@ interface AdInterface {
   public function getBucketId(): string;
 
   /**
-   * Returns the AD size.
+   * Returns the ad size.
    *
    * @return string
-   *   The AD size ID.
+   *   The ad size ID.
    */
   public function getSizeId(): string;
 
diff --git a/src/Bucket/BucketFactory.php b/src/Bucket/BucketFactory.php
index c1c59d274e3e6f00d02ddc3ffa8196f65e610eb1..d39a7bdf1fff243505e8d90c68a768b08a99ee3e 100644
--- a/src/Bucket/BucketFactory.php
+++ b/src/Bucket/BucketFactory.php
@@ -9,7 +9,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
- * The AD bucket factory.
+ * The ad bucket factory.
  *
  * @internal
  */
@@ -33,7 +33,7 @@ class BucketFactory extends AdFactoryBase implements BucketFactoryInterface {
    * BucketFactory constructor.
    *
    * @param \Drupal\ad\Bucket\BucketPluginManager $plugin_manager
-   *   The AD bucket plugin manager.
+   *   The ad bucket plugin manager.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The config factory.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
@@ -67,7 +67,7 @@ class BucketFactory extends AdFactoryBase implements BucketFactoryInterface {
     }
     catch (PluginException $e) {
     }
-    throw new \LogicException('No valid AD bucket found.');
+    throw new \LogicException('No valid advertisement bucket found.');
   }
 
 }
diff --git a/src/Bucket/BucketFactoryInterface.php b/src/Bucket/BucketFactoryInterface.php
index e037f8751ef9cd7e198997005004a78c0d29cca8..210331e4313ecf098596e581763bbeff83db4f01 100644
--- a/src/Bucket/BucketFactoryInterface.php
+++ b/src/Bucket/BucketFactoryInterface.php
@@ -5,18 +5,18 @@ namespace Drupal\ad\Bucket;
 use Drupal\ad\AdFactoryInterface;
 
 /**
- * Common interface for AD bucket factories.
+ * Common interface for ad bucket factories.
  */
 interface BucketFactoryInterface extends AdFactoryInterface {
 
   /**
-   * Returns the specified AD bucket.
+   * Returns the specified ad bucket.
    *
    * @param string $id
    *   The bucket machine name.
    *
    * @return \Drupal\ad\Bucket\BucketInterface
-   *   An AD bucket instance.
+   *   An ad bucket instance.
    */
   public function get(string $id): BucketInterface;
 
diff --git a/src/Bucket/BucketInterface.php b/src/Bucket/BucketInterface.php
index d3a19eddecd1d004f8da40c4ed5fa1ba20636a2c..5e143ceda7f730cb98051191f9b3ae201becc3ff 100644
--- a/src/Bucket/BucketInterface.php
+++ b/src/Bucket/BucketInterface.php
@@ -7,45 +7,45 @@ use Drupal\ad\Size\SizeInterface;
 use Drupal\ad\Track\TrackerInterface;
 
 /**
- * Common interface for AD buckets.
+ * Common interface for ad buckets.
  */
 interface BucketInterface {
 
   /**
-   * Builds an AD placeholder of the specified size.
+   * Builds an ad placeholder of the specified size.
    *
    * @param \Drupal\ad\Size\SizeInterface $size
-   *   An AD size.
+   *   An ad size.
    *
    * @return array
-   *   A renderable AD placeholder array.
+   *   A renderable ad placeholder array.
    */
   public function buildPlaceholder(SizeInterface $size): array;
 
   /**
-   * Builds an AD of the specified size.
+   * Builds an ad of the specified size.
    *
    * @param \Drupal\ad\Size\SizeInterface $size
-   *   An AD size.
+   *   An ad size.
    *
    * @return array
-   *   An AD render array.
+   *   An ad render array.
    */
   public function buildAd(SizeInterface $size): array;
 
   /**
-   * Returns the specified AD.
+   * Returns the specified ad.
    *
    * @param string $id
-   *   The AD identifier.
+   *   The ad identifier.
    *
    * @return \Drupal\ad\AdInterface|null
-   *   An AD instance or NULL if none could be found.
+   *   An ad instance or NULL if none could be found.
    */
   public function getAd(string $id): ?AdInterface;
 
   /**
-   * Returns the bucket's AD tracker.
+   * Returns the bucket's ad tracker.
    *
    * @return \Drupal\ad\Track\TrackerInterface
    *   A tracker instance.
diff --git a/src/Bucket/BucketPluginManager.php b/src/Bucket/BucketPluginManager.php
index 6f07c51dcbb12d264f429a0d27ad7d5c88f7620f..4d2a3ef9978862d793451a9b15b022020a0fde74 100644
--- a/src/Bucket/BucketPluginManager.php
+++ b/src/Bucket/BucketPluginManager.php
@@ -8,7 +8,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
- * AD bucket plugin manager.
+ * Ad bucket plugin manager.
  *
  * @internal
  */
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
index a70db47a884fe597346432872c4f76cd16874d7d..2c0934748d49382220333c449fab8a66553abd39 100644
--- a/src/Form/SettingsForm.php
+++ b/src/Form/SettingsForm.php
@@ -9,24 +9,23 @@ use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * AD settings configuration form.
+ * Ad settings configuration form.
  */
 class SettingsForm extends ConfigFormBase {
 
   /**
-   * The AD bucket factory.
+   * The ad bucket factory.
    *
    * @var \Drupal\ad\Bucket\BucketFactoryInterface
    */
   protected BucketFactoryInterface $bucketFactory;
 
   /**
-   * The AD tracker factory.
+   * The ad tracker factory.
    *
    * @var \Drupal\ad\Track\TrackerFactoryInterface
    */
@@ -38,9 +37,9 @@ class SettingsForm extends ConfigFormBase {
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The factory for configuration objects.
    * @param \Drupal\ad\Bucket\BucketFactoryInterface $bucket_factory
-   *   The AD bucket factory.
+   *   The ad bucket factory.
    * @param \Drupal\ad\Track\TrackerFactoryInterface $tracker_factory
-   *   The AD tracker factory.
+   *   The ad tracker factory.
    * @param \Drupal\Core\Config\TypedConfigManagerInterface|null $typedConfigManager
    *   The typed config manager.
    */
@@ -86,14 +85,14 @@ class SettingsForm extends ConfigFormBase {
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
     $config = $this->config('ad.settings');
-    $settings = $config->get('trackers');
+    $trackerSettings = $config->get('trackers');
     $trackers = $this->trackerFactory->getList();
     $buckets = $this->bucketFactory->getList();
 
     $form['trackers'] = [
       '#type' => 'fieldset',
-      '#title' => new TranslatableMarkup('Tracker configuration'),
-      '#description' => new TranslatableMarkup('Assign an AD statistics tracker for each AD source.'),
+      '#title' => $this->t('Tracker configuration'),
+      '#description' => $this->t('Assign an advertisement statistics tracker for each advertisement source.'),
       '#tree' => TRUE,
     ];
 
@@ -103,9 +102,7 @@ class SettingsForm extends ConfigFormBase {
           ->toString(TRUE)
           ->getGeneratedUrl(),
       ];
-      $form['trackers']['#description'] = new TranslatableMarkup('You need to <a href="@url">enable</a> at least one AD source and one AD statistics tracker engine.', $args);
-
-      return $form;
+      $form['trackers']['#description'] = $this->t('You need to <a href="@url">enable</a> at least one advertisement source and one advertisement statistics tracker engine.', $args);
     }
 
     foreach ($buckets as $id => $label) {
@@ -114,10 +111,17 @@ class SettingsForm extends ConfigFormBase {
         '#title' => $label,
         '#required' => TRUE,
         '#options' => $trackers,
-        '#default_value' => $settings[$id] ?? key($trackers),
+        '#default_value' => $trackerSettings[$id] ?? key($trackers),
       ];
     }
 
+    $form['advertisement_indicator'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Advertisement indicator'),
+      '#description' => $this->t('In many countries it is mandatory to label advertisements as being such. This setting adds a small print (e.g. "Advertisement") to all advertisement blocks.'),
+      '#default_value' => $config->get('advertisement_indicator') ?? '',
+    ];
+
     return parent::buildForm($form, $form_state);
   }
 
@@ -127,6 +131,7 @@ class SettingsForm extends ConfigFormBase {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->config('ad.settings')
       ->set('trackers', $form_state->getValue('trackers'))
+      ->set('advertisement_indicator', $form_state->getValue('advertisement_indicator'))
       ->save();
 
     parent::submitForm($form, $form_state);
diff --git a/src/Plugin/Ad/Track/NullTracker.php b/src/Plugin/Ad/Track/NullTracker.php
index 2d06aa9f497a8e6fec80e43640eeba16cfaaf873..87ecb0fbd15fe9e0ab6311e8d40ab05a2b3a1159 100644
--- a/src/Plugin/Ad/Track/NullTracker.php
+++ b/src/Plugin/Ad/Track/NullTracker.php
@@ -7,7 +7,7 @@ use Drupal\ad\Track\TrackerInterface;
 use Drupal\Core\Session\AccountInterface;
 
 /**
- * Null AD tracker, useful as a fallback if no actual tracker is available.
+ * Null ad tracker, useful as a fallback if no actual tracker is available.
  *
  * @Plugin(
  *   id = \Drupal\ad\Plugin\Ad\Track\NullTracker::TRACKER_ID,
diff --git a/src/Plugin/Block/AdBlock.php b/src/Plugin/Block/AdBlock.php
index 96d72f591e12bbde2f61b7e9ad5682fce8ceacae..57aea2447760277cbbcd472a6fbbc907d13abfd9 100644
--- a/src/Plugin/Block/AdBlock.php
+++ b/src/Plugin/Block/AdBlock.php
@@ -11,7 +11,7 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * Defines an AD block type.
+ * Defines an ad block type.
  *
  * @Block(
  *  id = "ad",
@@ -23,14 +23,14 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class AdBlock extends BlockBase implements ContainerFactoryPluginInterface {
 
   /**
-   * The AD size factory.
+   * The ad size factory.
    *
    * @var \Drupal\ad\Size\SizeFactory
    */
   protected SizeFactory $sizeFactory;
 
   /**
-   * The AD bucket factory.
+   * The ad bucket factory.
    *
    * @var \Drupal\ad\Bucket\BucketFactoryInterface
    */
diff --git a/src/Plugin/Block/Derivative/AdBlockDeriver.php b/src/Plugin/Block/Derivative/AdBlockDeriver.php
index 31aee6652d1cc087fce81931ca26534a73f1c9de..9ab31cb4c7c0c2eeef745c034ca0a3357953b1a4 100644
--- a/src/Plugin/Block/Derivative/AdBlockDeriver.php
+++ b/src/Plugin/Block/Derivative/AdBlockDeriver.php
@@ -8,12 +8,12 @@ use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * AD block deriver creating a derivative for each AD size.
+ * Ad block deriver creating a derivative for each ad size.
  */
 class AdBlockDeriver extends DeriverBase implements ContainerDeriverInterface {
 
   /**
-   * The AD size factory.
+   * The ad size factory.
    *
    * @var \Drupal\ad\Size\SizeFactory
    */
@@ -23,7 +23,7 @@ class AdBlockDeriver extends DeriverBase implements ContainerDeriverInterface {
    * AdBlockDeriver constructor.
    *
    * @param \Drupal\ad\Size\SizeFactory $ad_size_factory
-   *   The AD size factory.
+   *   The ad size factory.
    */
   public function __construct(SizeFactory $ad_size_factory) {
     $this->sizeFactory = $ad_size_factory;
diff --git a/src/Size/Size.php b/src/Size/Size.php
index ca5d00e2c227c8fc862e98c3ebeb534e2595a8e7..c013dff96ffeaaa2a60c9515c83ca3225818523f 100644
--- a/src/Size/Size.php
+++ b/src/Size/Size.php
@@ -3,21 +3,21 @@
 namespace Drupal\ad\Size;
 
 /**
- * An AD size.
+ * An ad size.
  *
  * @internal
  */
 class Size implements SizeInterface {
 
   /**
-   * The AD size ID.
+   * The ad size ID.
    *
    * @var string
    */
   protected string $id;
 
   /**
-   * The AD size label.
+   * The ad size label.
    *
    * @var string
    */
diff --git a/src/Size/SizeFactory.php b/src/Size/SizeFactory.php
index afd9157d8ae93d96c26addd12739295ade0cca2f..86931279a60b91ef43f0528c5642dd8c305184ba 100644
--- a/src/Size/SizeFactory.php
+++ b/src/Size/SizeFactory.php
@@ -5,27 +5,27 @@ namespace Drupal\ad\Size;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
- * The AD size factory.
+ * The ad size factory.
  *
  * @internal
  */
 class SizeFactory {
 
   /**
-   * All the available AD sizes.
+   * All the available ad sizes.
    *
    * @var \Drupal\ad\Size\SizeInterface[]
    */
   protected array $sizes;
 
   /**
-   * Returns the specified AD size.
+   * Returns the specified ad size.
    *
    * @param string $id
-   *   The AD size machine name.
+   *   The ad size machine name.
    *
    * @return \Drupal\ad\Size\SizeInterface
-   *   An AD size.
+   *   An ad size.
    */
   public function get(string $id): SizeInterface {
     $sizes = $this->getAll();
@@ -36,10 +36,10 @@ class SizeFactory {
   }
 
   /**
-   * Returns all available AD sizes.
+   * Returns all available ad sizes.
    *
    * @return \Drupal\ad\Size\SizeInterface[]
-   *   An array of AD sizes.
+   *   An array of ad sizes.
    */
   public function getAll(): array {
     if (!isset($this->sizes)) {
diff --git a/src/Size/SizeInterface.php b/src/Size/SizeInterface.php
index 35c3f7861d34940bad97275e2008a3c47cf794be..f41f3805e093959ed0ed9737d4a0b0bdc931c691 100644
--- a/src/Size/SizeInterface.php
+++ b/src/Size/SizeInterface.php
@@ -3,17 +3,17 @@
 namespace Drupal\ad\Size;
 
 /**
- * Common interface for AD sizes.
+ * Common interface for ad sizes.
  */
 interface SizeInterface {
 
   /**
-   * The AD size ID.
+   * The ad size ID.
    */
   public function getId(): string;
 
   /**
-   * The AD size human-readable label.
+   * The ad size human-readable label.
    */
   public function getLabel(): string;
 
diff --git a/src/Track/TrackerFactory.php b/src/Track/TrackerFactory.php
index 477a16932c32df9d1eafe109070a5e651bf9f9a7..86bafd1a219bef4e762770cfec5891b5dcc7ef89 100644
--- a/src/Track/TrackerFactory.php
+++ b/src/Track/TrackerFactory.php
@@ -10,7 +10,7 @@ use Drupal\Core\Logger\LoggerChannelFactory;
 use Psr\Log\LoggerInterface;
 
 /**
- * The AD tracker factory.
+ * The ad tracker factory.
  *
  * @internal
  */
@@ -27,7 +27,7 @@ class TrackerFactory extends AdFactoryBase implements TrackerFactoryInterface {
    * TrackerFactory constructor.
    *
    * @param \Drupal\ad\Track\TrackerPluginManager $plugin_manager
-   *   The AD tracker plugin manager.
+   *   The ad tracker plugin manager.
    * @param \Drupal\Core\Logger\LoggerChannelFactory $loggerFactory
    *   The logger factory service.
    */
diff --git a/src/Track/TrackerFactoryInterface.php b/src/Track/TrackerFactoryInterface.php
index 9a587f942c1cff1ccd1511bc9e48dd5c6391daa4..a865ecc41befd4a665c28b1a8c88d3dccf4b72a6 100644
--- a/src/Track/TrackerFactoryInterface.php
+++ b/src/Track/TrackerFactoryInterface.php
@@ -5,18 +5,18 @@ namespace Drupal\ad\Track;
 use Drupal\ad\AdFactoryInterface;
 
 /**
- * Common interface for AD tracker factories.
+ * Common interface for ad tracker factories.
  */
 interface TrackerFactoryInterface extends AdFactoryInterface {
 
   /**
-   * Returns the specified AD tracker.
+   * Returns the specified ad tracker.
    *
    * @param string $name
    *   The tracker machine name.
    *
    * @return \Drupal\ad\Track\TrackerInterface
-   *   An AD tracker instance.
+   *   An ad tracker instance.
    */
   public function get(string $name): TrackerInterface;
 
diff --git a/src/Track/TrackerInterface.php b/src/Track/TrackerInterface.php
index 66e85029ce48c838a78fe7ac7118d5c64558ca2d..853de79fad2ccd829161e69b59d55fa5b4059379 100644
--- a/src/Track/TrackerInterface.php
+++ b/src/Track/TrackerInterface.php
@@ -6,7 +6,7 @@ use Drupal\ad\AdInterface;
 use Drupal\Core\Session\AccountInterface;
 
 /**
- * Common interface for AD event trackers.
+ * Common interface for ad event trackers.
  */
 interface TrackerInterface {
 
@@ -24,12 +24,12 @@ interface TrackerInterface {
   public function id(): string;
 
   /**
-   * Tracks an AD impression.
+   * Tracks an ad impression.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD to be tracked.
+   *   The ad to be tracked.
    * @param \Drupal\Core\Session\AccountInterface $user
-   *   The user triggering the AD event.
+   *   The user triggering the ad event.
    * @param array $context
    *   The context.
    *
@@ -39,12 +39,12 @@ interface TrackerInterface {
   public function trackImpression(AdInterface $ad, AccountInterface $user, array $context = []): ?string;
 
   /**
-   * Tracks an AD click.
+   * Tracks an ad click.
    *
    * @param \Drupal\ad\AdInterface $ad
-   *   The AD to be tracked.
+   *   The ad to be tracked.
    * @param \Drupal\Core\Session\AccountInterface $user
-   *   The user triggering the AD event.
+   *   The user triggering the ad event.
    * @param array $context
    *   The context.
    *
diff --git a/src/Track/TrackerPluginManager.php b/src/Track/TrackerPluginManager.php
index 6ccd86132d7f2eb1d8301c9431b584d0d33ccd00..8a8861b7d87ada664162356567b18899c2cd136d 100644
--- a/src/Track/TrackerPluginManager.php
+++ b/src/Track/TrackerPluginManager.php
@@ -8,7 +8,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
- * AD bucket plugin manager.
+ * Ad bucket plugin manager.
  *
  * @internal
  */
diff --git a/tests/src/Functional/AdGenericTest.php b/tests/src/Functional/AdGenericTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..12a57d1f40a3904d65b1d0970dc32afca76b7d78
--- /dev/null
+++ b/tests/src/Functional/AdGenericTest.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Tests\ad\Functional;
+
+use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
+
+/**
+ * Generic module test for ad.
+ *
+ * @group ad
+ */
+class AdGenericTest extends GenericModuleTestBase {
+
+  /**
+   * {@inheritDoc}
+   */
+  protected function assertHookHelp(string $module): void {}
+
+}