diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3f9daee88888b6166844f4a4ab10b669b4ab5ce5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+codebase.md
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3fd27fcd166de790419efb4ecb34b45772845be4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,163 @@
+# Page Notifications
+
+A Drupal module that enables anonymous and authenticated users to subscribe to content updates and receive email notifications when changes occur.
+
+## CONTENTS OF THIS FILE
+* Introduction
+* Features
+* Requirements
+* Installation
+* Configuration
+* Usage
+* Security
+* API
+* Maintainers
+
+## INTRODUCTION
+
+Page Notifications provides a flexible system for users to subscribe to content changes on your Drupal site. When subscribed content is updated, subscribers receive customizable email notifications about the changes.
+
+## FEATURES
+
+* Email subscription system for any content entity (nodes by default)
+* Configurable email templates with token support
+* Anti-spam protection with multiple options:
+  - Simple math CAPTCHA
+  - reCAPTCHA integration (requires reCAPTCHA module)
+* Subscription management:
+  - Email verification system
+  - Secure unsubscribe links
+  - Automatic cleanup of unverified subscriptions
+  - Subscription migration tools
+* Administration:
+  - Settings interface
+  - Subscription overview and management
+  - Manual notification sending capability
+* Queue-based notification processing
+* Token support for email templates
+* Block-based subscription forms
+* Drupal Views integration
+
+## REQUIREMENTS
+
+This module requires the following:
+* Drupal 10.x
+* Node module (enabled by default)
+* Views module (enabled by default)
+
+Optional but recommended:
+* reCAPTCHA module for enhanced spam protection
+
+## INSTALLATION
+
+1. Install the module via Composer:
+   ```bash
+   composer require drupal/page_notifications
+   ```
+   Or download and extract to your modules directory.
+
+2. Enable the module at `/admin/modules` or via Drush:
+   ```bash
+   drush en page_notifications
+   ```
+
+3. Place the subscription block in your desired region at `/admin/structure/block`. Use block configuration to set visibility as desired.
+
+## CONFIGURATION
+
+All module settings can be configured at `/admin/config/system/page-notifications`:
+
+### Email Settings
+* Configure "From" email address
+* Set verification token expiration time
+* Customize email templates for:
+  - Subscription verification
+  - Update notifications
+
+### Security Settings
+* Toggle email verification requirement
+* Configure unverified subscription cleanup
+* Set spam prevention method:
+  - None
+  - Math CAPTCHA
+  - reCAPTCHA (if module installed)
+
+### Subscription Management
+* View and manage subscriptions
+* Migrate subscriptions between content
+* Send manual notifications
+
+## USAGE
+
+### For Site Builders
+1. Place the subscription block on content types where you want to enable notifications
+2. Configure email templates and security settings
+3. Manage subscriptions through the administrative interface
+
+### For Content Editors
+1. Update content normally
+2. Option to send notifications appears in content edit form next to the revision log
+3. Can include custom notes with notifications
+
+### For Users
+1. Subscribe to content via the subscription block
+2. Receive verification email (if enabled)
+3. Get notifications when content is updated
+4. Unsubscribe via secure links in notification emails
+
+## SECURITY
+
+The module implements several security measures:
+* Email verification system
+* CAPTCHA/reCAPTCHA spam prevention
+* Secure unsubscribe tokens
+* Automatic cleanup of unverified subscriptions
+* Permission-based access control
+
+## Email Customization
+
+### Template Override
+You can override the email template by copying 
+`templates/page-notifications-email-wrapper.html.twig` to your theme and modifying it.
+
+Template suggestions available:
+- page-notifications-email-wrapper--verification.html.twig
+- page-notifications-email-wrapper--notification.html.twig
+- page-notifications-email-wrapper--already-subscribed.html.twig
+
+### Programmatic Customization
+Implement these hooks in your custom module:
+
+```php
+/**
+ * Implements hook_page_notifications_email_variables_alter().
+ */
+function mymodule_page_notifications_email_variables_alter(&$variables) {
+  // Add custom variables to email template
+  $variables['my_variable'] = 'Custom content';
+}
+
+/**
+ * Implements hook_page_notifications_email_footer_alter().
+ */
+function mymodule_page_notifications_email_footer_alter(&$footer) {
+  $footer = 'Custom footer content';
+}
+
+## API
+
+The module provides services and interfaces for developers to:
+* Create and manage subscriptions programmatically
+* Customize notification handling
+* Extend spam prevention mechanisms
+* Integrate with other modules
+
+Key services:
+* `page_notifications.notification_manager`
+* `page_notifications.mail_handler`
+* `page_notifications.spam_prevention`
+
+## MAINTAINERS
+
+Current maintainers:
+* Lidiya Grushetska <grushetskl@chop.edu> is the original author https://www.drupal.org/u/lidia_ua.
\ No newline at end of file
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 8fe310296620591158d60b5782368e3caeb532d8..0000000000000000000000000000000000000000
--- a/README.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-Page Notifications README.txt
-=================
-
-
-CONTENTS OF THIS FILE
----------------------
-
-* Introduction
-* Requirements
-* Installation
-* Configuration
-* Related projects & alternatives
-* Maintainers
-
-
-INTRODUCTION
-------------
-
-Page Notifications is a simple, lightweight module for sending e-mail
-notifications to subscribers about changes on node on a Drupal web
-site.
-
-* For a full description of the module, visit the project page:
-  https://drupal.org/project/page_notifications
-
-* For more documentation about its use, visit the documentation page:
-  https://www.drupal.org/documentation/modules/page_notifications
-
-* To submit bug reports and feature suggestions, or to track changes:
-  https://drupal.org/project/issues/page_notifications
-
-
-REQUIREMENTS
-------------
-
-This module requires a supported version of Drupal 8 or 9 to be running.
-It is highly recommended to use recaptcha module https://www.drupal.org/project/recaptcha
-For correct display of emails allow your mail settings to send HTML format email.
-
-
-INSTALLATION
-------------
-
-1. Extract the Page Notifications module directory, including all its
-   subdirectories, into directory where you keep contributed modules
-   (e.g. /modules/).
-
-2. Enable the Page Notifications module on the Modules list page. The database tables and default data
-    will be created automatically for you at this point.
-
-3. Create three fields on content type that will have notification functionality:
-    3.1 Boolean - field that will enable function to send email to subscribers
-    3.2 Text (plain, long) - field that will contain notes or short message about changes
-    3.3 Timestamp - field will record when last notification emails were sent
-
-4. Go to /admin/page-notifications/tabs » tab "Messages configuration" and enter those three machine name fields into corresponded
-    configuration fields » Save configuration.
-
-4. Place Page Notifications block into the region you would like block to be displayed » Configure » Save.
-
-
-CONFIGURATION
--------------
-
-All configuration for the module is located under /admin/page-notifications/tabs: Admin menu » Extend »
- Page Notifications » Configure.
-
-Fill Out configuration fields under "Messages configuration" tab:
-Page Notifications header text - will be displayed above block form as header of the block;
-The "from" email - enter email if you don't want to use main site emails;
-Checkbox field - the boolean type filed machine name of the field that created on one of the content types
-to enable functionality;
-Notes field - the Text (plain, long) type filed machine name of the field that created on one of the content types
-that contain notes or short message about changes;
-Timestamp - the timestamp type filed machine name of the field that created on one of the content types
-that indicates when last notification emails were sent;
-
-Note: all configuration fields for emails and web messages must be in full HTML format.
-It will automagically save in full HTML format if chosen different format before saving configuration.
-
-RELATED PROJECTS & ALTERNATIVES
--------------------------------
-
-Notify: https://www.drupal.org/project/notify
-
-
-MAINTAINERS
------------
-
-Lidiya Grushetska <grushetskl@chop.edu> is the original author https://www.drupal.org/u/lidia_ua.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..118e852490ce22bf29276422bd977b8efe77d2e5
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,37 @@
+{
+  "name": "drupal/page_notifications",
+  "description": "Enables anonymous and authenticated users to subscribe to content updates and receive email notifications when changes occur.",
+  "type": "drupal-module",
+  "license": "GPL-2.0-or-later",
+  "homepage": "https://drupal.org/project/page_notifications",
+  "authors": [
+    {
+      "name": "Lidiya Grushetska",
+      "homepage": "https://www.drupal.org/u/lidia_ua",
+      "role": "Maintainer"
+    }
+  ],
+  "support": {
+    "issues": "https://drupal.org/project/issues/page_notifications",
+    "source": "https://git.drupalcode.org/project/page_notifications"
+  },
+  "require": {
+    "php": ">=8.1",
+    "drupal/core": "^10.3 || ^11"
+  },
+  "suggest": {
+    "drupal/captcha": "Provides additional spam protection options including reCAPTCHA integration"
+  },
+  "extra": {
+    "drush": {
+      "services": {
+        "drush.services.yml": "^10"
+      }
+    }
+  },
+  "minimum-stability": "dev",
+  "prefer-stable": true,
+  "config": {
+    "sort-packages": true
+  }
+}
\ No newline at end of file
diff --git a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_email.yml b/config/install/field.field.node.page_notify_subscriptions.field_page_notify_email.yml
deleted file mode 100644
index 41639d3f8a13ccfa9c0851f4e7d1c91713ffef76..0000000000000000000000000000000000000000
--- a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_email.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.field_page_notify_email
-    - node.type.page_notify_subscriptions
-id: node.page_notify_subscriptions.field_page_notify_email
-field_name: field_page_notify_email
-entity_type: node
-bundle: page_notify_subscriptions
-label: 'Subscriber E-mail'
-description: ''
-required: false
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings: {  }
-field_type: string
diff --git a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_node_id.yml b/config/install/field.field.node.page_notify_subscriptions.field_page_notify_node_id.yml
deleted file mode 100644
index 37c75b43c740e21b28b5e626b0a88a70c990ef6a..0000000000000000000000000000000000000000
--- a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_node_id.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.field_page_notify_node_id
-    - node.type.page_notify_subscriptions
-id: node.page_notify_subscriptions.field_page_notify_node_id
-field_name: field_page_notify_node_id
-entity_type: node
-bundle: page_notify_subscriptions
-label: 'Subscribed node'
-description: ''
-required: false
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings: {  }
-field_type: string
diff --git a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token.yml b/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token.yml
deleted file mode 100644
index 634b6926bb1b1684db0cec5c92ad4d59a2a77f27..0000000000000000000000000000000000000000
--- a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.field_page_notify_token
-    - node.type.page_notify_subscriptions
-id: node.page_notify_subscriptions.field_page_notify_token
-field_name: field_page_notify_token
-entity_type: node
-bundle: page_notify_subscriptions
-label: 'Token'
-description: ''
-required: false
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings: {  }
-field_type: string
diff --git a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token_user_id.yml b/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token_user_id.yml
deleted file mode 100644
index ac4729b2c9c9360cbc5d5764f4e0bce7d64c92e3..0000000000000000000000000000000000000000
--- a/config/install/field.field.node.page_notify_subscriptions.field_page_notify_token_user_id.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.field_page_notify_token_user_id
-    - node.type.page_notify_subscriptions
-id: node.page_notify_subscriptions.field_page_notify_token_user_id
-field_name: field_page_notify_token_user_id
-entity_type: node
-bundle: page_notify_subscriptions
-label: 'User Token'
-description: ''
-required: false
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings: {  }
-field_type: string
diff --git a/config/install/field.storage.node.field_page_notify_email.yml b/config/install/field.storage.node.field_page_notify_email.yml
deleted file mode 100644
index c55519b75281d4cc7c29cc4b4dd9a75c2c4c1e98..0000000000000000000000000000000000000000
--- a/config/install/field.storage.node.field_page_notify_email.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - node
-  enforced:
-    module:
-      - page_notifications
-id: node.field_page_notify_email
-field_name: field_page_notify_email
-entity_type: node
-type: string
-settings:
-  max_length: 255
-  is_ascii: false
-  case_sensitive: false
-module: core
-locked: false
-cardinality: 1
-translatable: true
-indexes: {  }
-persist_with_no_fields: false
-custom_storage: false
diff --git a/config/install/field.storage.node.field_page_notify_node_id.yml b/config/install/field.storage.node.field_page_notify_node_id.yml
deleted file mode 100644
index da8209f97419e7ee01d8f6a6b771467a3c3bb005..0000000000000000000000000000000000000000
--- a/config/install/field.storage.node.field_page_notify_node_id.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - node
-  enforced:
-    module:
-      - page_notifications
-id: node.field_page_notify_node_id
-field_name: field_page_notify_node_id
-entity_type: node
-type: string
-settings:
-  max_length: 255
-  is_ascii: false
-  case_sensitive: false
-module: core
-locked: false
-cardinality: 1
-translatable: true
-indexes: {  }
-persist_with_no_fields: false
-custom_storage: false
diff --git a/config/install/field.storage.node.field_page_notify_token.yml b/config/install/field.storage.node.field_page_notify_token.yml
deleted file mode 100644
index f6f40c50226d5ebefa57fca52468a719952713fc..0000000000000000000000000000000000000000
--- a/config/install/field.storage.node.field_page_notify_token.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - node
-  enforced:
-    module:
-      - page_notifications
-id: node.field_page_notify_token
-field_name: field_page_notify_token
-entity_type: node
-type: string
-settings:
-  max_length: 255
-  is_ascii: false
-  case_sensitive: false
-module: core
-locked: false
-cardinality: 1
-translatable: true
-indexes: {  }
-persist_with_no_fields: false
-custom_storage: false
diff --git a/config/install/field.storage.node.field_page_notify_token_user_id.yml b/config/install/field.storage.node.field_page_notify_token_user_id.yml
deleted file mode 100644
index 492502f5d13ebfc01d3f38636be48408ee66c1d9..0000000000000000000000000000000000000000
--- a/config/install/field.storage.node.field_page_notify_token_user_id.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - node
-  enforced:
-    module:
-      - page_notifications
-id: node.field_page_notify_token_user_id
-field_name: field_page_notify_token_user_id
-entity_type: node
-type: string
-settings:
-  max_length: 255
-  is_ascii: false
-  case_sensitive: false
-module: core
-locked: false
-cardinality: 1
-translatable: true
-indexes: {  }
-persist_with_no_fields: false
-custom_storage: false
diff --git a/config/install/node.type.page_notify_subscriptions.yml b/config/install/node.type.page_notify_subscriptions.yml
deleted file mode 100644
index d9dc52d2b13db1a51fea939ad9eaa80b8d59b260..0000000000000000000000000000000000000000
--- a/config/install/node.type.page_notify_subscriptions.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  enforced:
-    module:
-      - page_notifications
-name: 'Page Notifications - Subscriptions'
-type: page_notify_subscriptions
-description: 'Subscriptions to pages created by users who would like to be notified.'
-help: 'Do not create nodes for this Content type. They are getting created automaticly by Page Notifications module.'
-new_revision: false
-preview_mode: 1
-display_submitted: true
diff --git a/config/install/page_notifications.settings.yml b/config/install/page_notifications.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..54612b6b4d5f3780bbb9f0d96f99b27a6b243f3f
--- /dev/null
+++ b/config/install/page_notifications.settings.yml
@@ -0,0 +1,32 @@
+notification_settings:
+  from_email: ''
+  token_expiration: 48
+email_templates:
+  verification_subject: 'Verify your subscription to [node:title]'
+  verification_body:
+    value: '<p>Hello,</p><p>Please verify your email subscription to the page "<strong>[node:title]</strong>".</p><p>Click the following link to confirm your subscription:<br><a href="[subscription:verify-url]">[subscription:verify-url]</a></p><p><em>This verification link will expire soon.<br>Please verify your subscription promptly.</em></p><p>If you did not request this subscription, please ignore this email.</p>'
+    format: full_html
+  notification_subject: '[node:title] has been updated'
+  notification_body:
+    value: '<p>Dear subscriber,</p><p>The page "<strong>[node:title]</strong>" that you are subscribed to has been updated.</p><p>[notification:notes]</p><p>You can view the updated page here:<br><a href="[node:url]">[node:url]</a></p><p>To unsubscribe from these notifications, click here:<br><a href="[subscription:unsubscribe-url]">[subscription:unsubscribe-url]</a></p><p>Regards,<br>[site:name] team</p>'
+    format: full_html
+  already_subscribed_subject: 'You are already subscribed to [node:title]'
+  already_subscribed_body:
+    value: '<p>Dear subscriber,</p><p>You are already subscribed to "<strong>[node:title]</strong>" and ready for future notifications.</p><p>You can view the content here:<br><a href="[node:url]">[node:url]</a></p><p>If you wish to unsubscribe, you can do so here:<br><a href="[subscription:unsubscribe-url]">[subscription:unsubscribe-url]</a></p><p>Regards,<br>[site:name] team</p>'
+    format: full_html
+security:
+  require_verification: true
+  flood_control:
+    ip_limit: 200
+    ip_window: 1
+    identifier_limit: 50
+    identifier_window: 1
+spam_protection:
+  enable_modal: false
+  enable_math_captcha: true
+  math_captcha_operator: +
+  captcha_point: null
+spam_prevention:
+  captcha_type: none
+  math_operator: +
+  use_recaptcha: false
\ No newline at end of file
diff --git a/config/install/views.view.page_notification_subscriptions.yml b/config/install/views.view.page_notification_subscriptions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..34b1d81ad2bb58ff3b756f3173fd88bfa938179c
--- /dev/null
+++ b/config/install/views.view.page_notification_subscriptions.yml
@@ -0,0 +1,512 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - node
+    - page_notifications
+id: page_notification_subscriptions
+label: 'Page Notification Subscriptions'
+module: views
+description: 'Lists all page notification subscriptions'
+tag: ''
+base_table: page_notification_subscription
+base_field: id
+display:
+  default:
+    id: default
+    display_title: Default
+    display_plugin: default
+    position: 0
+    display_options:
+      title: 'Page Notification Subscriptions'
+      fields:
+        operations:
+          id: operations
+          table: page_notification_subscription
+          field: operations
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: null
+          entity_field: null
+          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: false
+        email:
+          id: email
+          table: page_notification_subscription
+          field: email
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          entity_field: email
+          plugin_id: standard
+          label: Email
+          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
+        id:
+          id: id
+          table: page_notification_subscription
+          field: id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          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
+        status:
+          id: status
+          table: page_notification_subscription
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          entity_field: status
+          plugin_id: boolean
+          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
+          type: yes-no
+          type_custom_true: ''
+          type_custom_false: ''
+          not: false
+        subscribed_entity_id:
+          id: subscribed_entity_id
+          table: page_notification_subscription
+          field: subscribed_entity_id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          plugin_id: numeric
+          label: 'Subscribed Entity 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
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ''
+          format_plural: false
+          format_plural_string: !!binary MQNAY291bnQ=
+          prefix: ''
+          suffix: ''
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          relationship: subscribed_entity
+          group_type: group
+          admin_label: ''
+          entity_type: node
+          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
+        created:
+          id: created
+          table: page_notification_subscription
+          field: created
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          entity_field: created
+          plugin_id: date
+          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
+          date_format: 'raw time ago'
+          custom_date_format: ''
+          timezone: ''
+      pager:
+        type: mini
+        options:
+          offset: 0
+          pagination_heading_level: h4
+          items_per_page: 10
+          total_pages: null
+          id: 0
+          tags:
+            next: ››
+            previous: ‹‹
+          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
+      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: none
+        options: {  }
+      cache:
+        type: tag
+        options: {  }
+      empty: {  }
+      sorts: {  }
+      arguments: {  }
+      filters: {  }
+      style:
+        type: table
+      row:
+        type: fields
+      query:
+        type: views_query
+        options:
+          query_comment: ''
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_tags: {  }
+      relationships:
+        subscribed_entity:
+          id: subscribed_entity
+          table: page_notification_subscription
+          field: subscribed_entity
+          relationship: none
+          group_type: group
+          admin_label: 'Subscribed Node'
+          entity_type: page_notification_subscription
+          plugin_id: standard
+          required: false
+      header: {  }
+      footer: {  }
+      display_extenders: {  }
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+      tags: {  }
+  page_1:
+    id: page_1
+    display_title: Page
+    display_plugin: page
+    position: 1
+    display_options:
+      display_extenders:
+        simple_sitemap_display_extender:
+          variants: {  }
+      path: admin/content/subscriptions
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+      tags: {  }
diff --git a/config/install/views.view.top_subscribed_content.yml b/config/install/views.view.top_subscribed_content.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c36259791a376b7dcecfa43115b9e0223c0f259a
--- /dev/null
+++ b/config/install/views.view.top_subscribed_content.yml
@@ -0,0 +1,326 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - node
+    - page_notifications
+id: top_subscribed_content
+label: 'Top Subscribed Content'
+module: views
+description: ''
+tag: ''
+base_table: page_notification_subscription
+base_field: id
+display:
+  default:
+    id: default
+    display_title: Default
+    display_plugin: default
+    position: 0
+    display_options:
+      title: 'Top Subscribed Content'
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          relationship: subscribed_entity
+          group_type: group
+          admin_label: ''
+          entity_type: node
+          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
+        id:
+          id: id
+          table: page_notification_subscription
+          field: id
+          relationship: none
+          group_type: count
+          admin_label: ''
+          entity_type: page_notification_subscription
+          entity_field: id
+          plugin_id: field
+          label: 'Subscription Count'
+          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: {  }
+          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
+          set_precision: false
+          precision: 0
+          decimal: .
+          format_plural: 0
+          format_plural_string: !!binary MQNAY291bnQ=
+          prefix: ''
+          suffix: ''
+      pager:
+        type: mini
+        options:
+          offset: 0
+          pagination_heading_level: h4
+          items_per_page: 10
+          total_pages: null
+          id: 0
+          tags:
+            next: ››
+            previous: ‹‹
+          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
+      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: none
+        options: {  }
+      cache:
+        type: tag
+        options: {  }
+      empty: {  }
+      sorts: {  }
+      arguments: {  }
+      filters:
+        status:
+          id: status
+          table: page_notification_subscription
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          entity_type: page_notification_subscription
+          entity_field: status
+          plugin_id: boolean
+          operator: '='
+          value: '1'
+          group: 1
+          exposed: false
+          expose:
+            operator_id: ''
+            label: ''
+            description: ''
+            use_operator: false
+            operator: ''
+            operator_limit_selection: false
+            operator_list: {  }
+            identifier: ''
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+      style:
+        type: table
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          columns:
+            title: title
+            id: id
+            title_1: title_1
+          default: id
+          info:
+            title:
+              sortable: true
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            id:
+              sortable: true
+              default_sort_order: desc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            title_1:
+              sortable: false
+              default_sort_order: asc
+              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:
+        subscribed_entity:
+          id: subscribed_entity
+          table: page_notification_subscription
+          field: subscribed_entity
+          relationship: none
+          group_type: group
+          admin_label: 'Subscribed Node'
+          entity_type: page_notification_subscription
+          plugin_id: standard
+          required: true
+      group_by: true
+      header: {  }
+      footer: {  }
+      display_extenders:
+        simple_sitemap_display_extender: {  }
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+      tags: {  }
+  page_1:
+    id: page_1
+    display_title: Page
+    display_plugin: page
+    position: 1
+    display_options:
+      display_extenders:
+        simple_sitemap_display_extender:
+          variants: {  }
+      path: admin/content/top-subscribed
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+      tags: {  }
diff --git a/config/schema/page_notifications.schema.yml b/config/schema/page_notifications.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de7a70a4a0d2f8f554bb26e6455111c6a3389497
--- /dev/null
+++ b/config/schema/page_notifications.schema.yml
@@ -0,0 +1,68 @@
+page_notifications.settings:
+  type: config_object
+  label: 'Page Notifications settings'
+  mapping:
+    notification_settings:
+      type: mapping
+      mapping:
+        from_email:
+          type: string
+          label: 'From email address'
+        token_expiration:
+          type: integer
+          label: 'Token expiration time in hours (0 = never expire)'
+    email_templates:
+      type: mapping
+      label: 'Email Templates'
+      mapping:
+        verification_subject:
+          type: string
+          label: 'Verification email subject'
+        verification_body:
+          type: text_format
+          label: 'Verification email body'
+        notification_subject:
+          type: string
+          label: 'Notification email subject'
+        notification_body:
+          type: text_format
+          label: 'Notification email body'
+    security:
+      type: mapping
+      label: 'Security Settings'
+      mapping:
+        require_verification:
+          type: boolean
+          label: 'Require email verification'
+        flood_control:
+          type: mapping
+          label: 'Flood Control Settings'
+          mapping:
+            ip_limit:
+              type: integer
+              label: 'IP-based attempt limit'
+            ip_window:
+              type: integer
+              label: 'IP-based time window (hours)'
+            identifier_limit:
+              type: integer
+              label: 'Email-based attempt limit'
+            identifier_window:
+              type: integer
+              label: 'Email-based time window (hours)'
+    spam_protection:
+      type: mapping
+      label: 'Spam Protection Settings'
+      mapping:
+        enable_modal:
+          type: boolean
+          label: 'Enable modal dialog'
+        enable_math_captcha:
+          type: boolean
+          label: 'Enable math captcha'
+        math_captcha_operator:
+          type: string
+          label: 'Math captcha operator'
+        captcha_point:
+          type: string
+          label: 'CAPTCHA point'
\ No newline at end of file
diff --git a/css/modal.css b/css/modal.css
new file mode 100644
index 0000000000000000000000000000000000000000..133c457ef63174fadd647ac1da0006643d3d83cd
--- /dev/null
+++ b/css/modal.css
@@ -0,0 +1,53 @@
+.ui-widget-overlay {
+    background: #000;
+    opacity: .5;
+    filter: Alpha(Opacity=50);
+}
+.ui-dialog {
+  background-color: #ffffff;
+  border-radius: 0.5rem;
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-lg */
+  width: 100%;
+  max-width: 32rem;
+  margin: 1.5rem;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+
+.ui-dialog-titlebar {
+  background-color: #1f2937;
+  color: #ffffff;
+  border-top-left-radius: 0.5rem;
+  border-top-right-radius: 0.5rem;
+  padding: 0.5rem 1rem;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.ui-dialog-title {
+  font-weight: 600;
+}
+
+.ui-dialog-titlebar-close {
+  background: none;
+  border: none;
+  color: #ffffff;
+  font-size: 1.25rem;
+  cursor: pointer;
+}
+.ui-dialog-titlebar-close::before {
+  content: "\00d7"; /* Unicode for 'X' */
+  font-size: 1.25rem;
+  color: #ffffff;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+
+.ui-dialog-content {
+  padding: 1rem;
+}
\ No newline at end of file
diff --git a/js/modal.js b/js/modal.js
new file mode 100644
index 0000000000000000000000000000000000000000..3c63602dd4dc9476a8119ea71e08661a3b2bfa59
--- /dev/null
+++ b/js/modal.js
@@ -0,0 +1,11 @@
+(function ($, Drupal) {
+  Drupal.behaviors.pageNotificationsModal = {
+    attach: function (context, settings) {
+      once('pageNotificationsModal', '[data-drupal-selector="modal-close"]', context).forEach(function (button) {
+        $(button).on('click', function () {
+          $(this).closest('.ui-dialog-content').dialog('close');
+        });
+      });
+    }
+  };
+})(jQuery, Drupal);
\ No newline at end of file
diff --git a/js/page_notifications.js b/js/page_notifications.js
deleted file mode 100644
index a044f7ae209554305b177a8282e57997375239e4..0000000000000000000000000000000000000000
--- a/js/page_notifications.js
+++ /dev/null
@@ -1,28 +0,0 @@
-(function($){
-
-  var checkbox = document.getElementById("edit-pagenotifycheckall");
-  var checkboxs = document.getElementsByClassName("form-checkbox");
-
-  if (checkbox) {
-    checkbox.addEventListener("click", checkUncheck);
-  }
-
-  function checkUncheck() {
-    if (checkbox.checked == false){
-      for (i = 0; i < checkboxs.length; i++) {
-        var target = document.getElementById('edit-page-notifications-list-'+i+'-field-page-notify-node-id');
-        if (target !== null){
-          target.checked = false;
-        }
-      }
-    } else {
-      for (i = 0; i < checkboxs.length; i++) {
-        var target = document.getElementById('edit-page-notifications-list-'+i+'-field-page-notify-node-id');
-        if (target !== null){
-          target.checked = true;
-        }
-      }
-    }
-  }
-
-})(jQuery);
diff --git a/page_notifications.info.yml b/page_notifications.info.yml
index b113093f09b00284f8eb8a901844bcdf7e0d7289..68108751098aa315b413e00c39b1781162242824 100644
--- a/page_notifications.info.yml
+++ b/page_notifications.info.yml
@@ -1,27 +1,11 @@
 name: Page Notifications
 type: module
-description: 'Anonymous users can subscribe to a page to receive notifications about updates about page/node.'
-core_version_requirement: ^8 || ^9 || ^10
-configure: page_notifications.tabs
+description: 'Anonymous users can subscribe to pages to receive notifications about updates.'
+core_version_requirement: ^10.3 || ^11
+configure: page_notifications.settings
 dependencies:
   - drupal:node
-
-# Information added by Drupal.org packaging script on 2020-11-18
-# version: '3.0.2'
-# project: 'page_notifications'
-# datestamp: 1605715872
-
-# Information added by Drupal.org packaging script on 2021-10-26
-# version: '2.0.2'
-# project: 'page_notifications'
-# datestamp: 1635249489
-
-# Information added by Drupal.org packaging script on 2023-08-07
-# version: '2.1.1'
-# project: 'page_notifications'
-# datestamp: 1691431686
-
-# Information added by Drupal.org packaging script on 2023-09-25
-version: '2.1.2'
-project: 'page_notifications'
-datestamp: 1695669145
+  - drupal:views
+  - token:token
+suggestions:
+  - captcha:captcha
\ No newline at end of file
diff --git a/page_notifications.install b/page_notifications.install
index 4e3e355b571183dba44beb3a744bf86badea06d2..808d6be01e00cfea3f184128827a98fbae8a0a59 100644
--- a/page_notifications.install
+++ b/page_notifications.install
@@ -1,393 +1,213 @@
 <?php
 
-use \Drupal\field\Entity\FieldStorageConfig;
-use \Drupal\field\Entity\FieldConfig;
-use Drupal\Core\Database\Database;
-
+use Drupal\Core\Config\FileStorage;
+use Drupal\page_notifications\Service\MigrationService;
 
 /**
  * @file
- * Install, update and uninstall functions for the Page Notifications module.
+ * Install, update and uninstall functions for the page_notifications module.
  */
 
 /**
- *  hook_install()
+ * Implements hook_install().
  */
-function page_notifications_install()
-{
-
-  $moduleHandler = \Drupal::service('module_handler');
-  $page_notify_recaptcha = ($moduleHandler->moduleExists('recaptcha')) ? 1 : 0;
-
-  $settings_query = \Drupal::database()->insert('page_notify_settings')
-    ->fields([
-      'page_notify_settings_group_name' => 'page_notify_general_settings',
-      'page_notify_recaptcha' => $page_notify_recaptcha,
-      'page_notify_captcha' => 0,
-      'page_notify_subscribers_count' => 1,
-      'enable_message_subscription_not_available' => 1,
-      'page_notify_settings_enable_content_type' => 0,
-      'page_notify_settings_enable_view' => 0,
-    ]);
-  $settings_query->execute();
-
-  $request_time = Drupal::time()->getRequestTime();
-  $template_query = \Drupal::database()->insert('page_notify_email_template')
-    ->fields([
-      'body' => '<p>Subscribe to: [notify_node_title]</p>',
-      'from_email' => '',
-      'checkbox_field' => '',
-      'notes_field' => '',
-      'node_timestamp' => '',
-      'created' => $request_time,
-      'verification_email_subject' => 'Subscription Confirmation – [notify_node_title]',
-      'verification_email_text' => '<p>Hello [notify_user_email],</p>
-          <p>Please <a href="[notify_verify_url]">confirm your subscription</a>.</p>
-          <p>Once complete, you will receive a “Now Subscribed” email notification.</p>
-          <p>Thank you!</p>',
-      'confirmation_email_subject' => 'You are now subscribed to - [notify_node_title]',
-      'confirmation_email_text' => '<p>Hello [notify_user_email],</p>
-          <p>You are now subscribed to <a href="[notify_node_url]">[notify_node_title]</a>.<br />
-          <a href="[notify_unsubscribe_url]">Unsubscribe</a> or visit <a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>
-          <p>Thank you!</p>',
-      'sent_verify_web_page_message' => '<p>Hey [notify_user_email],</p>
-           <p>Please check your email to finalize your subscription!</p>
-           <p style="font-size:9px">*If you didn’t get an e-mail, please check the spam folder</p>',
-      'record_exist_verify_web_page_message' => '<p>Hey [notify_user_email],</p>
-          <p>You already subscribed to this page!</p>
-          <p><a href="[notify_unsubscribe_url]">Unsubscribe from this page</a></p>',
-      'error_web_page_message' => '<p>Hey [notify_user_email],</p><p>There was an error on this page!</p>',
-      'subscription_not_available_web_page_message' => '<p>Subscription is not available for this page.</p>',
-      'confirmation_web_page_message' => '<p>Hey [notify_user_email],</p>
-          <p>You are all set!</p>
-          <p>Thank you for subscribing!</p>
-          <p><a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>',
-      'general_email_template_subject' => '[notify_node_title] – Notification of New Update',
-      'general_email_template' => '<p>Hello [notify_user_email],</p>
-          <p>The "<a href="[notify_node_url]">[notify_node_title]</a>." has been updated.<br />
-          If you would like to unsubscribe to this page please go <a href="[notify_user_email]">here</a> or visit <a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>
-          <p>[notify_notes]</p>
-          <p>Thank you!</p>',
-    ]);
-  $template_query->execute();
+function page_notifications_install() {
+  // Try to find the best available text format
+  $preferred_formats = ['email', 'easy_email', 'full_html', 'basic_html', 'restricted_html', 'plain_text'];
+  $selected_format = 'plain_text'; // Default fallback
+
+  $format_storage = \Drupal::entityTypeManager()->getStorage('filter_format');
+  foreach ($preferred_formats as $format_id) {
+    if ($format = $format_storage->load($format_id)) {
+      $selected_format = $format_id;
+      break;
+    }
+  }
+  // Set Dynamic configuration for verification, notification and already_subscribed formats
+  $config = \Drupal::configFactory()->getEditable('page_notifications.settings');
+ // Set default body values with selected format
+ $config
+ ->set('email_templates.verification_body', [
+   'value' => $config->get('email_templates.verification_body.value'),
+   'format' => $selected_format,
+ ])
+ ->set('email_templates.notification_body', [
+   'value' => $config->get('email_templates.notification_body.value'),
+   'format' => $selected_format,
+ ])
+ ->set('email_templates.already_subscribed_body', [
+   'value' => $config->get('email_templates.already_subscribed_body.value'),
+   'format' => $selected_format,
+ ])
+ ->save();
 
 }
 
 /**
  * Implements hook_schema().
  */
-function page_notifications_schema()
-{
+function page_notifications_schema() {
   $schema = [];
-  $schema['page_notify_email_template'] = [
-    'description' => 'Table of accounts subscribed for notifications.',
-    'fields' => [
-      'template_id' => [
-        'description' => 'The primary identifier for submition.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ],
-      'body' => [
-        'description' => 'Header of Block Notify.',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'from_email' => [
-        'description' => 'From email',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'checkbox_field' => [
-        'description' => 'Checkbox field of the node',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'notes_field' => [
-        'description' => 'Notes field of the node',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'node_timestamp' => [
-        'description' => 'Timestamp field of the node',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'created' => [
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => 0,
-      ],
-      'verification_email_subject' => [
-        'description' => 'Verification Email Subject.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'verification_email_text' => [
-        'description' => 'Verification email',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'confirmation_email_subject' => [
-        'description' => 'Confirmation Email Subject.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'confirmation_email_text' => [
-        'description' => 'Confirmation email',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'sent_verify_web_page_message' => [
-        'description' => 'Web message that verification email sent.',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'record_exist_verify_web_page_message' => [
-        'description' => 'Web message that record exist.',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'error_web_page_message' => [
-        'description' => 'Web error message.',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'subscription_not_available_web_page_message' => [
-        'description' => 'Web message when subscription is not available.',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'confirmation_web_page_message' => [
-        'description' => 'Confirmation web message',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'general_email_template_subject' => [
-        'description' => 'Subject of the email.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-      'general_email_template' => [
-        'description' => 'Confirmation email',
-        'type' => 'text',
-        'length' => 255,
-        'not null' => FALSE,
-      ],
-    ],
-    'primary key' => ['template_id'],
-  ];
-
-  $schema['page_notify_settings'] = [
-    'description' => 'Page Notifications settings.',
-    'fields' => [
-      'page_notify_id' => [
-        'description' => 'The primary identifier settings.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ],
-      'page_notify_settings_group_name' => [
-        'description' => 'Machine name of the settings group',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'page_notify_recaptcha' => [
-        'description' => 'Enable/Disable recaptcha',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'page_notify_captcha' => [
-        'description' => 'Enable/Disable Captcha',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'page_notify_subscribers_count' => [
-        'description' => 'Show number of subscribers on node edit',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'enable_message_subscription_not_available' => [
-        'description' => 'Display message when subscriptions are not avaliable.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-      ],
-      'page_notify_settings_enable_content_type' => [
-        'description' => 'Enable/Disable content type functionality.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'page_notify_settings_enable_view' => [
-        'description' => 'Enable/Disable views functionality.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-
-    ],
-    'primary key' => ['page_notify_id'],
-  ];
-
+  // Add any additional database tables needed beyond entities
   return $schema;
 }
 
 /**
- * Uninstall Field UI.
- */
-function page_notifications_update_8001(&$sandbox)
-{
-
-  \Drupal::service('module_installer')->uninstall(['page_notifications']);
-  $result = \Drupal::entityQuery("node")
-    ->condition("type", "subscriptions")
-    ->accessCheck(FALSE)
-    ->execute();
-  $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-  $entities = $storage_handler->loadMultiple($result);
-  $storage_handler->delete($entities);
-}
-
-/**
- * Summary of page_notifications_update_9001
- * @param array $sandbox
- * @return void
+ * Implements hook_uninstall().
  */
-function page_notifications_update_9001(array &$sandbox)
-{
-  $spec = [
-    'description' => 'Page Notifications settings',
-    'fields' => [
-      'page_notify_id' => [
-        'description' => 'The primary identifier for settings.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ],
-      'page_notify_settings_group_name' => [
-        'description' => 'Machine name of the settings group',
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => FALSE,
-      ],
-      'page_notify_recaptcha' => [
-        'description' => 'Enable/Disable recaptcha',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'page_notify_captcha' => [
-        'description' => 'Enable/Disable Captcha',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'page_notify_subscribers_count' => [
-        'description' => 'Show number of subscribers on node edit',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ],
-      'enable_message_subscription_not_available' => [
-        'description' => 'Display message when subscriptions are not avaliable.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-      ],
-      'page_notify_settings_enable_content_type' => [
-        'description' => 'Enable/Disable content type functionality.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-      ],
-      'page_notify_settings_enable_view' => [
-        'description' => 'Enable/Disable views functionality.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-      ],
-    ],
-    'primary key' => ['page_notify_id'],
+function page_notifications_uninstall() {
+  // Remove configuration
+  $config_factory = \Drupal::configFactory();
+
+  // List all config objects that need to be removed
+  $config_names = [
+    'page_notifications.settings',
+    'views.view.page_notification_subscriptions',
+    'views.view.top_subscribed_content',
   ];
-  $schema = Database::getConnection()->schema();
-  $schema->createTable('page_notify_settings', $spec);
-
-  $moduleHandler = \Drupal::service('module_handler');
-  $page_notify_recaptcha = ($moduleHandler->moduleExists('recaptcha')) ? 1 : 0;
-
-  $query = \Drupal::database()->insert('page_notify_settings')
-    ->fields([
-      'page_notify_settings_group_name' => 'page_notify_general_settings',
-      'page_notify_recaptcha' => $page_notify_recaptcha,
-      'page_notify_captcha' => 0,
-      'page_notify_subscribers_count' => 1,
-      'enable_message_subscription_not_available' => 1,
-      'page_notify_settings_enable_content_type' => 0,
-      'page_notify_settings_enable_view' => 0,
-    ]);
-  $query->execute();
-}
+  // remove mail system handler for page notifications
+  $settings = \Drupal::configFactory()->getEditable('mailsystem.settings');
+  $settings->clear('modules.page_notifications.none')->save();
 
-/**
- * Summary of page_notifications_update_9002
- * @param array $sandbox
- * @return void
- */
-function page_notifications_update_9002(array &$sandbox)
-{
-  $spec = [
-    'description' => 'Enable/Disable captcha',
-    'type' => 'int',
-    'unsigned' => TRUE,
-    'not null' => TRUE,
-    'default' => 0,
-  ];
-  $schema = Database::getConnection()->schema();
-  $schema->addField('page_notify_settings', 'page_notify_captcha', $spec);
+  foreach ($config_names as $config_name) {
+    $config_factory->getEditable($config_name)->delete();
+  }
 
+  // Clean up state
+  \Drupal::state()->delete('page_notifications_v3_backup');
 }
 
-/*
- * Implementation of hook_uninstall()
+/**
+ * Install new schema, views, default configuration, and migrate subscriptions from v3 to v4.
  */
+function page_notifications_update_10001(&$sandbox) {
+  // First ensure the new schema is installed
+  $entity_type = \Drupal::entityTypeManager()->getDefinition('page_notification_subscription');
+  \Drupal::service('entity_type.listener')->onEntityTypeCreate($entity_type);
+
+  // Check if this is a migration or fresh install by looking for v3 tables
+  $schema = \Drupal::database()->schema();
+  $has_v3_data = $schema->tableExists('page_notify_settings') &&
+                 $schema->tableExists('page_notify_email_template');
+
+  // Install required views and configuration regardless of migration status
+  $module_path = \Drupal::service('extension.list.module')->getPath('page_notifications');
+  $source = new FileStorage($module_path . '/config/install');
+  $config_storage = \Drupal::service('config.storage');
+
+  // List of all configurations to install
+  $configs = [
+    'views.view.page_notification_subscriptions',
+    'views.view.top_subscribed_content',
+    'page_notifications.settings',
+  ];
 
-function page_notifications_uninstall()
-{
-  $db_connection = \Drupal::database();
-  $db_connection->schema()->dropTable('page_notify_settings');
-  $db_connection->schema()->dropTable('page_notify_email_template');
-
+  foreach ($configs as $config_name) {
+    $config_record = $source->read($config_name);
+    if (is_array($config_record)) {
+      $config_storage->write($config_name, $config_record);
+      \Drupal::logger('page_notifications')->notice('Installed configuration: @config', ['@config' => $config_name]);
+    }
+    else {
+      \Drupal::logger('page_notifications')->error('Failed to read configuration: @config', ['@config' => $config_name]);
+    }
+  }
+
+  // Only attempt migration if v3 tables exist
+  if ($has_v3_data) {
+    try {
+      // Get v3 settings
+      $v3_settings = \Drupal::database()->select('page_notify_settings', 'pns')
+        ->fields('pns')
+        ->execute()
+        ->fetchAssoc();
+
+      $v3_template = \Drupal::database()->select('page_notify_email_template', 'pnet')
+        ->fields('pnet')
+        ->execute()
+        ->fetchAssoc();
+
+      // Store v3 data
+      \Drupal::state()->set('page_notifications_v3_backup', [
+        'settings' => $v3_settings,
+        'template' => $v3_template,
+      ]);
+
+      // Map settings to v4
+      $config = \Drupal::configFactory()->getEditable('page_notifications.settings');
+
+      // Migrate settings
+      if ($v3_template && $v3_settings) {
+        // Map email settings
+        $config->set('notification_settings.from_email', $v3_template['from_email'] ?? '');
+
+        /**
+         * Converts v3 tokens to v4 format in a string.
+         */
+        function page_notifications_convert_tokens($text) {
+          $token_map = [
+            '[notify_user_email]' => '[subscription:email]',
+            '[notify_verify_url]' => '[subscription:verify-url]',
+            '[notify_unsubscribe_url]' => '[subscription:unsubscribe-url]',
+            '[notify_node_title]' => '[node:title]',
+            '[notify_node_url]' => '[node:url]',
+            '[notify_notes]' => '[notification:notes]',
+            // Remove or convert deprecated tokens
+            '[notify_user_name]' => '',
+            '[notify_subscribe_url]' => '',
+            '[notify_user_subscribtions]' => '',
+          ];
+
+          return str_replace(
+            array_keys($token_map),
+            array_values($token_map),
+            $text
+          );
+        }
+
+        // Map CAPTCHA settings
+        if (!empty($v3_settings['page_notify_recaptcha'])) {
+          $config->set('spam_prevention.captcha_type', 'recaptcha');
+          $config->set('spam_prevention.use_recaptcha', TRUE);
+        }
+        elseif (!empty($v3_settings['page_notify_captcha'])) {
+          $config->set('spam_prevention.captcha_type', 'math');
+        }
+
+        // Map email templates with token conversion
+        $config->set('email_templates.verification_subject',
+        page_notifications_convert_tokens($v3_template['verification_email_subject'] ?? ''));
+
+        $config->set('email_templates.verification_body',
+        page_notifications_convert_tokens($v3_template['verification_email_text'] ?? ''));
+
+        $config->set('email_templates.notification_subject',
+        page_notifications_convert_tokens($v3_template['general_email_template_subject'] ?? ''));
+
+        $config->set('email_templates.notification_body',
+        page_notifications_convert_tokens($v3_template['general_email_template'] ?? ''));
+
+        $config->save();
+
+        \Drupal::logger('page_notifications')->notice('Migrated settings from v3 to v4.');
+        \Drupal::messenger()->addWarning(t('Email templates have been migrated from v3 to v4. Please review your templates as some tokens have changed. See documentation for the new token format.'));
+
+      }
+
+      // Set up batch migration for subscriptions
+      $batch = MigrationService::createMigrationBatch();
+      if ($batch) {
+        batch_set($batch);
+      }
+    }
+    catch (\Exception $e) {
+      \Drupal::logger('page_notifications')->error('Error during v3 to v4 migration: @message', [
+        '@message' => $e->getMessage()
+      ]);
+    }
+  }
+
+  return $has_v3_data ?
+    t('Subscription data has been migrated, views and default configuration have been installed. Please review your Page Notifications settings at /admin/config/system/page-notifications') :
+    t('Page Notifications v4 has been installed with default configuration.');
 }
diff --git a/page_notifications.libraries.yml b/page_notifications.libraries.yml
index 81dc8543ac32d478178164fe55c5a9e63af1ba9e..9ef4be7124dae3801240db497970c97ee7dfceb2 100644
--- a/page_notifications.libraries.yml
+++ b/page_notifications.libraries.yml
@@ -1,7 +1,11 @@
-page_notifications:
-  js:
-    js/page_notifications.js: {}
-recaptcha:
+modal:
   version: 1.x
+  css:
+    theme:
+      css/modal.css: {}
   js:
-    https://www.google.com/recaptcha/api.js: { type: external }
+    js/modal.js: {}
+  dependencies:
+    - core/drupal.dialog.ajax
+    - core/jquery
+    - core/once
\ No newline at end of file
diff --git a/page_notifications.links.menu.yml b/page_notifications.links.menu.yml
index d1dd26a83d202fa0215ae1846f9a9fe0541986ac..d4a54d75c84c96c7236ec48982ab848efaa22301 100644
--- a/page_notifications.links.menu.yml
+++ b/page_notifications.links.menu.yml
@@ -1,78 +1,13 @@
-#page_notifications:
-#  title: 'Page Notifications'
-#  description: 'Simplest possible menu type, and the parent menu entry for others'
-#  expanded: 1
-#  route_name: page_notifications
-
-#page_notifications.alternate_menu:
-#  title: 'Page Notifications: Menu in alternate menu'
-#   #If menu_name is omitted, the "Tools" menu will be used.
-#  menu_name: 'main'
-#  route_name: page_notifications.alternate_menu
-
-#page_notifications.permissioned:
-#  title: 'Permissioned Notifications'
-#  parent: page_notifications
-#  expanded: 1
-#  route_name: page_notifications.permissioned
-#  weight: 10
-
-#page_notifications.permissioned_controlled:
-#  title: 'Permissioned Menu Item'
-#  parent: page_notifications.permissioned
-#  route_name: page_notifications.permissioned_controlled
-#  weight: 10
-
-#page_notifications.custom_access:
-#  title: 'Custom Access Notifications'
-#  parent: page_notifications
-#  expanded: 1
-#  route_name: page_notifications.custom_access
-#  weight: -5
-
-#page_notifications.custom_access_page:
-#  title: 'Custom Access Menu Item'
-#  parent: page_notifications.custom_access
-#  route_name: page_notifications.custom_access_page
-
-#page_notifications.route_only:
-#  title: 'Route only notifications'
-#  parent: page_notifications
-#  route_name: page_notifications.route_only
-#  weight: 20
-
-page_notifications.tabs:
-  title: 'Tabs'
-  description: 'Shows how to create primary and secondary tabs'
-#  parent: page_notifications
-  parent: system.admin_structure
-  route_name: page_notifications.tabs
-  weight: 30
-
-#page_notifications.use_url_arguments:
-#  title: 'URL Arguments'
-#  description: 'The page callback can use the arguments provided after the path used as key'
-#  parent: page_notifications
-#  route_name: page_notifications.use_url_arguments
-#  weight: 40
-
-#page_notifications.title_callbacks:
-#  title: 'Dynamic title'
-#  description: 'The title of this menu item is dynamically generated'
-#  parent: page_notifications
-#  route_name: page_notifications.title_callbacks
-#  weight: 50
-
-#page_notifications.placeholder_argument:
-#  title: Placeholder Arguments
-#  description: ''
-#  parent: 'page_notifications'
-#  route_name: page_notifications.placeholder_argument
-#  weight: 60
-
-#page_notifications.path_override:
-#  title: Path Override
-#  description: ''
-#  parent: 'page_notifications'
-#  route_name: page_notifications.path_override
-#  weight: 70
+page_notifications.settings:
+  title: 'Page Notifications'
+  description: 'Configure Page Notifications settings'
+  parent: system.admin_config_system
+  route_name: page_notifications.settings
+  weight: 0
+
+page_notifications.subscription_list:
+  title: 'Subscriptions'
+  description: 'Manage Page Notification subscriptions'
+  parent: system.admin_content
+  route_name: page_notifications.subscription_list
+  weight: 0
\ No newline at end of file
diff --git a/page_notifications.links.task.yml b/page_notifications.links.task.yml
index 3f0e8804d6340ce9cda2fc1e21d7ea8e1eae8912..c2db43abd404f3bef87e81878f7b25509b17be7a 100644
--- a/page_notifications.links.task.yml
+++ b/page_notifications.links.task.yml
@@ -1,28 +1,29 @@
-page_notifications.tabs:
-  route_name: page_notifications.tabs
-  title: General configuration
-  base_route: page_notifications.tabs
+page_notifications.settings:
+  route_name: page_notifications.settings
+  title: 'Settings'
+  base_route: page_notifications.admin_settings
+  weight: 0
 
-page_notifications.tabs_second:
-  route_name: page_notifications.tabs_second
-  title: Migrate Subscribtions
-  base_route: page_notifications.tabs
-  weight: 2
-
-page_notifications.tabs_third:
-  route_name: page_notifications.tabs_third
-  title: Migrate Subscribtions Content Type
-  base_route: page_notifications.tabs
-  weight: 3
-
-page_notifications.tabs.secondary:
-  route_name: page_notifications.tabs
-  title: General settings
-  parent_id: page_notifications.tabs
+page_notifications.send_manual:
+  route_name: page_notifications.send_manual
+  title: 'Send Notification'
+  base_route: page_notifications.admin_settings
   weight: 1
 
-page_notifications.tabs_default_second:
-  route_name: page_notifications.tabs_default_second
-  title: Messages configuration
-  parent_id: page_notifications.tabs
+page_notifications.subscription_list:
+  route_name: page_notifications.subscription_list
+  title: 'Subscriptions'
+  base_route: page_notifications.admin_settings
   weight: 2
+
+page_notifications.subscription_migrate:
+  route_name: page_notifications.subscription_migrate
+  title: 'Migrate Subscriptions'
+  base_route: page_notifications.admin_settings
+  weight: 4
+
+page_notifications.top_subscribed:
+  route_name: page_notifications.top_subscribed
+  title: 'Top Subscribed'
+  base_route: system.admin_content  # This connects it to the admin content page
+  weight: 5
\ No newline at end of file
diff --git a/page_notifications.module b/page_notifications.module
index f629a97e1ba90c4f087ac65878f8c8b473b1ea1a..5053e5020b9150793be972daeaa716b4e8269a3e 100644
--- a/page_notifications.module
+++ b/page_notifications.module
@@ -1,340 +1,145 @@
 <?php
 
-use Drupal\Core\Mail\MailManagerInterface;
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Component\Utility\Html;
-use Drupal\Core\Mail\MailFormatHelper;
-use Drupal\page_notifications\Form;
-use Drupal\page_notifications\LoadDataBaseInfo;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Render\Markup;
-use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\NodeInterface;
-use Drupal\taxonomy\Entity\Term;
-use \Drupal\Core\Url;
+use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Entity\EntityBase;
 
 /**
- * Implements hook_theme().
+ * @file
+ * Primary module hooks for Page Notifications module.
  */
-function page_notifications_theme ($existing, $type, $theme, $path) {
-  return [
-    'SubscriberPage' => [
-      'variables' => [
-        'page_notifications_var' => 'page_notifications_subscriber_page'
-      ],
-    ],
-  ];
-}
 
-function page_notifications_page_attachments(array &$attachments) {
-  $attachments['#attached']['library'][] = 'page_notifications/page_notifications';
+/**
+ * Implements hook_mail().
+ */
+function page_notifications_mail($key, &$message, $params) {
+  \Drupal::service('page_notifications.mail_handler')->mail($key, $message, $params);
 }
 
- /**
- * Implements hook_form_alter().
+/**
+ * Implements hook_token_info().
  */
-function page_notifications_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  $formObject = $form_state->getFormObject();
-  if ($form_state->getFormObject() instanceof \Drupal\Core\Entity\EntityFormInterface) {
-  $entity = $form_state->getFormObject()->getEntity();
-  $entity_type = $entity->getEntityTypeId();
-  if ($entity_type) {
-    if ($entity_type == "taxonomy_term") {
-      $nid = $form_state->getformObject()->getEntity()->id();
-      $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-      $all_node_subscriptions = \Drupal::service('load.databaseinnfo.service')->getAllNodeSubscription($nid);
-      $node_subscriptions_count  = 0;
-      if ($notify_settings['page_notify_subscribers_count']) {
-        if ($all_node_subscriptions) {
-          $node_subscriptions_count = count($all_node_subscriptions);
-        }
-        $morethen = ($node_subscriptions_count != 0) ? $node_subscriptions_count . ' Subscriptions' : $node_subscriptions_count . ' Subscriptions';
-        $form['options']['page_notifications_count'] = array(
-          '#type' => 'details',
-          '#access' => TRUE,
-          '#title' => 'Page Notifications',
-          '#collapsible' => TRUE,
-          '#collapsed' => TRUE,
-          '#group' => 'advanced',
-          '#weight' => 100,
-          'page_notifications_count_active' => array(
-            '#markup' =>  t('There are: <a href="/admin/page-notifications/all-subscriptions/'.$nid.'-term" target="_blank">'. $morethen . '</a> for this page.'),
-           ),
-        );
-      }
-    } elseif ($entity_type  == "node") {
-      if ($form_state->getFormObject()) {
-        $formObject = $form_state->getFormObject();
-        if (!($formObject instanceof \Drupal\Core\Entity\EntityFormInterface)) {
-          return;
-        }
-        if ($form_state->getformObject()->getEntity()->id()) {
-          $nid = $form_state->getformObject()->getEntity()->id();
-          $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-          $all_node_subscriptions = \Drupal::service('load.databaseinnfo.service')->getAllNodeSubscription($nid);
-          $node_subscriptions_count  = 0;
-          if ($notify_settings['page_notify_subscribers_count']) {
-            if ($all_node_subscriptions) {
-              $node_subscriptions_count = count($all_node_subscriptions);
-            }
-            $morethen = ($node_subscriptions_count != 0) ? $node_subscriptions_count . ' Subscriptions' : $node_subscriptions_count . ' Subscriptions';
-            $form['options']['page_notifications_count'] = array(
-              '#type' => 'details',
-              '#access' => TRUE,
-              '#title' => 'Page Notifications',
-              '#collapsible' => TRUE,
-              '#collapsed' => TRUE,
-              '#group' => 'advanced',
-              '#weight' => 100,
-              'page_notifications_count_active' => array(
-                '#markup' =>  t('There are: <a href="/admin/page-notifications/all-subscriptions/'.$nid.'" target="_blank">'. $morethen . '</a> for this page.'),
-               ),
-            );
-          }
-         }
-        }
-    }
-   }
-  }
+function page_notifications_token_info() {
+  return \Drupal::service('page_notifications.subscription_token')->hookTokenInfo();
 }
 
+/**
+ * Implements hook_tokens().
+ */
+function page_notifications_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+  return \Drupal::service('page_notifications.subscription_token')->hookTokens($type, $tokens, $data, $options, $bubbleable_metadata);
+}
 
-function page_notifications_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
-  $template_info = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-  $checkbox_field = $template_info['checkbox_field'];
-  $notes_field = $template_info['notes_field'];
-  if (method_exists($entity, 'hasField')) {
-   if ($entity->hasField($checkbox_field) && !is_null($checkbox_field)) {
-    if ($entity->get($checkbox_field)->value == 1) {
-      $records = page_notifications_get_subscribes_list($entity->id());
-      $count_records = count($records);
-
-      if ($records && !is_null($records) && $count_records != 0) {
-        $node_title = $entity->getName();
-        $absolute_node_path = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical',['taxonomy_term' => $entity->tid->value],['absolute' => TRUE])->toString();
-
-        if ($template_info['from_email'] && !is_null($template_info['from_email'])) {
-          $from = $template_info['from_email'];
-        } else {
-          $from = \Drupal::config('system.site')->get('mail');
-        }
-
-        if ($template_info['general_email_template_subject'] && !is_null($template_info['general_email_template_subject'])) {
-          $subject = $template_info['general_email_template_subject'];
-        } else {
-          $subject = 'New update on - "' . $node_title . '" page';
-        }
-
-        if ($entity->hasField($notes_field) && !is_null($notes_field)) {
-          $email_update_notes = $entity->get($notes_field)->value;
-        }
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ */
+function page_notifications_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
+  /** @var \Drupal\node\NodeInterface $node */
+  $node = $form_state->getFormObject()->getEntity();
+
+  // Get subscriber count
+  $subscriber_count = \Drupal::entityTypeManager()
+    ->getStorage('page_notification_subscription')
+    ->getQuery()
+    ->condition('subscribed_entity_id', $node->id())
+    ->condition('subscribed_entity_type', 'node')
+    ->condition('status', TRUE)
+    ->count()
+    ->accessCheck(FALSE)
+    ->execute();
 
-        foreach ($records as $record) {
-          $user_token = $record->get("field_page_notify_token_user_id")->getValue();
-          $subscriber_email = $record->get("field_page_notify_email")->getValue();
-          $subscriber_token_notify = $record->get("field_page_notify_token")->getValue();
-          $host = \Drupal::request()->getSchemeAndHttpHost();
-          $unsubscribe_link = $host . "/page-notifications/unsubscribe/" . $entity->id() . "-" . $subscriber_token_notify[0]['value'];
-          $unsubscribe_text = 'To unsubscribe, please go to this <a href=' . $unsubscribe_link .'>link</a>';
-          $subscriptions_url = $host . '/page-notifications/verify-list/' . $entity->id() . "-" . $subscriber_token_notify[0]['value'];
+  // Add notification checkbox to the meta header region
+  $form['meta']['send_notification'] = [
+    '#type' => 'checkbox',
+    '#title' => t('Send notification to subscribers (<strong>@count active @subscribers</strong>)', [
+      '@count' => $subscriber_count,
+      '@subscribers' => $subscriber_count == 1 ? 'subscriber' : 'subscribers',
+    ]),
+    '#description' => $subscriber_count > 0 ?
+      t('If checked, subscribers will receive an email about this update. The revision log message will be included in the notification.') :
+      t('This content has no active subscribers.'),
+    '#default_value' => FALSE,
+    '#disabled' => $subscriber_count === 0,
+    '#group' => 'meta',
+    '#weight' => 30,
+    '#access' => \Drupal::currentUser()->hasPermission('send page notifications'),
+  ];
 
-          $subject_replacements = array(
-            '[notify_user_name]' => '',
-            '[notify_user_email]' => $subscriber_email[0]['value'],
-            '[notify_verify_url]' => '',
-            '[notify_subscribe_url]' => '',
-            '[notify_unsubscribe_url]' => '',
-            '[notify_user_subscribtions]' => '',
-            '[notify_node_title]' => $node_title,
-            '[notify_node_url]' => '',
-            '[notify_notes]' => '',
-          );
-          $body_replacements = array(
-            '[notify_user_name]' => '',
-            '[notify_user_email]' => $subscriber_email[0]['value'],
-            '[notify_verify_url]' => '',
-            '[notify_subscribe_url]' => '',
-            '[notify_unsubscribe_url]' => $unsubscribe_link,
-            '[notify_user_subscribtions]' => $subscriptions_url,
-            '[notify_node_title]' => $node_title,
-            '[notify_node_url]' => $absolute_node_path,
-            '[notify_notes]' => '<div class="notify-notes">' . $email_update_notes . '</div>',
-          );
-          $tokanized_subject = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['general_email_template_subject'], $subject_replacements);
-          $tokanized_body = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['general_email_template'], $body_replacements);
-          strval($tokanized_subject);
+  // Add custom submit handler
+  $form['actions']['submit']['#submit'][] = 'page_notifications_node_form_submit';
+}
 
-          $message['to'] = $subscriber_email[0]['value'];
-          $message['subject'] = $tokanized_subject;
-          $message['body'] = $tokanized_body;
-          $result = \Drupal::service('plugin.manager.mail')->mail(
-             'page_notifications',
-             'notifications_email',
-             $subscriber_email[0]['value'],
-             \Drupal::languageManager()->getDefaultLanguage()->getId(),
-             $message
-           );
-        }
-        \Drupal::messenger()->addStatus(t('Number of email(s) sent: ' . $count_records . '.'));
-      } else {
-        \Drupal::messenger()->addStatus(t('Sorry, there  subscribers for this page.'));
-      }
-      if ($template_info['node_timestamp'] && !is_null($template_info['node_timestamp'])) {
-        $notify_node_timestamp = $template_info['node_timestamp'];
-        if($entity->hasField($notify_node_timestamp)) {
-          $request_time = Drupal::time()->getRequestTime();
-          $entity->set($notify_node_timestamp, $request_time);
-        }
-      }
-      $entity->set($checkbox_field, 0);
-    }
+/**
+ * Submit handler for node form.
+ */
+function page_notifications_node_form_submit($form, FormStateInterface $form_state) {
+  $meta = $form_state->getValue('meta');
+  if (!empty($meta['send_notification'])) {
+    $node = $form_state->getFormObject()->getEntity();
+    /** @var \Drupal\page_notifications\Service\NotificationManagerInterface $notification_manager */
+    $notification_manager = \Drupal::service('page_notifications.notification_manager');
+    $notification_manager->notifySubscribers($node);
+
+    \Drupal::messenger()->addMessage(t('Notification queued for sending to subscribers.'));
   }
- }
 }
 
-function page_notifications_node_presave(Drupal\node\NodeInterface $node) {
-  $moduleHandler = \Drupal::service('module_handler');
-  if ($moduleHandler->moduleExists('page_notifications')) {
-    $template_info = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    //TODO we can replace those two variables
-    $checkbox_field = $template_info['checkbox_field'];
-    $notes_field = $template_info['notes_field'];
-    //TODO Do we want to have people to create checkbox and notes fields on node or just require the checkbox field?
-    if ($node->hasField($checkbox_field) && !is_null($checkbox_field)) {
-      //$node->hasField($notes_field)
-      if ($node->get($checkbox_field)->value == 1) {
-        $records = page_notifications_get_subscribes_list($node->id());
-        $count_records = count($records);
-
-        if ($records && !is_null($records) && $count_records != 0) {
-          $node_title = $node->getTitle();
-          $absolute_node_path = $node->toUrl()->setAbsolute()->toString();
-          if ($template_info['from_email'] && !is_null($template_info['from_email'])) {
-            $from = $template_info['from_email'];
-          }
-          else {
-            $from = \Drupal::config('system.site')->get('mail');
-          }
-
-          if ($template_info['general_email_template_subject'] && !is_null($template_info['general_email_template_subject'])) {
-            $subject = $template_info['general_email_template_subject'];
-          }
-          else {
-            $subject = 'New update on - "' . $node_title . '" page';
-          }
-
-          if ($node->hasField($notes_field) && !is_null($notes_field)) {
-            $email_update_notes = $node->get($notes_field)->value;
-          }
-
-          foreach ($records as $record) {
-            $user_token = $record->get("field_page_notify_token_user_id")->getValue();
-            $subscriber_email = $record->get("field_page_notify_email")->getValue();
-            $subscriber_token_notify = $record->get("field_page_notify_token")->getValue();
-
-            $host = \Drupal::request()->getSchemeAndHttpHost();
-            $unsubscribe_link = $host . "/page-notifications/unsubscribe/" . $node->id() . "-" . $subscriber_token_notify[0]['value'];
-            $unsubscribe_text = 'To unsubscribe, please go to this <a href=' . $unsubscribe_link .'>link</a>';
-            $subscriptions_url = $host . '/page-notifications/verify-list/' . $node->id() . "-" . $subscriber_token_notify[0]['value'];
-
-            $subject_replacements = array(
-              '[notify_user_name]' => '',
-              '[notify_user_email]' => $subscriber_email[0]['value'],
-              '[notify_verify_url]' => '',
-              '[notify_subscribe_url]' => '',
-              '[notify_unsubscribe_url]' => '',
-              '[notify_user_subscribtions]' => '',
-              '[notify_node_title]' => $node_title,
-              '[notify_node_url]' => '',
-              '[notify_notes]' => '',
-            );
-            $body_replacements = array(
-              '[notify_user_name]' => '',
-              '[notify_user_email]' => $subscriber_email[0]['value'],
-              '[notify_verify_url]' => '',
-              '[notify_subscribe_url]' => '',
-              '[notify_unsubscribe_url]' => $unsubscribe_link,
-              '[notify_user_subscribtions]' => $subscriptions_url,
-              '[notify_node_title]' => $node_title,
-              '[notify_node_url]' => $absolute_node_path,
-              '[notify_notes]' => '<div class="notify-notes">' . $email_update_notes . '</div>',
-            );
-            $tokanized_subject = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['general_email_template_subject'], $subject_replacements);
-            $tokanized_body = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['general_email_template'], $body_replacements);
-            strval($tokanized_subject);
+/**
+ * Implements hook_cron().
+ */
+function page_notifications_cron() {
+  \Drupal::service('page_notifications.cron_manager')->processCron();
+}
 
-            $message['to'] = $subscriber_email[0]['value'];
-            $message['subject'] = $tokanized_subject;
-            $message['body'] = $tokanized_body;
-            $result = \Drupal::service('plugin.manager.mail')->mail(
-               'page_notifications',
-               'notifications_email',
-               $subscriber_email[0]['value'],
-               \Drupal::languageManager()->getDefaultLanguage()->getId(),
-               $message
-             );
-          }
-          \Drupal::messenger()->addStatus(t('Number of email(s) sent: ' . $count_records . '.'));
-        } else {
-          \Drupal::messenger()->addStatus(t('Sorry, there no subscribers for this page.'));
-        }
-        if ($template_info['node_timestamp'] && !is_null($template_info['node_timestamp'])) {
-          $notify_node_timestamp = $template_info['node_timestamp'];
-          if($node->hasField($notify_node_timestamp)) {
-            $request_time = Drupal::time()->getRequestTime();
-            $node->set($notify_node_timestamp, $request_time);
-          }
-        }
-        $node->set($checkbox_field, 0);
-      }
-    }
-  }
+/**
+ * Implements hook_theme().
+ */
+function page_notifications_theme() {
+  return [
+    'block__page_notifications_subscription' => [
+      'template' => 'block--page-notifications-subscription',
+      'base hook' => 'block',
+    ],
+    'page_notifications_email_wrapper' => [
+      'variables' => [
+        'content' => NULL,
+        'email_type' => NULL,
+        'subscription' => NULL,
+        'entity' => NULL,
+        'logo_url' =>  theme_get_setting('logo.url') ? \Drupal::request()->getSchemeAndHttpHost() . theme_get_setting('logo.url') : NULL,
+        'site_name' => \Drupal::config('system.site')->get('name'),
+        'footer' => NULL,
+      ],
+      'template' => 'page-notifications-email-wrapper',
+    ],
+    'page_notifications_modal_success' => [
+      'variables' => [
+        'message' => NULL,
+      ],
+    ],
+  ];
 }
 
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function page_notifications_theme_suggestions_page_notifications_email_wrapper(array $variables) {
+  $suggestions = [];
 
-function page_notifications_node_delete($node) {
-  $nid = $node->id();
-  $page_notify_subscription_nids = \Drupal::entityQuery("node")
-   ->accessCheck(FALSE)
-   ->condition('type', 'page_notify_subscriptions')
-   ->condition('field_page_notify_node_id', $nid)
-   ->execute();
-   $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-   $subscribers = $storage_handler->loadMultiple($page_notify_subscription_nids);
-   $storage_handler->delete($subscribers);
-}
- /**
-  * Implements hook_mail().
-  */
-  function page_notifications_mail($key, &$message) {
-    $message['headers']['Content-Type'] = 'text/html; charset=UTF-8;';
-    $text[] = $message['params']['body'];
-    $message['subject'] = t($message['params']['subject']);
-    $message['body'] = array_map(function ($text) {
-      return Markup::create($text);
-    }, $text);
+  if (!empty($variables['email_type'])) {
+    $suggestions[] = 'page_notifications_email_wrapper__' . $variables['email_type'];
   }
 
- function page_notifications_view_alter(array &$build, BlockPluginInterface $block) {
-   // We'll search for the string 'uppercase'.
-   $definition = $block->getPluginDefinition();
-   if ((!empty($build['#configuration']['label']) && mb_strpos($build['#configuration']['label'], 'uppercase')) || (!empty($definition['subject']) && mb_strpos($definition['subject'], 'uppercase'))) {
-     // This will uppercase the block title.
-     $build['#configuration']['label'] = mb_strtoupper($build['#configuration']['label']);
-   }
- }
+  return $suggestions;
+}
 
- function page_notifications_get_subscribes_list($nid) {
-   $nids = \Drupal::entityQuery("node")
-    ->accessCheck(FALSE)
-    ->condition('type', 'page_notify_subscriptions')
-    ->condition('field_page_notify_node_id', $nid)
-    ->condition('status', TRUE)
-    ->execute();
-    $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-    $subscribers = $storage_handler->loadMultiple($nids);
-    return $subscribers;
+/**
+ * Implements hook_preprocess_page_notifications_email_wrapper().
+ */
+function page_notifications_preprocess_page_notifications_email_wrapper(&$variables) {
+  // Ensure logo URL is absolute
+  if (!empty($variables['logo_url']) && !preg_match('/^(http|https):\/\//', $variables['logo_url'])) {
+    $variables['logo_url'] = \Drupal::request()->getSchemeAndHttpHost() . $variables['logo_url'];
+  }
+  // Allow modules to alter email variables
+  \Drupal::moduleHandler()->alter('page_notifications_email_variables', $variables);
 }
diff --git a/page_notifications.permissions.yml b/page_notifications.permissions.yml
index 0977ed4cc96ef6670aadbf6c9fb3293984ff1df9..ff8c1474dae15118f1d65c09213dac326d28a1e3 100644
--- a/page_notifications.permissions.yml
+++ b/page_notifications.permissions.yml
@@ -1,8 +1,28 @@
-access protected page notifications:
-  title: 'Access protected page notifications'
-  description: 'Bypass access control when accessing page notifications.'
-  restrict access: TRUE
-view page notifications reports:
-  title: 'View page notifications reports'
-  description: 'View lists and statistics of page notifications.'
+administer page notification subscriptions:
+  title: 'Administer page notification subscriptions'
+  description: 'Full administrative access to page notification subscriptions.'
   restrict access: TRUE
+
+view page notification subscriptions:
+  title: 'View page notification subscriptions'
+  description: 'View existing page notification subscriptions.'
+
+create page notification subscriptions:
+  title: 'Create page notification subscriptions'
+  description: 'Create new page notification subscriptions.'
+
+edit page notification subscriptions:
+  title: 'Edit page notification subscriptions'
+  description: 'Edit existing page notification subscriptions.'
+
+delete page notification subscriptions:
+  title: 'Delete page notification subscriptions'
+  description: 'Delete existing page notification subscriptions.'
+
+view subscription list:
+  title: 'View subscription list'
+  description: 'Access the subscription list view'
+
+view top subscribed content:
+  title: 'View top subscribed content'
+  description: 'Access the top subscribed content list'
\ No newline at end of file
diff --git a/page_notifications.routing.yml b/page_notifications.routing.yml
index 19abc114d734aecf3675366bc0e107936b20f922..bab80c970e5199f4f99d5eb710e02dd68d6fa48e 100644
--- a/page_notifications.routing.yml
+++ b/page_notifications.routing.yml
@@ -1,115 +1,87 @@
-page_notifications.tabs:
-  path: '/admin/page-notifications/tabs'
+page_notifications.settings:
+  path: '/admin/config/system/page-notifications'
   defaults:
-    _form: '\Drupal\page_notifications\Form\GeneralSettingsForm'
-    _title: 'General configuration'
+    _form: '\Drupal\page_notifications\Form\SettingsForm'
+    _title: 'Page Notifications Settings'
   requirements:
-    _permission: 'access protected page notifications'
-page_notifications.tabs_second:
-  path: '/admin/page-notifications/tabs/second'
+    _permission: 'administer page notification subscriptions'
+
+page_notifications.subscription.verify:
+  path: '/page-notifications/verify/{token}'
   defaults:
-    _form: '\Drupal\page_notifications\Form\MigrationForm'
-    _title: 'Migration of Subscriptions'
+    _controller: 'page_notifications.notification_manager:verifySubscription'
+    _title: 'Verify Subscription'
   requirements:
-    _permission: 'access protected page notifications'
-page_notifications.tabs_third:
-  path: '/admin/page-notifications/tabs/third'
-  defaults:
-    _form: '\Drupal\page_notifications\Form\ContentTypeMigrationForm'
-    _title: 'Migration of nodes to new Content type'
-  requirements:
-    _permission: 'access protected page notifications'
-page_notifications.node_subscriptions:
-  path: '/admin/page-notifications/all-subscriptions/{node_id}'
-  defaults:
-    _title: 'Page Notifications - Node Subscriptions List'
-    _controller: '\Drupal\page_notifications\Controller\AdminSubscriptionsListPage::getNodeSubscribersList'
-    arg1: ''
-  requirements:
-    _permission: 'access protected page notifications'
+    _access: 'TRUE'
   options:
-    no_cache: 'TRUE'
-page_notifications.all:
-  path: '/admin/page-notifications/all-subscriptions'
+    no_cache: TRUE
+
+# Secure unsubscribe for anonymous users
+page_notifications.subscription.unsubscribe:
+  path: '/page-notifications/unsubscribe/{subscription}/{token}'
   defaults:
-    _title: 'Page Notifications - All Subscriptions'
-    _controller: '\Drupal\page_notifications\Controller\AdminSubscriptionsListPage::getNodeSubscribersList'
+    _controller: '\Drupal\page_notifications\Controller\UnsubscribeController::unsubscribe'
+    _title: 'Unsubscribe from Notifications'
   requirements:
-    _permission: 'access protected page notifications'
+    _custom_access: '\Drupal\page_notifications\Controller\UnsubscribeController::checkAccess'
   options:
-    no_cache: 'TRUE'
-page_notifications.tabs_default_second:
-  path: '/admin/page-notifications/tabs/default/second'
+    no_cache: TRUE
+
+page_notifications.send_manual:
+  path: '/admin/config/system/page-notifications/send'
   defaults:
-    _form: '\Drupal\page_notifications\Form\MessagesForm'
-    _title: 'Messages configuration'
+    _form: '\Drupal\page_notifications\Form\ManualNotificationForm'
+    _title: 'Send Manual Notification'
   requirements:
-    _permission: 'access protected page notifications'
-page_notifications.path_override:
-  path: '/admin/page-notifications/menu-original-path'
+    _permission: 'administer page notification subscriptions'
+
+page_notifications.subscription_list:
+  path: '/admin/config/system/page-notifications/subscriptions'
   defaults:
-    _title: 'Menu path that will be altered'
-    _controller: '\Drupal\page_notifications\Controller\PageNotificationsController::pathOverride'
+    _controller: '\Drupal\page_notifications\Controller\SubscriptionListController::content'
+    _title: 'Subscriptions'
   requirements:
-    _permission: 'access content'
-route_callbacks:
-  - '\Drupal\page_notifications\Routing\PageNotificationsDynamicRoutes::routes'
-page_notifications.page_notifications_form_confirm:
-  path: '/page-notifications/confirmation/{email}/{subscription_token}'
+    _permission: 'view subscription list'
+
+page_notifications.top_subscribed:
+  path: '/admin/content/top-subscribed'
   defaults:
-    _form: '\Drupal\page_notifications\Form\EmailConfirmationPage'
-    _title: 'Confirmation Page'
+    _controller: '\Drupal\page_notifications\Controller\TopSubscribedController::content'
+    _title: 'Top Subscribed Content'
   requirements:
-    _permission: 'access content'
-page_notifications.page_notifications_form_verification:
-  path: '/page-notifications/verify-list/{subscription_token}'
+    _permission: 'view subscription list'
+
+page_notifications.subscription_migrate:
+  path: '/admin/config/system/page-notifications/migrate'
   defaults:
-    _form: '\Drupal\page_notifications\Form\AccessVerificationStep'
-    _title: 'My Subscribtions'
+    _form: '\Drupal\page_notifications\Form\SubscriptionMigrateForm'
+    _title: 'Migrate Subscriptions'
   requirements:
-    _permission: 'access content'
-page_notifications.user_subscriptions_page:
-  path: '/page-notifications/my-subscriptions/{subscription_token}'
+    _permission: 'administer page notification subscriptions'
+
+page_notifications.subscription_add:
+  path: '/admin/config/system/page-notifications/subscriptions/add'
   defaults:
-    _form: '\Drupal\page_notifications\Form\UserSubscriptionsPage'
-    _title: 'Manage Your Page Watching Subscriptions'
+    _form: '\Drupal\page_notifications\Form\ManualSubscriptionAddForm'
+    _title: 'Add Subscriptions'
   requirements:
-    _permission: 'access content'
-  options:
-    no_cache: 'TRUE'
-page_notifications.subscriberpage:
-  path: '/page-notifications/my-list/{user_token}'
+    _permission: 'administer page notification subscriptions'
+
+page_notifications.purge_subscriptions_confirm:
+  path: '/admin/config/system/page-notifications/purge-confirm'
   defaults:
-    _controller: '\Drupal\page_notifications\Controller\SubscriberPage::subscriberpage'
-    _title: 'Manage Your Page Watching Subscriptions'
+    _form: '\Drupal\page_notifications\Form\PurgeSubscriptionsConfirmForm'
+    _title: 'Confirm subscription purge'
   requirements:
-    _permission: 'access content'
-  options:
-    no_cache: 'TRUE'
-page_notifications.cancel_subscription_ajax:
-  path: '/ajax/cancel_subscription/{token}'
+    _permission: 'administer page notification subscriptions'
+page_notifications.modal_form:
+  path: '/page-notifications/modal-form/{entity_type}/{entity}'
   defaults:
-    _controller: '\Drupal\page_notifications\Controller\SubscriberPage::cancel_subscription'
-    _title: 'Manage Your Page Watching Subscriptions'
+    _form: '\Drupal\page_notifications\Form\ModalSubscriptionForm'
+    _title: 'Subscribe to Updates'
   requirements:
-    _permission: 'access content'
-page_notifications.cancel_all_ajax:
-  path: '/ajax/cancel_all/{user_token}'
-  defaults:
-    _controller: '\Drupal\page_notifications\Controller\SubscriberPage::cancel_all'
-    _title: 'Manage Your Page Watching Subscriptions'
-  requirements:
-    _permission: 'access content'
-page_notifications.page_notify_unsubscribe:
-  path: '/page-notifications/unsubscribe/{subscription_token}'
-  defaults:
-    _form: '\Drupal\page_notifications\Form\EmailUnsubscribePage'
-  requirements:
-    _permission: 'access content'
-page_notifications.autocomplete.subscriptions:
-  path: '/admin/page-notifications/autocomplete/subscriptions'
-  defaults:
-    _controller: '\Drupal\page_notifications\Controller\SubscriptionsAutoCompleteController::handleAutocomplete'
-    _format: json
-  requirements:
-    _permission: 'access protected page notifications'
+    _access: 'TRUE'
+  options:
+    parameters:
+      entity:
+        type: entity:{entity_type}
\ No newline at end of file
diff --git a/page_notifications.services.yml b/page_notifications.services.yml
index 0b80dfd0456b3a9c2e2f418fb334c971cdf592b8..2012e8e721106ae1eccbbc163ff58e5ef066baf7 100644
--- a/page_notifications.services.yml
+++ b/page_notifications.services.yml
@@ -1,12 +1,61 @@
 services:
-  load.databaseinnfo.service:
-    class: Drupal\page_notifications\LoadDataBaseInfo
-  page_notifications.access_check.role:
-    class: Drupal\page_notifications\Access\RoleAccessCheck
-    arguments: ['@current_user']
+  page_notifications.notification_manager:
+    class: Drupal\page_notifications\Service\NotificationManager
+    arguments:
+      - '@config.factory'
+      - '@plugin.manager.mail'
+      - '@entity_type.manager'
+      - '@queue'
+      - '@logger.factory'
+      - '@event_dispatcher'
+      - '@datetime.time'
+      - '@string_translation'
+      - '@messenger'
+    calls:
+      - [setStringTranslation, ['@string_translation']]
+  page_notifications.mail_handler:
+    class: Drupal\page_notifications\Mail\PageNotificationsMailHandler
+    arguments:
+      - '@config.factory'
+      - '@renderer'
+      - '@token'
+      - '@string_translation'
+      - '@theme.manager'
+  page_notifications.subscription_token:
+    class: Drupal\page_notifications\Token\SubscriptionToken
     tags:
-      - { name: access_check, applies_to: _page_notifications_role }
-  page_notifications.route_subscriber:
-    class: Drupal\page_notifications\Routing\RouteSubscriber
+      - { name: token.provider }
+  page_notifications.cron_manager:
+    class: Drupal\page_notifications\Service\CronManager
+    arguments:
+      - '@entity_type.manager'
+      - '@config.factory'
+      - '@queue'
+      - '@plugin.manager.queue_worker'
+      - '@logger.factory'
+      - '@datetime.time'
+  page_notifications.queue_worker:
+    class: Drupal\page_notifications\Plugin\QueueWorker\NotificationQueue
+    arguments:
+      - '@entity_type.manager'
+      - '@plugin.manager.mail'
+      - '@config.factory'
+      - '@logger.factory'
     tags:
-      - { name: event_subscriber }
+      - { name: queue_worker, id: page_notifications_queue }
+  page_notifications.spam_prevention:
+    class: Drupal\page_notifications\Service\SpamPrevention
+    arguments:
+      - '@config.factory'
+      - '@module_handler'
+      - '@session_manager'
+      - '@string_translation'
+  page_notifications.migration:
+    class: Drupal\page_notifications\Service\MigrationService
+    arguments:
+      - '@database'
+      - '@entity_type.manager'
+      - '@config.factory'
+      - '@state'
+      - '@logger.factory'
+      - '@datetime.time'
\ No newline at end of file
diff --git a/src/Access/RoleAccessCheck.php b/src/Access/RoleAccessCheck.php
deleted file mode 100644
index efac4b1b7a119ad9d94f0e52195031782b23fe1b..0000000000000000000000000000000000000000
--- a/src/Access/RoleAccessCheck.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Access;
-
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Routing\Access\AccessInterface;
-use Drupal\Core\Session\AccountInterface;
-
-class RoleAccessCheck implements AccessInterface {
-
-  /**
-   * Checks access.
-   *
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The currently logged in account.
-   *
-   * @return string
-   *   A \Drupal\Core\Access\AccessInterface constant value.
-   */
-  public function access(AccountInterface $account) {
-    return AccessResult::allowedIf($account->isAuthenticated());
-  }
-
-}
diff --git a/src/Controller/AdminSubscriptionsListPage.php b/src/Controller/AdminSubscriptionsListPage.php
deleted file mode 100644
index efa8908081f5289d3951b2582e36aaeea20e5ec6..0000000000000000000000000000000000000000
--- a/src/Controller/AdminSubscriptionsListPage.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\page_notifications\Form\AccessVerificationStep;
-use Drupal\taxonomy\Entity\Term;
-
-class AdminSubscriptionsListPage extends ControllerBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getModuleName() {
-    return 'admin-node-subscribers-list';
-  }
-
-  public function getNodeSubscribersList($node_id = NULL) {
-  $node_id_parts = explode("-", $node_id);
-  if (count($node_id_parts) == 2) {
-    $node_id_row = $node_id_parts[0];
-    $node_id_entity_type = $node_id_parts[1];
-  } else {
-    $node_id_row = $node_id;
-    $node_id_entity_type = 'node';
-  }
-
-  $header = array(
-    array('data' => $this->t('Node Title'), 'field' => 'title', 'sort' => 'asc'),
-    array('data' => $this->t('E-Mail'), 'field' => 'e-mail'),
-    array('data' => $this->t('Operations')),
-  );
-
-  if ($node_id_entity_type == 'node') {
-    $query = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('type', 'page_notify_subscriptions')
-      ->condition('field_page_notify_node_id', $node_id_row, '=')
-      ->condition('status', 1)
-      ->sort('created', 'DESC')
-      ->pager(25);
-    $records = $query->execute();
-  } elseif ($node_id_entity_type == 'term') {
-    $query = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('type', 'page_notify_subscriptions')
-      ->condition('field_page_notify_node_id', $node_id_row, '=')
-      ->condition('field_page_notify_token', 'term', 'CONTAINS')
-      ->condition('status', 1)
-      ->sort('created', 'DESC')
-      ->pager(25);
-      $records = $query->execute();
-  } else {
-    $query = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('type', 'page_notify_subscriptions')
-      ->condition('status', 1)
-      ->sort('created', 'DESC')
-      ->pager(25);
-    $records = $query->execute();
-  }
-      $rows = array();
-      foreach ($records as $record) {
-        if ($record) {
-          $node = \Drupal\node\Entity\Node::load($record);
-          $page_title = $node->getTitle();
-          $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-          $field_page_notify_email = $node->get("field_page_notify_email")->getValue();
-        }
-
-          $rows[] = array('data' => array(
-            'title' => new FormattableMarkup('<a href="@page_url">@page_title</a>',
-              [
-                '@page_title' => $page_title,
-                '@page_url' => $subscribed_node_url,
-              ]),
-            'e-mail' => new FormattableMarkup('@user_email',
-                [
-                  '@user_email' => $field_page_notify_email[0]['value'],
-                ]),
-            'cancel_one' => new FormattableMarkup('<a href="@record_link">@name</a>',
-                ['@name' => 'View', '@record_link' => $subscribed_node_url]
-              ),
-          ));
-      }
-
-
-      $page_name = '<h1>Subscriptions List</h1>';
-      $build['page_name'] = [
-        '#markup' => $page_name,
-        '#attributes' => [
-          'class' => ['page-notifications-user-list-page-name'],
-        ],
-      ];
-      $build['config_table'] = array(
-        '#theme' => 'table',
-        '#header' => $header,
-        '#rows' => $rows,
-        '#empty' => t('No records found'),
-        '#attributes' => [
-          'class' => ['page-notifications-block-subscriberpage'],
-          'id' => 'page-notifications-block-subscriberpage',
-          'no_striping' => TRUE,
-        ],
-      );
-      $build['pager'] = array(
-        '#type' => 'pager'
-      );
-      return $build;
-  }
-}
diff --git a/src/Controller/ModalFormController.php b/src/Controller/ModalFormController.php
new file mode 100644
index 0000000000000000000000000000000000000000..875c2689b68339484d7f2b67cd21446544c4c41f
--- /dev/null
+++ b/src/Controller/ModalFormController.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\page_notifications\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\node\NodeInterface;
+
+/**
+ * Controller for the subscription modal form.
+ */
+class ModalFormController extends ControllerBase {
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * Constructs a new ModalFormController.
+   *
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder.
+   */
+  public function __construct(FormBuilderInterface $form_builder) {
+    $this->formBuilder = $form_builder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('form_builder')
+    );
+  }
+
+  /**
+   * Returns the subscription form in a modal.
+   *
+   * @param \Drupal\node\NodeInterface $node
+   *   The node being subscribed to.
+   *
+   * @return array
+   *   The render array for the modal form.
+   */
+  public function content(NodeInterface $node) {
+    $build = [
+      '#prefix' => '<div id="modal-subscription-form-wrapper">',
+      '#suffix' => '</div>',
+      'status_messages' => [
+        '#type' => 'status_messages',
+      ],
+      'form' => $this->formBuilder->getForm('\Drupal\page_notifications\Form\SubscriptionForm', $node),
+    ];
+
+    return $build;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Controller/PageNotificationsController.php b/src/Controller/PageNotificationsController.php
deleted file mode 100644
index 449bec97f9b24fcddd5f748f69b04e6f7dcddfc0..0000000000000000000000000000000000000000
--- a/src/Controller/PageNotificationsController.php
+++ /dev/null
@@ -1,256 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Link;
-use Drupal\Core\Url;
-//use Drupal\examples\Utility\DescriptionTemplateTrait;
-
-/**
- * Controller routines for menu example routes.
- */
-class PageNotificationsController extends ControllerBase {
-
-  //use DescriptionTemplateTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getModuleName() {
-    return 'page_notifications';
-  }
-
-  /**
-   * Page callback for the simplest introduction menu entry.
-   *
-   * The controller callback defined page_notifications.routing.yml file,
-   * maps the path 'admin/page-notifications' to this method.
-   *
-   * @throws \InvalidArgumentException
-   */
-  public function basicInstructions() {
-    return [
-      $this->description(),
-    ];
-  }
-
-  /**
-   * Show a menu link in a menu other than the default "Navigation" menu.
-   */
-  public function alternateMenu() {
-    return [
-      '#markup' => $this->t('This will be in the Main menu instead of the default Tools menu'),
-    ];
-
-  }
-
-  /**
-   * A menu entry with simple permissions using 'access protected menu example'.
-   *
-   * @throws \InvalidArgumentException
-   */
-  public function permissioned() {
-    $url = Url::fromRoute('page_notifications.permissioned_controlled');
-    return [
-      '#markup' => $this->t('A menu item that requires the "access protected menu example" permission is at @link', [
-        '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
-      ]),
-    ];
-  }
-
-  /**
-   * Only accessible when the user will be granted with required permission.
-   *
-   * The permission is defined in file page_notifications.permissions.yml.
-   */
-  public function permissionedControlled() {
-    return [
-      '#markup' => $this->t('This menu entry will not show and the page will not be accessible without the "access protected menu example" permission to current user.'),
-    ];
-  }
-
-  /**
-   * Demonstrates the use of custom access check in routes.
-   *
-   * @throws \InvalidArgumentException
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::customAccessPage()
-   */
-  public function customAccess() {
-    $url = Url::fromRoute('page_notifications.custom_access_page');
-    return [
-      '#markup' => $this->t('A menu item that requires the user to posess a role of "authenticated" is at @link', [
-        '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
-      ]),
-    ];
-  }
-
-  /**
-   * Content will be displayed only if access check is satisfied.
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::customAccess()
-   */
-  public function customAccessPage() {
-    return [
-      '#markup' => $this->t('This menu entry will not be visible and access will result
-        in a 403 error unless the user has the "authenticated" role. This is
-        accomplished with a custom access check plugin.'),
-    ];
-  }
-
-  /**
-   * Give the user a link to the route-only page.
-   *
-   * @throws \InvalidArgumentException
-   */
-  public function routeOnly() {
-    $url = Url::fromRoute('page_notifications.route_only.callback');
-    return [
-      '#markup' => $this->t('A menu entry with no menu link is at @link', [
-        '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName())->toString(),
-      ]),
-    ];
-  }
-
-  /**
-   * Such callbacks can be user for creating web services in Drupal 8.
-   */
-  public function routeOnlyCallback() {
-    return [
-      '#markup' => $this->t('The route entry has no corresponding menu links entry, so it provides a route without a menu link, but it is the same in every other way to the simplest example.'),
-    ];
-  }
-
-  /**
-   * Uses the path and title to determine the page content.
-   *
-   * This controller is mapped dynamically based on the 'route_callbacks:' key
-   * in the routing YAML file.
-   *
-   * @param string $path
-   *   Path/URL of menu item.
-   * @param string $title
-   *   Title of menu item.
-   *
-   * @return array
-   *   Controller response.
-   *
-   * @see Drupal\page_notifications\Routing\PageNotificationsDynamicRoutes
-   */
-  public function tabsPage($path, $title) {
-    $secondary = substr_count($path, '/') > 2 ? 'secondary ' : '';
-    return [
-      '#markup' => $this->t('This is the @secondary tab "@tabname" in the "basic tabs" example.', ['@secondary' => $secondary, '@tabname' => $title]),
-    ];
-  }
-
-  /**
-   * Demonstrates use of optional URL arguments in for menu item.
-   *
-   * @param string $arg1
-   *   First argument of URL.
-   * @param string $arg2
-   *   Second argument of URL.
-   *
-   * @return array
-   *   Controller response.
-   *
-   * @see https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes
-   */
-  public function urlArgument($arg1, $arg2) {
-    // Perpare URL for single arguments.
-    $url_single = Url::fromRoute('page_notifications.use_url_arguments', ['arg1' => 'one']);
-
-    // Prepare URL for multiple arguments.
-    $url_double = Url::fromRoute('page_notifications.use_url_arguments', ['arg1' => 'one', 'arg2' => 'two']);
-
-    // Add these argument links to the page content.
-    $markup = $this->t('This page demonstrates using arguments in the url. For example, access it with @link_single for single argument or @link_double for two arguments in URL', [
-      '@link_single' => Link::createFromRoute($url_single->getInternalPath(), $url_single->getRouteName(), $url_single->getRouteParameters())->toString(),
-      '@link_double' => Link::createFromRoute($url_double->getInternalPath(), $url_double->getRouteName(), $url_double->getRouteParameters())->toString(),
-    ]);
-
-    // Process the arguments if they're provided.
-    if (!empty($arg1)) {
-      $markup .= '<div>' . $this->t('Argument 1 = @arg', ['@arg' => $arg1]) . '</div>';
-    }
-    if (!empty($arg2)) {
-      $markup .= '<div>' . $this->t('Argument 2 = @arg', ['@arg' => $arg2]) . '</div>';
-    }
-
-    // Finally return the markup.
-    return [
-      '#markup' => $markup,
-    ];
-  }
-
-  /**
-   * Demonstrate generation of dynamic creation of page title.
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::backTitle()
-   */
-  public function titleCallbackContent() {
-    return [
-      '#markup' => $this->t('The title of this page is dynamically changed by the title callback for this route defined in menu_example.routing.yml.'),
-    ];
-  }
-
-  /**
-   * Generates title dynamically.
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::titleCallback()
-   */
-  public function titleCallback() {
-    return [
-      '#markup' => $this->t('The new title is your username: @name', [
-        '@name' => $this->currentUser()->getDisplayName(),
-      ]),
-    ];
-  }
-
-  /**
-   * Demonstrates how you can provide a placeholder url arguments.
-   *
-   * @throws \InvalidArgumentException
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::placeholderArgsDisplay()
-   * @see https://www.drupal.org/docs/8/api/routing-system/using-parameters-in-routes
-   */
-  public function placeholderArgs() {
-    $url = Url::fromRoute('page_notifications.placeholder_argument.display', ['arg' => 3343]);
-    return [
-      '#markup' => $this->t('Demonstrate placeholders by visiting @link', [
-        '@link' => Link::createFromRoute($url->getInternalPath(), $url->getRouteName(), $url->getRouteParameters())->toString(),
-      ]),
-    ];
-  }
-
-  /**
-   * Displays placeholder argument supplied in URL.
-   *
-   * @param int $arg
-   *   URL argument.
-   *
-   * @return array
-   *   URL argument.
-   *
-   * @see \Drupal\page_notifications\Controller\PageNotificationsController::placeholderArgs()
-   */
-  public function placeholderArgsDisplay($arg) {
-    return [
-      '#markup' => $arg,
-    ];
-
-  }
-
-  /**
-   * Demonstrate how one can alter the existing routes.
-   */
-  public function pathOverride() {
-    return [
-      '#markup' => $this->t('This menu item was created strictly to allow the RouteSubscriber class to have something to operate on. page_notifications.routing.yml defined the path as admin/menu-example/menu-original-path. The alterRoutes() changes it to /admin/menu-example/menu-altered-path. You can try navigating to both paths and see what happens!'),
-    ];
-  }
-
-}
diff --git a/src/Controller/SubscriberPage.php b/src/Controller/SubscriberPage.php
deleted file mode 100644
index 94563b7239c91b8b313d999c714d29d5d8d58e31..0000000000000000000000000000000000000000
--- a/src/Controller/SubscriberPage.php
+++ /dev/null
@@ -1,194 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Drupal\examples\Utility\DescriptionTemplateTrait;
-use Drupal\Core\Ajax\HtmlCommand;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\page_notifications\Form\AccessVerificationStep;
-use Drupal\Core\Url;
-use Drupal\Core\Link;
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\ReplaceCommand;
-use Drupal\taxonomy\Entity\Term;
-
-/**
- * Controller routines for page example routes.
- */
-class SubscriberPage extends ControllerBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getModuleName() {
-    return 'subscriberpage';
-  }
-
-  public function subscriberpage($user_token = NULL) {
-    if ($user_token && !is_null($user_token)) {
-      $header = array(
-        // We make it sortable by name.
-        array('data' => $this->t('Watching pages list'), 'field' => 'title', 'sort' => 'asc'),
-        array('data' => $this->t('')),
-      );
-
-      $query = \Drupal::entityQuery('node')
-        ->accessCheck(FALSE)
-        ->condition('type', 'page_notify_subscriptions')
-        ->condition('field_page_notify_token_user_id', $user_token, '=')
-        ->condition('status', 1)
-        ->sort('created', 'DESC')
-        ->pager(10);
-      $records = $query->execute();
-
-      // Populate the rows.
-      $rows = array();
-      foreach ($records as $record) {
-        $subscriprion_node = \Drupal\node\Entity\Node::load($record);
-        $record_field_page_notify_node_id = $subscriprion_node->get("field_page_notify_node_id")->getValue();
-        $record_field_token_notify = $subscriprion_node->get("field_page_notify_token")->getValue();
-        $record_field_token_notify_pieces = explode("-", $record_field_token_notify[0]['value']);
-
-        if (count($record_field_token_notify_pieces) == 2) {
-          $record_field_entity_type = $record_field_token_notify_pieces[1];
-        } else {
-          $record_field_entity_type = 'node';
-        }
-
-        if ($record_field_entity_type == 'node') {
-          $node = \Drupal\node\Entity\Node::load($record_field_page_notify_node_id[0]['value']);
-          $page_title = $node->getTitle();
-          $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-        } elseif ($record_field_entity_type == 'term') {
-          $node = Term::load($record_field_page_notify_node_id[0]['value']);
-          $page_title = $node->getName();
-          $subscribed_node_url = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical',['taxonomy_term' => $node->tid->value],['absolute' => TRUE])->toString();
-        } else {
-          $node = NULL;
-        }
-
-        $rows[] = array('data' => array(
-          'title' => new FormattableMarkup('<a href="@page_url">@page_title</a>',
-            [
-              '@page_title' => $page_title,
-              '@page_url' => $subscribed_node_url,
-            ]),
-          'cancel_one' => new FormattableMarkup('<a id="notify-cancel-@token" href="/nojs/cancel_subscription/@token" class="use-ajax btn btn-default notify-cancel-subscription">@name</a>',
-              ['@name' => 'Stop Watching', '@token' => $record_field_token_notify[0]['value']]
-            ),
-        ));
-
-        if ($rows) {
-          $cancelall =  '<a id="notify-cancel-all" href="/nojs/cancel_all/' . $user_token .'" class="use-ajax btn btn-default notify-cancel-all-subscription">Unsubscribe from all</a>';
-          $header = array(
-            // We make it sortable by name.
-            array('data' => $this->t('Page Name'), 'field' => 'title', 'sort' => 'asc'),
-            array('data' => $this->t($cancelall)),
-          );
-        }
-        else {
-          $build = array();
-        }
-      }
-
-      $page_name = '<h1>Manage Your Page Watching Subscriptions</h1>';
-      $build['page_name'] = [
-        '#markup' => $page_name,
-        '#attributes' => [
-          'class' => ['page-notifications-user-list-page-name'],
-        ],
-      ];
-      $build['config_table'] = array(
-        '#theme' => 'table',
-        '#header' => $header,
-        '#rows' => $rows,
-        '#empty' => t('No records found'),
-        '#attributes' => [
-          'class' => ['page-notifications-block-subscriberpage'],
-          'id' => 'page-notifications-block-subscriberpage',
-          'no_striping' => TRUE,
-        ],
-      );
-      $build['pager'] = array(
-        '#type' => 'pager'
-      );
-      return $build;
-    }
-  }
-
-  public function cancel_subscription($token) {
-      $response = new AjaxResponse();
-      page_notifications_delete_record($token);
-      $response->addCommand(new ReplaceCommand('#notify-cancel-' . $token, '<span class="notify-cancel-cancelled">Cancelled</span>'));
-      return $response;
-  }
-
-  public function cancel_all($user_token) {
-      $response = new AjaxResponse();
-      page_notifications_delete_all_records($user_token);
-      $response->addCommand(new ReplaceCommand('#notify-cancel-all', '<span class="notify-cancel-all-cancelled">All cancelled</span>'));
-      $response->addCommand(new ReplaceCommand('#notify-cancel-', "Cancelled"));
-      return $response;
-  }
-}
-
-
-
-function checkForRecord($subscription_token_url, $email) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_token' => $subscription_token_url,
-      'field_page_notify_email' => $email
-    ])
-  );
-  if ($record) {
-    return $record;
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function getAllRecords($email) {
-  $query = \Drupal::entityQuery('node')
-    ->accessCheck(FALSE)
-    ->condition('status', 1)
-    ->condition('field_page_notify_email', $email, '=');
-  $records = $query->execute();
-  foreach ($records as $key => $record) {
-    $node = \Drupal\node\Entity\Node::load($record);
-    $nodes[] = $node;
-  }
-  if ($nodes) {
-    return $nodes;
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function page_notifications_delete_record($token) {
-  $num_deleted = \Drupal::entityQuery("node")
-    ->accessCheck(FALSE)
-    ->condition("type", "page_notify_subscriptions")
-    ->condition("field_page_notify_token", $token)
-    ->accessCheck(FALSE)
-    ->execute();
-  $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-  $entities = $storage_handler->loadMultiple($num_deleted);
-  $storage_handler->delete($entities);
-}
-
-function page_notifications_delete_all_records($user_token) {
-  $num_deleted = \Drupal::entityQuery("node")
-    ->accessCheck(FALSE)
-    ->condition("type", "page_notify_subscriptions")
-    ->condition("field_page_notify_token_user_id", $user_token)
-    ->accessCheck(FALSE)
-    ->execute();
-  $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-  $entities = $storage_handler->loadMultiple($num_deleted);
-  $storage_handler->delete($entities);
-}
diff --git a/src/Controller/SubscriptionListController.php b/src/Controller/SubscriptionListController.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab1bd0ba3930b0abcf1865ccc1043da72ba7a043
--- /dev/null
+++ b/src/Controller/SubscriptionListController.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\page_notifications\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Url;
+
+/**
+ * Controller for the subscription list page.
+ */
+class SubscriptionListController extends ControllerBase {
+
+  /**
+   * Displays the subscription list view.
+   *
+   * @return array
+   *   A render array for the view.
+   */
+  public function content() {
+    // Add the "Add Subscription" button
+    $build['add_form'] = [
+      '#type' => 'link',
+      '#title' => $this->t('Add Subscription'),
+      '#url' => Url::fromRoute('page_notifications.subscription_add'),
+      '#attributes' => [
+        'class' => ['button', 'button--action', 'button--primary'],
+      ],
+    ];
+
+    // Add some spacing after the button
+    $build['spacing'] = [
+      '#type' => 'html_tag',
+      '#tag' => 'div',
+      '#attributes' => [
+        'style' => 'margin: 1em 0;',
+      ],
+    ];
+
+    // Add the view
+    $build['view'] = views_embed_view('page_notification_subscriptions', 'default');
+
+    return $build;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Controller/SubscriptionsAutoCompleteController.php b/src/Controller/SubscriptionsAutoCompleteController.php
deleted file mode 100644
index dea67c6712478f5eadb305e091fce45090de29eb..0000000000000000000000000000000000000000
--- a/src/Controller/SubscriptionsAutoCompleteController.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Drupal\Component\Utility\Xss;
-use Drupal\Core\Entity\Element\EntityAutocomplete;
-
-/**
- * Defines a route controller for watches autocomplete form elements.
- */
-class SubscriptionsAutoCompleteController extends ControllerBase {
-
-  /**
-   * The node storage.
-   *
-   * @var \Drupal\node\NodeStorage
-   */
-  protected $nodeStorage;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
-    $this->nodeStroage = $entity_type_manager->getStorage('node');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager')
-    );
-  }
-
-  /**
-   * Handler for autocomplete request.
-   */
-  public function handleAutocomplete(Request $request) {
-    $results = [];
-    $input = $request->query->get('q');
-
-    if (!$input) {
-      return new JsonResponse($results);
-    }
-
-    $input = Xss::filter($input);
-
-    $query = $this->nodeStroage->getQuery()
-      ->condition('title', $input, 'CONTAINS')
-      ->groupBy('nid')
-      ->sort('created', 'DESC')
-      ->range(0, 10);
-
-    $ids = $query->execute();
-    $nodes = $ids ? $this->nodeStroage->loadMultiple($ids) : [];
-
-    foreach ($nodes as $node) {
-      switch ($node->isPublished()) {
-        case TRUE:
-          $availability = '✅';
-          break;
-
-        case FALSE:
-        default:
-          $availability = '🚫';
-          break;
-      }
-
-      $label = [
-        $node->getTitle(),
-        '<small>(' . $node->id() . ')</small>',
-        $availability,
-      ];
-
-      $results[] = [
-        'value' => EntityAutocomplete::getEntityLabels([$node]),
-        'label' => implode(' ', $label),
-      ];
-    }
-    return new JsonResponse($results);
-  }
-}
diff --git a/src/Controller/TopSubscribedController.php b/src/Controller/TopSubscribedController.php
new file mode 100644
index 0000000000000000000000000000000000000000..a97bbcb8ae4b42c35a1286690b202f7aa86b43fd
--- /dev/null
+++ b/src/Controller/TopSubscribedController.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\page_notifications\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+
+/**
+ * Controller for the top subscribed content page.
+ */
+class TopSubscribedController extends ControllerBase {
+
+  /**
+   * Displays the top subscribed content view.
+   *
+   * @return array
+   *   A render array for the view.
+   */
+  public function content() {
+    $view = views_embed_view('top_subscribed_content', 'default');
+    return [
+      '#type' => 'container',
+      'view' => $view,
+    ];
+  }
+
+}
\ No newline at end of file
diff --git a/src/Controller/UnsubscribeController.php b/src/Controller/UnsubscribeController.php
new file mode 100644
index 0000000000000000000000000000000000000000..253dd9e9487372d9c4defde92dc3f772dad11ad6
--- /dev/null
+++ b/src/Controller/UnsubscribeController.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace Drupal\page_notifications\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Access\AccessResult;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Flood\FloodInterface;
+use Drupal\page_notifications\Traits\FloodControlTrait;
+
+/**
+ * Controller for handling unsubscribe requests.
+ */
+class UnsubscribeController extends ControllerBase {
+  use FloodControlTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new UnsubscribeController.
+   */
+  public function __construct(
+    EntityTypeManagerInterface $entity_type_manager,
+    FloodInterface $flood
+  ) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->setFloodService($flood);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('flood')
+    );
+  }
+
+  /**
+   * Custom access check for unsubscribe URLs.
+   */
+  public function checkAccess($subscription, $token) {
+    // Check flood control first
+    $ip = \Drupal::request()->getClientIp();
+    $flood_config = $this->getFloodControlConfig();
+
+    if (!$this->flood->isAllowed('page_notifications.unsubscribe', $flood_config['ip_limit'], $flood_config['ip_window'], $ip)) {
+      return AccessResult::forbidden('Too many unsubscribe attempts from this IP address.');
+    }
+
+    // Register flood event for this attempt
+    $this->flood->register('page_notifications.unsubscribe', $flood_config['ip_window'], $ip);
+
+    if (is_numeric($subscription)) {
+      try {
+        $subscription = $this->entityTypeManager
+          ->getStorage('page_notification_subscription')
+          ->load($subscription);
+      }
+      catch (\Exception $e) {
+        return AccessResult::forbidden();
+      }
+    }
+
+    if (!$subscription) {
+      return AccessResult::forbidden();
+    }
+
+    if ($subscription->getUnsubscribeToken() !== $token) {
+      $this->logSecurityEvent('invalid_unsubscribe_token', [
+        'ip' => $ip,
+        'subscription_id' => $subscription->id(),
+      ]);
+      return AccessResult::forbidden();
+    }
+
+    return AccessResult::allowed();
+  }
+
+  /**
+   * Handles the unsubscribe request.
+   */
+  public function unsubscribe($subscription, $token) {
+    if (is_numeric($subscription)) {
+      try {
+        $subscription = $this->entityTypeManager
+          ->getStorage('page_notification_subscription')
+          ->load($subscription);
+      }
+      catch (\Exception $e) {
+        $this->messenger()->addError($this->t('An error occurred while processing your request.'));
+        return new RedirectResponse('/');
+      }
+    }
+
+    try {
+      if ($subscription && $subscription->getUnsubscribeToken() === $token) {
+        // Get the node ID and entity type before deleting the subscription
+        $entity_id = $subscription->getSubscribedEntityId();
+        $entity_type = $subscription->getSubscribedEntityType();
+
+        $subscription->delete();
+        $this->messenger()->addStatus($this->t('You have been successfully unsubscribed.'));
+
+        // Load the entity and get its URL
+        try {
+          $entity = $this->entityTypeManager
+            ->getStorage($entity_type)
+            ->load($entity_id);
+
+          if ($entity && $entity->hasLinkTemplate('canonical')) {
+            return new RedirectResponse($entity->toUrl()->toString());
+          }
+        }
+        catch (\Exception $e) {
+          \Drupal::logger('page_notifications')->error('Redirect error: @message', ['@message' => $e->getMessage()]);
+        }
+      }
+      else {
+        $this->messenger()->addError($this->t('Invalid unsubscribe link.'));
+      }
+    }
+    catch (\Exception $e) {
+      $this->messenger()->addError($this->t('An error occurred while processing your request.'));
+      \Drupal::logger('page_notifications')->error('Unsubscribe error: @message', ['@message' => $e->getMessage()]);
+    }
+
+    // Fallback to homepage if anything goes wrong
+    return new RedirectResponse('/');
+  }
+}
\ No newline at end of file
diff --git a/src/Entity/Subscription.php b/src/Entity/Subscription.php
new file mode 100644
index 0000000000000000000000000000000000000000..24c675b2dc57fd5019c04a8fd1f1b6968ebad273
--- /dev/null
+++ b/src/Entity/Subscription.php
@@ -0,0 +1,273 @@
+<?php
+
+namespace Drupal\page_notifications\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\user\EntityOwnerTrait;
+
+/**
+ * Defines the Subscription entity.
+ *
+ * @ContentEntityType(
+ *   id = "page_notification_subscription",
+ *   label = @Translation("Page Notification Subscription"),
+ *   handlers = {
+ *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
+ *     "list_builder" = "Drupal\page_notifications\Entity\SubscriptionListBuilder",
+ *     "views_data" = "Drupal\page_notifications\Entity\SubscriptionViewsData",
+ *     "form" = {
+ *       "default" = "Drupal\page_notifications\Form\SubscriptionForm",
+ *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm"
+ *     },
+ *     "access" = "Drupal\page_notifications\Entity\SubscriptionAccessControlHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider"
+ *     }
+ *   },
+ *   base_table = "page_notification_subscription",
+ *   data_table = "page_notification_subscription_field_data",
+ *   admin_permission = "administer page notification subscriptions",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "owner" = "uid",
+ *     "langcode" = "langcode"
+ *   },
+ *   links = {
+ *     "canonical" = "/admin/content/subscriptions/{page_notification_subscription}",
+ *     "delete-form" = "/admin/content/subscriptions/{page_notification_subscription}/delete",
+ *     "collection" = "/admin/content/subscriptions"
+ *   }
+ * )
+ */
+class Subscription extends ContentEntityBase implements SubscriptionInterface {
+
+  use EntityChangedTrait;
+  use EntityOwnerTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEmail() {
+    return $this->get('email')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setEmail($email) {
+    $this->set('email', $email);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSubscribedEntityId() {
+    return $this->get('subscribed_entity_id')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setSubscribedEntityId($id) {
+    $this->set('subscribed_entity_id', $id);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSubscribedEntityType() {
+    return $this->get('subscribed_entity_type')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setSubscribedEntityType($entity_type) {
+    $this->set('subscribed_entity_type', $entity_type);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getToken() {
+    return $this->get('token')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setToken($token) {
+    $this->set('token', $token);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isActive() {
+    return (bool) $this->get('status')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setActive($status) {
+    $this->set('status', $status ? 1 : 0);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCreatedTime() {
+    return $this->get('created')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCreatedTime($timestamp) {
+    $this->set('created', $timestamp);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageCode() {
+    return $this->get('langcode')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLanguageCode($langcode) {
+    $this->set('langcode', $langcode);
+    return $this;
+  }
+
+/**
+   * Gets the default langcode.
+   *
+   * @return string
+   *   The site's default language code.
+   */
+  public static function getDefaultLangcode() {
+    return \Drupal::config('system.site')->get('default_langcode') ?: 'en';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUnsubscribeToken() {
+    return $this->get('unsubscribe_token')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUnsubscribeToken($token) {
+    $this->set('unsubscribe_token', $token);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
+
+    $fields['email'] = BaseFieldDefinition::create('email')
+      ->setLabel(t('Email'))
+      ->setDescription(t('The email address of the subscriber.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(TRUE)
+      ->setSettings([
+        'max_length' => 255,
+      ])
+      ->setDisplayOptions('view', [
+        'label' => 'above',
+        'type' => 'string',
+        'weight' => -5,
+      ])
+      ->setDisplayOptions('form', [
+        'type' => 'email_default',
+        'weight' => -5,
+      ])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayConfigurable('view', TRUE);
+
+    $fields['subscribed_entity_id'] = BaseFieldDefinition::create('integer')
+      ->setLabel(t('Subscribed Entity ID'))
+      ->setDescription(t('The ID of the entity being subscribed to.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(FALSE);
+
+    $fields['subscribed_entity_type'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Subscribed Entity Type'))
+      ->setDescription(t('The type of the entity being subscribed to.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(FALSE)
+      ->setSettings([
+        'max_length' => 32,
+      ]);
+
+    $fields['token'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Token'))
+      ->setDescription(t('The subscription verification token.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(FALSE)
+      ->setSettings([
+        'max_length' => 64,
+      ]);
+
+    $fields['status'] = BaseFieldDefinition::create('boolean')
+      ->setLabel(t('Status'))
+      ->setDescription(t('A boolean indicating whether the subscription is active.'))
+      ->setDefaultValue(TRUE)
+      ->setTranslatable(FALSE)
+      ->setDisplayOptions('form', [
+        'type' => 'boolean_checkbox',
+        'weight' => 0,
+      ]);
+
+    $fields['created'] = BaseFieldDefinition::create('created')
+      ->setLabel(t('Created'))
+      ->setDescription(t('The time that the subscription was created.'))
+      ->setTranslatable(FALSE);
+
+    $fields['changed'] = BaseFieldDefinition::create('changed')
+      ->setLabel(t('Changed'))
+      ->setDescription(t('The time that the subscription was last edited.'))
+      ->setTranslatable(FALSE);
+
+    $fields['langcode'] = BaseFieldDefinition::create('language')
+    ->setLabel(t('Language'))
+    ->setDescription(t('The subscription language code.'))
+    ->setDefaultValueCallback(static::class . '::getDefaultLangcode')
+    ->setDisplayOptions('form', [
+      'type' => 'language_select',
+      'weight' => 2,
+    ]);
+
+    $fields['unsubscribe_token'] = BaseFieldDefinition::create('string')
+  ->setLabel(t('Unsubscribe Token'))
+  ->setDescription(t('The token required to unsubscribe from notifications.'))
+  ->setRequired(TRUE)
+  ->setTranslatable(FALSE)
+  ->setSettings([
+    'max_length' => 64,
+  ]);
+
+    return $fields;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Entity/SubscriptionAccessControlHandler.php b/src/Entity/SubscriptionAccessControlHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a6931d585aad8562805afa9c860841879721bd8
--- /dev/null
+++ b/src/Entity/SubscriptionAccessControlHandler.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\page_notifications\Entity;
+
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Access\AccessResult;
+
+/**
+ * Access controller for page notification subscription entities.
+ */
+class SubscriptionAccessControlHandler extends EntityAccessControlHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    /** @var \Drupal\page_notifications\Entity\SubscriptionInterface $entity */
+    switch ($operation) {
+      case 'view':
+        return AccessResult::allowedIfHasPermission($account, 'view page notification subscriptions');
+
+      case 'update':
+        return AccessResult::allowedIfHasPermission($account, 'edit page notification subscriptions');
+
+      case 'delete':
+        // Allow deletion if user has permission or is the owner of the subscription
+        return AccessResult::allowedIfHasPermissions($account, [
+          'delete page notification subscriptions',
+          'administer page notification subscriptions',
+        ], 'OR')
+          ->orIf(AccessResult::allowedIf($account->isAuthenticated() && $account->id() === $entity->getOwnerId())
+            ->addCacheableDependency($entity));
+    }
+
+    return AccessResult::neutral();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    return AccessResult::allowedIfHasPermissions($account, [
+      'create page notification subscriptions',
+      'administer page notification subscriptions',
+    ], 'OR');
+  }
+
+}
\ No newline at end of file
diff --git a/src/Entity/SubscriptionInterface.php b/src/Entity/SubscriptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef8d41456a89475594c3d2c328cf1bebfd890700
--- /dev/null
+++ b/src/Entity/SubscriptionInterface.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Drupal\page_notifications\Entity;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\user\EntityOwnerInterface;
+
+/**
+ * Interface for Page Notification Subscription entities.
+ */
+interface SubscriptionInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface {
+
+  /**
+   * Gets the subscription email.
+   *
+   * @return string
+   *   The subscription email address.
+   */
+  public function getEmail();
+
+  /**
+   * Sets the subscription email.
+   *
+   * @param string $email
+   *   The subscription email address.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setEmail($email);
+
+  /**
+   * Gets the subscribed entity ID.
+   *
+   * @return int
+   *   The entity ID.
+   */
+  public function getSubscribedEntityId();
+
+  /**
+   * Sets the subscribed entity ID.
+   *
+   * @param int $id
+   *   The entity ID.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setSubscribedEntityId($id);
+
+  /**
+   * Gets the subscribed entity type.
+   *
+   * @return string
+   *   The entity type (e.g., 'node', 'taxonomy_term').
+   */
+  public function getSubscribedEntityType();
+
+  /**
+   * Sets the subscribed entity type.
+   *
+   * @param string $entity_type
+   *   The entity type.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setSubscribedEntityType($entity_type);
+
+  /**
+   * Gets the subscription token.
+   *
+   * @return string
+   *   The subscription token.
+   */
+  public function getToken();
+
+  /**
+   * Sets the subscription token.
+   *
+   * @param string $token
+   *   The subscription token.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setToken($token);
+
+  /**
+   * Gets the subscription status.
+   *
+   * @return bool
+   *   TRUE if the subscription is active, FALSE otherwise.
+   */
+  public function isActive();
+
+  /**
+   * Sets the subscription status.
+   *
+   * @param bool $status
+   *   The subscription status.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setActive($status);
+
+  /**
+   * Gets the subscription creation timestamp.
+   *
+   * @return int
+   *   Creation timestamp of the subscription.
+   */
+  public function getCreatedTime();
+
+  /**
+   * Sets the subscription creation timestamp.
+   *
+   * @param int $timestamp
+   *   The subscription creation timestamp.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setCreatedTime($timestamp);
+
+  /**
+   * Gets the subscription language code.
+   *
+   * @return string
+   *   The language code of the subscription.
+   */
+  public function getLanguageCode();
+
+  /**
+   * Sets the subscription language code.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return $this
+   *   The called subscription entity.
+   */
+  public function setLanguageCode($langcode);
+
+}
\ No newline at end of file
diff --git a/src/Entity/SubscriptionListBuilder.php b/src/Entity/SubscriptionListBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..7f96449426f7d7c715a7dfbe015408ece88279cc
--- /dev/null
+++ b/src/Entity/SubscriptionListBuilder.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace Drupal\page_notifications\Entity;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListBuilder;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Datetime\DateFormatterInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Link;
+use Drupal\Core\Url;
+
+/**
+ * Provides a list builder for page notification subscriptions.
+ */
+class SubscriptionListBuilder extends EntityListBuilder {
+
+  /**
+   * The date formatter service.
+   *
+   * @var \Drupal\Core\Datetime\DateFormatterInterface
+   */
+  protected $dateFormatter;
+
+  /**
+   * Constructs a new SubscriptionListBuilder object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
+   *   The entity storage class.
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
+   *   The date formatter service.
+   */
+  public function __construct(
+    EntityTypeInterface $entity_type,
+    EntityStorageInterface $storage,
+    DateFormatterInterface $date_formatter
+  ) {
+    parent::__construct($entity_type, $storage);
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * {@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('date.formatter')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header = [];
+    $header['id'] = $this->t('ID');
+    $header['email'] = $this->t('Email');
+    $header['subscribed_entity'] = $this->t('Subscribed To');
+    $header['status'] = $this->t('Status');
+    $header['created'] = $this->t('Created');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    /** @var \Drupal\page_notifications\Entity\SubscriptionInterface $entity */
+    $row = [];
+    $row['id'] = $entity->id();
+    $row['email'] = $entity->getEmail();
+
+    // Get the subscribed entity and create a link if possible.
+    $entity_type = $entity->getSubscribedEntityType();
+    $entity_id = $entity->getSubscribedEntityId();
+    try {
+      $subscribed_entity = \Drupal::entityTypeManager()
+        ->getStorage($entity_type)
+        ->load($entity_id);
+      if ($subscribed_entity) {
+        $row['subscribed_entity'] = Link::createFromRoute(
+          $subscribed_entity->label(),
+          'entity.' . $entity_type . '.canonical',
+          [$entity_type => $entity_id]
+        );
+      }
+      else {
+        $row['subscribed_entity'] = $this->t('Entity not found (@type: @id)', [
+          '@type' => $entity_type,
+          '@id' => $entity_id,
+        ]);
+      }
+    }
+    catch (\Exception $e) {
+      $row['subscribed_entity'] = $this->t('Invalid entity reference');
+    }
+
+    $row['status'] = $entity->isActive() ? $this->t('Active') : $this->t('Inactive');
+    $row['created'] = $this->dateFormatter->format($entity->getCreatedTime(), 'short');
+
+    return $row + parent::buildRow($entity);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+protected function getDefaultOperations(EntityInterface $entity) {
+  $operations = parent::getDefaultOperations($entity);
+
+  // Add verify link if not active
+  if (!$entity->isActive()) {
+    $operations['verify'] = [
+      'title' => $this->t('Verify'),
+      'url' => Url::fromRoute('page_notifications.subscription.verify', [
+        'token' => $entity->getToken(),
+      ]),
+    ];
+  }
+
+  return $operations;
+}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    $build = parent::render();
+    $build['table']['#empty'] = $this->t('No subscriptions found.');
+    return $build;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Entity/SubscriptionViewsData.php b/src/Entity/SubscriptionViewsData.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f5150884258b71eb31b3ec3d146f4b5047435f0
--- /dev/null
+++ b/src/Entity/SubscriptionViewsData.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Drupal\page_notifications\Entity;
+
+use Drupal\views\EntityViewsData;
+
+/**
+ * Provides Views data for Page Notification Subscription entities.
+ */
+class SubscriptionViewsData extends EntityViewsData {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getViewsData() {
+    $data = parent::getViewsData();
+
+    // Base table definition
+    $data['page_notification_subscription']['table']['base'] = [
+      'field' => 'id',
+      'title' => $this->t('Page Notification Subscription'),
+      'help' => $this->t('Contains subscription information for page notifications.'),
+      'weight' => -10,
+    ];
+
+
+    // Define the relationship to nodes
+    $data['page_notification_subscription']['subscribed_entity'] = [
+      'title' => $this->t('Subscribed Node'),
+      'help' => $this->t('The node this subscription is associated with.'),
+      'relationship' => [
+        'base' => 'node_field_data',
+        'base field' => 'nid',
+        'field' => 'subscribed_entity_id',
+        'id' => 'standard',
+        'label' => $this->t('Subscribed Node'),
+      ],
+    ];
+
+      // ID field
+      $data['page_notification_subscription']['subscribed_entity_id'] = [
+        'title' => $this->t('Subscribed Entity ID'),
+        'help' => $this->t('The ID of the entity being subscribed to.'),
+        'field' => [
+          'id' => 'numeric',
+        ],
+        'filter' => [
+          'id' => 'numeric',
+        ],
+        'sort' => [
+          'id' => 'standard',
+        ],
+        'argument' => [
+          'id' => 'numeric',
+        ],
+      ];
+
+      // Status field
+      $data['page_notification_subscription']['status'] = [
+        'title' => $this->t('Status'),
+        'help' => $this->t('The status of the subscription.'),
+        'field' => [
+          'id' => 'boolean',
+        ],
+        'filter' => [
+          'id' => 'boolean',
+          'label' => $this->t('Status'),
+          'type' => 'yes-no',
+        ],
+        'sort' => [
+          'id' => 'standard',
+        ],
+      ];
+
+      // Email field
+      $data['page_notification_subscription']['email'] = [
+        'title' => $this->t('Email'),
+        'help' => $this->t('The email address of the subscriber.'),
+        'field' => [
+          'id' => 'standard',
+        ],
+        'filter' => [
+          'id' => 'string',
+        ],
+        'sort' => [
+          'id' => 'standard',
+        ],
+      ];
+
+      // Created field
+      $data['page_notification_subscription']['created'] = [
+        'title' => $this->t('Created'),
+        'help' => $this->t('When the subscription was created.'),
+        'field' => [
+          'id' => 'date',
+        ],
+        'filter' => [
+          'id' => 'date',
+        ],
+        'sort' => [
+          'id' => 'date',
+        ],
+      ];
+
+    // Operations
+    $data['page_notification_subscription']['operations'] = [
+      'field' => [
+        'title' => $this->t('Operations'),
+        'help' => $this->t('Provides links to perform subscription operations.'),
+        'id' => 'entity_operations',
+      ],
+    ];
+
+    return $data;
+  }
+
+}
\ No newline at end of file
diff --git a/src/EventSubscriber/NodeUpdateSubscriber.php b/src/EventSubscriber/NodeUpdateSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..7278ed7fa3d82687c03fea5fc59234438e41b228
--- /dev/null
+++ b/src/EventSubscriber/NodeUpdateSubscriber.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Drupal\page_notifications\EventSubscriber;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\page_notifications\Service\NotificationManagerInterface;
+
+/**
+ * Node update subscriber for sending notifications.
+ */
+class NodeUpdateSubscriber implements EventSubscriberInterface {
+  // TODO: Implement event subscriber for node updates
+}
\ No newline at end of file
diff --git a/src/Form/AccessVerificationStep.php b/src/Form/AccessVerificationStep.php
deleted file mode 100644
index 0b87a055b667b6ec07f38eb5d16dae333399723b..0000000000000000000000000000000000000000
--- a/src/Form/AccessVerificationStep.php
+++ /dev/null
@@ -1,214 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Mail\MailManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Component\Utility\EmailValidator;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\Core\Url;
-use Drupal\node\Entity\Node;
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\CloseModalDialogCommand;
-use Drupal\Core\Ajax\HtmlCommand;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-
-/**
- * Implements the build demo form controller.
- *
- * This example uses the Messenger service to demonstrate the order of
- * controller method invocations by the form api.
- *
- * @see \Drupal\Core\Form\FormBase
- * @see \Drupal\Core\Form\ConfigFormBase
- */
-class AccessVerificationStep extends FormBase {
-
-  /**
-   * The mail manager.
-   *
-   * @var \Drupal\Core\Mail\MailManagerInterface
-   */
-  protected $mailManager;
-
-  /**
-   * The email validator.
-   *
-   * @var \Drupal\Component\Utility\EmailValidator
-   */
-  protected $emailValidator;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Counter keeping track of the sequence of method invocation.
-   *
-   * @var int
-   */
-  protected static $sequenceCounter = 0;
-
-  /**
-   * Constructs a new EmailUnsubscribePage.
-   *
-   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
-   *   The mail manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Component\Utility\EmailValidator $email_validator
-   *   The email validator.
-   */
-  public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, EmailValidator $email_validator) {
-    $this->mailManager = $mail_manager;
-    $this->languageManager = $language_manager;
-    $this->emailValidator = $email_validator;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $form = new static(
-      $container->get('plugin.manager.mail'),
-      $container->get('language_manager'),
-      $container->get('email.validator')
-    );
-    $form->setStringTranslation($container->get('string_translation'));
-    return $form;
-  }
-
-  /**
-   * Update form processing information.
-   *
-   * Display the method being called and it's sequence in the form
-   * processing.
-   *
-   * @param string $method_name
-   *   The method being invoked.
-   */
-  private function displayMethodInvocation($method_name) {
-    self::$sequenceCounter++;
-    $this->messenger()->addMessage(self::$sequenceCounter . ". $method_name");
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, $subscription_token = NULL) {
-
-    $host = \Drupal::request()->getSchemeAndHttpHost();
-    if ($subscription_token && !is_null($subscription_token)) {
-      $form['#id'] = 'page-notifications-block-verify';
-      $form['subscription_token'] = [
-        '#type' => 'hidden',
-        '#value' => $subscription_token,
-      ];
-      $form['email_verify'] = [
-        '#type' => 'textfield',
-        '#title' => $this->t('Enter your E-mail Address:'),
-        '#description' => $this->t('Please enter your email for verification.'),
-        '#required' => TRUE,
-      ];
-      $form['actions'] = [
-        '#type' => 'actions',
-      ];
-      $form['actions']['submit'] = [
-        '#type' => 'submit',
-        '#value' => 'Find my subscribtions',
-      ];
-      return $form;
-    }
-    else {
-      $form['intro'] = [
-        '#markup' => $this->t('<p>You link might be broken or incomplete.</p>'),
-      ];
-      return $form;
-    }
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page-notifications-block-verify';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $email = $form_state->getValue('email_verify');
-    $user_token = $form_state->getValue('subscription_token');
-    if (!$this->emailValidator->isValid($form_state->getValue('email_verify')) && !is_null($form_state->getValue('email_verify'))) {
-      $form_state->setErrorByName('email', $this->t('That e-mail address is not valid.'));
-    }
-    else {
-      $record = page_notifications_verify_if_record_exist($email, $user_token);
-      //$record = \Drupal::service('load.databaseinnfo.service')->verifyByNodeAndEmail($email, $subscription_token);
-      if ($record == true) {
-        $email_verify = $form_state->getValue('email_verify');
-      }
-      else {
-        $form_state->setErrorByName('email', $this->t('We don\'t have any subscription for this email.'));
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-
-  /**
-   * Implements ajax submit callback.
-   *
-   * @param array $form
-   *   Form render array.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   Current state of the form.
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $page_notifications_user_token = \Drupal::service('load.databaseinnfo.service')->pageNotifyGetUserToken($form_state->getValue('email_verify'));
-    $user_token =  $page_notifications_user_token['field_page_notify_token_user_id'];
-    $response = $this->redirect(
-        'page_notifications.subscriberpage',
-        array('user_token' => $user_token),
-    );
-    $response->send();
-  }
-
-  /**
-   * Implements submit callback for Rebuild button.
-   *
-   * @param array $form
-   *   Form render array.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   Current state of the form.
-   */
-  public function rebuildFormSubmit(array &$form, FormStateInterface $form_state) {
-    $this->displayMethodInvocation('rebuildFormSubmit');
-    $form_state->setRebuild(TRUE);
-  }
-}
-
-function page_notifications_verify_if_record_exist($email, $user_token) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_token_user_id' => $user_token,
-      'field_page_notify_email' => $email,
-    ])
-  );
-  if ($record && !is_null($record)) {
-    return TRUE;
-  }
-  else {
-    return FALSE;
-  }
-}
diff --git a/src/Form/ContentTypeMigrationForm.php b/src/Form/ContentTypeMigrationForm.php
deleted file mode 100644
index 946067184190e6308aac7f37886ebfc90b118e1b..0000000000000000000000000000000000000000
--- a/src/Form/ContentTypeMigrationForm.php
+++ /dev/null
@@ -1,274 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\Element\EntityAutocomplete;
-use Drupal\node\Entity\Node;
-
-/**
- * @see \Drupal\Core\Form\FormBase
- */
-class ContentTypeMigrationForm extends FormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_notifications_content_type_migration_form';
-  }
-
-  /**
-   *
-   * @var \Drupal\node\NodeStorage
-   */
-  protected $nodeStorage;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
-    $this->nodeStorage = $entity_type_manager->getStorage('node');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-
-    if ($form_state->has('page_num') && $form_state->get('page_num') == 2) {
-      return self::pageNotificationsPageTwo($form, $form_state);
-    }
-
-    $form_state->set('page_num', 1);
-    $form['intro'] = [
-      '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>Step 1 - Content Type selection.</h2>
-      <h3>Instructions:</h3>
-      <ol>
-       <li>Create new content type</li>
-       <li>Add custom Text (plain) fields to new content type</li>
-       <li>After that come back here and enter content type machine name in fields below</li>
-      </ol>
-      <p>You can migrate subscriptions from one content type to another but the module will work just with page_notify_subscriptions content type.</p>
-      "),
-    ];
-    $form['content_type_export'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('From Content Type'),
-      '#description' => $this->t('Content Type machine name from where subscriptions needs to be moved'),
-      '#default_value' => $form_state->getValue('content_type_export', ''),
-      '#required' => TRUE,
-      '#maxlength' => 1024,
-    ];
-    $form['content_type_import'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('To Content Type'),
-      '#description' => $this->t('Content Type machine name to where subscriptions needs to be moved'),
-      '#default_value' => $form_state->getValue('content_type_import', ''),
-      '#required' => TRUE,
-      '#maxlength' => 1024,
-    ];
-    $form['actions'] = [
-      '#type' => 'actions',
-    ];
-    $form['actions']['next'] = [
-      '#type' => 'submit',
-      '#button_type' => 'primary',
-      '#value' => $this->t('Next'),
-      '#submit' => ['::pageContentSubscriptionsMigrationForm'],
-      '#validate' => ['::pageNotificationsMultistepFormNextValidate'],
-    ];
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-
-    $page_values = $form_state->get('page_values');
-    $count = 0;
-    $clean_values = $form_state->cleanValues()->getValues();
-    $result = \Drupal::entityQuery("node")
-      ->condition("type", $page_values["content_type_export"])
-      ->accessCheck(FALSE)
-      ->execute();
-    $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-    $entities = $storage_handler->loadMultiple($result);
-
-    foreach ($entities as $entity_key => $entity) {
-      $new_values = array();
-      foreach ($clean_values as $key => $clean_value) {
-        if ($clean_value !== 'none'){
-          $new_field = array();
-          $node_filed_value = $entity->get($clean_value)->getValue();
-          if($node_filed_value[0]["value"] && $node_filed_value !== "" || $node_filed_value !== null || !is_null($node_filed_value)){
-            $new_field[$key] = $node_filed_value[0]["value"];
-          } else {
-            $new_field[$key] = $node_filed_value;
-          }
-          $new_values[$key] = $new_field[$key];
-        }
-      }
-
-      $new_node = Node::create(['type' => $page_values["content_type_import"]]);
-      $new_node->set('title', $entity->getTitle());
-      foreach ($new_values as $key => $new_value) {
-        $new_node->set($key, $new_value);
-      }
-      $new_node->enforceIsNew();
-      $new_node->save();
-      $count++;
-    }
-
-    $this->messenger()->addMessage($this->t('Migrated total of: @count', ['@count' => $count]));
-  }
-
-  /**
-   * Provides custom validation handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.a
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function pageNotificationsMultistepFormNextValidate(array &$form, FormStateInterface $form_state) {
-    if (is_null($form_state->getValue('content_type_export'))) {
-      $form_state->setErrorByName('content_type_export', $this->t('Please enter a valid value'));
-    } elseif (is_null($form_state->getValue('content_type_import'))) {
-      $form_state->setErrorByName('content_type_import', $this->t('Please enter a valid value'));
-    }
-  }
-
-  /**
-   * Provides custom submission handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function pageContentSubscriptionsMigrationForm(array &$form, FormStateInterface $form_state) {
-    $form_values = $form_state->getValue(array());
-
-    $form_state
-      ->set('page_values', [
-        'content_type_export' => $form_values['content_type_export'],
-        'content_type_import' => $form_values['content_type_import'],
-      ])
-      ->set('page_num', 2)
-      ->setRebuild(TRUE);
-  }
-
-  /**
-   * Builds the second step form (page 2).
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return array
-   *   The render array defining the elements of the form.
-   */
-  public function pageNotificationsPageTwo(array &$form, FormStateInterface $form_state) {
-    $vals = $form_state->getStorage();
-    $content_type_export = $vals['page_values']['content_type_export'];
-    $content_type_import = $vals['page_values']['content_type_import'];
-
-    $content_type_export_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', $content_type_export);
-    $content_type_import_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', $content_type_import);
-
-    $export_count = count($content_type_export_fields);
-    $import_count = count($content_type_import_fields);
-
-    $result = \Drupal::entityQuery("node")
-      ->condition("type", $content_type_export)
-      ->accessCheck(FALSE)
-      ->execute();
-    $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-    $entities = $storage_handler->loadMultiple($result);
-    $entities_count = count($entities);
-
-
-    if($import_count == 0){
-      $form['intro'] = [
-        '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>Step 2 - Review. No Content type fields found.</h2>"),
-      ];
-    } else {
-      $form['intro'] = [
-        '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>Step 2 - Review. There are ".$entities_count." node(s) to transfer.</h2>
-        <p>Below is the list of content type fileds. Please map each field to corresponding page notifications content type field:</p>
-        "),
-      ];
-
-      $options = array('none' => 'None');
-      foreach($content_type_export_fields as $content_type_export_field) {
-        if (str_starts_with($content_type_export_field->getName(), 'field_')) {
-          $option = $content_type_export_field->getLabel() . ' (' . $content_type_export_field->getName() . ')';
-          $options[$content_type_export_field->getName()] = $content_type_export_field->getLabel();
-        }
-      }
-
-      $i=0;
-      foreach($content_type_import_fields as $content_type_import_field) {
-        $field_name = $content_type_import_field->getName();
-        $field_type = $content_type_import_field->getType();
-        $field_label = $content_type_import_field->getLabel();
-        if (str_starts_with($field_name, 'field_')) {
-          $form['fields-list'][$i][$field_name]= [
-            '#type' => 'select',
-            '#title' => $this->t($field_label),
-            '#description' => $this->t('Machine Name: ' . $field_name),
-            '#options' => $options,
-          ];
-          $i++;
-        }
-      }
-
-      $build['pager'] = array(
-        '#markup' => 'pager',
-      );
-      $form['back'] = [
-        '#type' => 'submit',
-        '#value' => $this->t('Back'),
-        '#submit' => ['::pageNotificationsPageTwoBack'],
-        '#limit_validation_errors' => [],
-      ];
-      $form['submit'] = [
-        '#type' => 'submit',
-        '#button_type' => 'primary',
-        '#value' => $this->t('Submit'),
-      ];
-    }
-    return $form;
-  }
-
-  /**
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function pageNotificationsPageTwoBack(array &$form, FormStateInterface $form_state) {
-    $form_state
-      ->setValues($form_state->get('page_values'))
-      ->set('page_num', 1)
-      ->setRebuild(TRUE);
-  }
-
-
-
-}
diff --git a/src/Form/EmailConfirmationPage.php b/src/Form/EmailConfirmationPage.php
deleted file mode 100644
index 5213878197f2a5684be62f56a82d9ae84f580765..0000000000000000000000000000000000000000
--- a/src/Form/EmailConfirmationPage.php
+++ /dev/null
@@ -1,239 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Mail\MailManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Component\Utility\EmailValidator;
-use Drupal\Core\Url;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\node\Entity\Node;
-use Drupal\taxonomy\Entity\Term;
-
-/**
- * @ingroup page_notifications
- */
-class EmailConfirmationPage extends FormBase {
-
-  /**
-   * The mail manager.
-   *
-   * @var \Drupal\Core\Mail\MailManagerInterface
-   */
-  protected $mailManager;
-
-  /**
-   * The email validator.
-   *
-   * @var \Drupal\Component\Utility\EmailValidator
-   */
-  protected $emailValidator;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a new EmailUnsubscribePage.
-   *
-   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
-   *   The mail manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Component\Utility\EmailValidator $email_validator
-   *   The email validator.
-   */
-  public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, EmailValidator $email_validator) {
-    $this->mailManager = $mail_manager;
-    $this->languageManager = $language_manager;
-    $this->emailValidator = $email_validator;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $form = new static(
-      $container->get('plugin.manager.mail'),
-      $container->get('language_manager'),
-      $container->get('email.validator')
-    );
-    $form->setMessenger($container->get('messenger'));
-    $form->setStringTranslation($container->get('string_translation'));
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_notifications_confirmation';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, $email = NULL, $subscription_token = NULL) {
-    if($subscription_token != strip_tags($subscription_token) && $email != strip_tags($email)) {
-      \Drupal::messenger()->addError(t('Sorry, no data on this.'));
-    } else {
-      if($email && !is_null($email) && $subscription_token && !is_null($subscription_token)) {
-        $subscription_token_pieces = explode("-", $subscription_token);
-        $node_id = $subscription_token_pieces[0];
-        $subscription_entity_type = $subscription_token_pieces[1];
-        $subscription_token_raw = $subscription_token_pieces[2];
-        $host = \Drupal::request()->getSchemeAndHttpHost();
-        $inrecords = secondCheckIfRecordExistNode($email, $node_id, $subscription_entity_type, $subscription_token_raw);
-
-        if ($inrecords && $inrecords->isPublished() == true) {
-          $user_token_exist = \Drupal::service('load.databaseinnfo.service')->pageNotifyGetUserToken($email);
-          if ($user_token_exist && $user_token_exist != false) {
-            $user_token = $user_token_exist['field_page_notify_token_user_id'];
-          }
-          $unsubscribe_link = $host . "/page-notifications/verify-list/" . $user_token;
-          $form['intro'] = [
-            '#markup' => $this->t('<div clas="page-notifications-find-my-subscriptions"> Find your subscriptions <a href='. $unsubscribe_link .'>here</a>.</div>'),
-          ];
-          return $form;
-        }
-        else {
-          $user_token_exist = \Drupal::service('load.databaseinnfo.service')->pageNotifyGetUserToken($email);
-          if ($user_token_exist && $user_token_exist != false) {
-            $user_token = $user_token_exist['field_page_notify_token_user_id'];
-          } else {
-            $user_token = \Drupal::service('load.databaseinnfo.service')->page_notifications_generateRandom_user_token();
-          }
-
-          if ($subscription_entity_type == 'node') {
-            $node = \Drupal\node\Entity\Node::load($node_id);
-            $page_title = $node->getTitle();
-            $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-          } elseif ($subscription_entity_type == 'term') {
-            $node = Term::load($node_id);
-            $page_title = $node->getName();
-            $subscribed_node_url = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical',['taxonomy_term' => $node->tid->value],['absolute' => TRUE])->toString();
-          } else {
-            $node = \Drupal\node\Entity\Node::load($node_id);
-            $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-          }
-
-          $new_submition = Node::create([
-            'type' => 'page_notify_subscriptions',
-            'title' => 'Subscription to - ' . $node_id . ' - ' . $subscription_entity_type . ' - ' . $subscription_token_raw,
-            'field_page_notify_node_id' => $node_id,
-            'field_page_notify_email' => $email,
-            'field_page_notify_token' => $subscription_token_raw . '-' . $subscription_entity_type,
-            'field_page_notify_token_user_id' => $user_token,
-          ]);
-          $new_submition->save();
-
-          $all_subscriptions_url = $host . '/page-notifications/verify-list/' . $user_token;
-          $unsubscribe_link = $host . "/page-notifications/unsubscribe/" . $subscription_token_raw . '-' . $subscription_entity_type;
-
-          $template_info = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-          if ($template_info['from_email']) {
-            $from = $template_info['from_email'];
-          }
-          else {
-            $from = \Drupal::config('system.site')->get('mail');
-          }
-
-          $subject_replacements = array(
-            '[notify_user_name]' => '',
-            '[notify_user_email]' => $email,
-            '[notify_verify_url]' => '',
-            '[notify_subscribe_url]' => '',
-            '[notify_unsubscribe_url]' => '',
-            '[notify_user_subscribtions]' => '',
-            '[notify_node_title]' => $page_title,
-            '[notify_node_url]' => '',
-            '[notify_notes]' => '',
-          );
-          $body_replacements = array(
-            '[notify_user_name]' => '',
-            '[notify_user_email]' => $email,
-            '[notify_verify_url]' => '',
-            '[notify_subscribe_url]' => '',
-            '[notify_unsubscribe_url]' => $unsubscribe_link,
-            '[notify_user_subscribtions]' => $all_subscriptions_url,
-            '[notify_node_title]' => $page_title,
-            '[notify_node_url]' => $subscribed_node_url,
-            '[notify_notes]' => '',
-          );
-
-          $tokanized_subject = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['confirmation_email_subject'], $subject_replacements);
-          $tokanized_body = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['confirmation_email_text'], $body_replacements);
-          strval($tokanized_subject);
-          $message['to'] = $email;
-          $message['subject'] = $tokanized_subject;
-          $message['body'] = $tokanized_body;
-          $result = \Drupal::service('plugin.manager.mail')->mail(
-             'page_notifications',
-             'configuration_email',
-             $email,
-             \Drupal::languageManager()->getDefaultLanguage()->getId(),
-             $message
-           );
-
-          $tokanized_notify_confirmation_web_page_message = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['confirmation_web_page_message'], $body_replacements);
-          if ($template_info['confirmation_web_page_message']) {
-            $form['intro'] = [
-              '#type' => 'processed_text',
-              '#text' => $this->t($tokanized_notify_confirmation_web_page_message),
-              '#format' => 'full_html',
-            ];
-          }
-          else {
-            $form['intro'] = [
-              '#markup' => new FormattableMarkup('<br /><p>You are now subscribed to</p> <h2><a href="@link">@title</a></h2><br />
-                <p><a href="@all_subscriptions_url">Manage Your Page Watching Subscriptions</a>.</p>',
-                ['@title' => $page_title, '@link' => $subscribed_node_url, '@all_subscriptions_url' => $all_subscriptions_url]
-              ),
-            ];
-          }
-          return $form;
-        }
-      }
-      else {
-        \Drupal::messenger()->addError(t('Sorry, no data on this.'));
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $form_values = $form_state->getValues();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $form_values = $form_state->getValues();
-  }
-}
-
-function secondCheckIfRecordExistNode($email, $node_id, $subscription_entity_type = NULL, $subscription_token_raw = NULL) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_email' => $email,
-      'field_page_notify_node_id' => $node_id,
-      'field_page_notify_token' => $subscription_token_raw . '-' . $subscription_entity_type
-    ])
-  );
-  if ($record) {
-    return $record;
-  }
-  else {
-    return FALSE;
-  }
-}
diff --git a/src/Form/EmailUnsubscribePage.php b/src/Form/EmailUnsubscribePage.php
deleted file mode 100644
index 45b085923a6a43d62d1f51417264f5070081f180..0000000000000000000000000000000000000000
--- a/src/Form/EmailUnsubscribePage.php
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Mail\MailManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Component\Utility\EmailValidator;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\Core\Url;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Drupal\node\Entity\Node;
-use Drupal\taxonomy\Entity\Term;
-
-
-/**
- * @ingroup page_notifications
- */
-class EmailUnsubscribePage extends FormBase {
-
-  /**
-   * The mail manager.
-   *
-   * @var \Drupal\Core\Mail\MailManagerInterface
-   */
-  protected $mailManager;
-
-  /**
-   * The email validator.
-   *
-   * @var \Drupal\Component\Utility\EmailValidator
-   */
-  protected $emailValidator;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a new EmailUnsubscribePage.
-   *
-   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
-   *   The mail manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Component\Utility\EmailValidator $email_validator
-   *   The email validator.
-   */
-  public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, EmailValidator $email_validator) {
-    $this->mailManager = $mail_manager;
-    $this->languageManager = $language_manager;
-    $this->emailValidator = $email_validator;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $form = new static(
-      $container->get('plugin.manager.mail'),
-      $container->get('language_manager'),
-      $container->get('email.validator')
-    );
-    $form->setMessenger($container->get('messenger'));
-    $form->setStringTranslation($container->get('string_translation'));
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_notifications_unsubscribe';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, $subscription_token = NULL) {
-    if($subscription_token != strip_tags($subscription_token) || is_null($subscription_token)) {
-      \Drupal::messenger()->addError(t('You link might be broken or incomplete. Please make sure you dont have extra space in your address link.'));
-    } else {
-      $host = \Drupal::request()->getSchemeAndHttpHost();
-      if (is_string($subscription_token) && !is_null($subscription_token)) {
-        $part_subscription_token = explode("-", $subscription_token);
-        $inrecord_by_token = checkIfRecordExistUnsubscribebyTokens($subscription_token);
-
-        if ($inrecord_by_token) {
-          $field_page_notify_token_user_id = $inrecord_by_token->get('field_page_notify_token_user_id')->getValue();
-          $subscriptions_url = $host . '/page-notifications/verify-list/' . $field_page_notify_token_user_id[0]['value'];
-          $node_id = $inrecord_by_token->get('field_page_notify_node_id')->getValue();
-          if (count($part_subscription_token) == 1 && strlen($part_subscription_token[0]) == 10) {
-            $subscription_entity_type = 'node';
-          } elseif (count($part_subscription_token) == 2) {
-            $subscription_entity_type = $part_subscription_token[1];
-          }
-
-          if ($subscription_entity_type == 'node') {
-            $node = \Drupal\node\Entity\Node::load($node_id[0]['value']);
-            $page_title = $node->getTitle();
-            $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-          } elseif ($subscription_entity_type == 'term') {
-            $node = Term::load($node_id[0]['value']);
-            $page_title = $node->getName();
-            $subscribed_node_url = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical',['taxonomy_term' => $node->tid->value],['absolute' => TRUE])->toString();
-          } else {
-            $node = \Drupal\node\Entity\Node::load($node_id[0]['value']);
-            $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-          }
-        }
-
-        if ($subscription_token) {
-          $form['intro'] = [
-            '#markup' => $this->t('<h1>Unsubscribe from "' . $page_title . '" page.</h1>'),
-          ];
-          $form['subscription_token'] = [
-            '#type' => 'hidden',
-            '#value' => $subscription_token,
-          ];
-          $form['email_unsubscribe'] = [
-            '#type' => 'textfield',
-            '#title' => $this->t('Enter your E-mail Address:'),
-            '#description' => $this->t('Please enter your email for confirmation.'),
-            '#required' => TRUE,
-          ];
-          $form['unsubscribe_all'] = [
-            '#type' => 'checkbox',
-            '#title' => $this->t('Unsubscribe me from all my subscriptions'),
-            '#required' => FALSE,
-          ];
-          $form['intro2'] = [
-            '#markup' => new FormattableMarkup('<div>or visit <a target="_blank" href="@url">@name</a></div><br />',
-            [
-              '@name' => ' Manage Your Page Watching Subscriptions.',
-              '@url' => $subscriptions_url
-            ]),
-          ];
-          $form['submit'] = [
-            '#type' => 'submit',
-            '#value' => $this->t('Submit'),
-          ];
-          return $form;
-        } else {
-          $form['intro'] = [
-            '#markup' => new FormattableMarkup('<div>The page you been subscribed is no longer available or moved to different location. <br />You can find out by going to: <a target="_blank" href="@url">@name</a></div><br />',
-            [
-              '@name' => ' Manage Your Page Watching Subscriptions.',
-              '@url' => $subscriptions_url
-            ]),
-          ];
-        }
-
-      } else {
-        $form['intro'] = [
-          '#markup' => $this->t('<p>You link might be broken or incomplete.</p>'),
-        ];
-      }
-
-      return $form;
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    if (!$this->emailValidator->isValid($form_state->getValue('email_unsubscribe')) || is_null($form_state->getValue('email_unsubscribe'))) {
-      $form_state->setErrorByName('email_unsubscribe', $this->t('That e-mail address is not valid.'));
-    } else {
-      $email_unsubscribe = strip_tags($form_state->getValue('email_unsubscribe'));
-      $subscription_token = strip_tags($form_state->getValue('subscription_token'));
-      if(is_string($email_unsubscribe) && is_string($subscription_token)) {
-        $record = \Drupal::service('load.databaseinnfo.service')->verifyByTokenAndEmail($form_state->getValue('email_unsubscribe'), $form_state->getValue('subscription_token'));
-        if ($record == true) {
-          $email_verify = $email_unsubscribe;
-        } else {
-          $form_state->setValue('email_unsubscribe', '');
-          \Drupal::messenger()->addError(t("We don't have subscription for this email."));
-        }
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $form_values = $form_state->getValues();
-    $unsubscribe_all = $form_state->getValue('unsubscribe_all');
-    $email_unsubscribe = $form_state->getValue('email_unsubscribe');
-    $subscription_token = $form_state->getValue('subscription_token');
-
-    if ($subscription_token == strip_tags($subscription_token) && $email_unsubscribe == strip_tags($email_unsubscribe)) {
-      $page_notify_user_token = \Drupal::service('load.databaseinnfo.service')->pageNotifyGetUserToken($email_unsubscribe);
-      //$user_token =  $page_notify_user_token['field_page_notify_token_user_id'];
-
-      if ($unsubscribe_all && !is_null($unsubscribe_all) && $page_notify_user_token != false) {
-        $result = \Drupal::entityQuery("node")
-          ->condition("type", "page_notify_subscriptions")
-          ->condition("field_page_notify_email", $email_unsubscribe)
-          ->accessCheck(FALSE)
-          ->execute();
-        $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-        $entities = $storage_handler->loadMultiple($result);
-        $storage_handler->delete($entities);
-        \Drupal::messenger()->addStatus(t('You have successfully unsubscribed from all pages.'));
-      } else {
-        $inrecords = checkIfRecordExistUnsubscribe($email_unsubscribe, $subscription_token);
-
-        if ($inrecords && !is_null($inrecords)) {
-          $result = \Drupal::entityQuery("node")
-            ->condition("type", "page_notify_subscriptions")
-            ->condition("field_page_notify_token", $form_state->getValue('subscription_token'))
-            ->accessCheck(FALSE)
-            ->execute();
-          $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-          $entities = $storage_handler->loadMultiple($result);
-          $storage_handler->delete($entities);
-          if($result){
-            \Drupal::messenger()->addStatus(t('You have successfully unsubscribed.'));
-          }
-        } else {
-          \Drupal::messenger()->addError(t('This subscription no longer exist.'));
-        }
-      }
-    } else {
-      \Drupal::messenger()->addError(t('You link might be broken or incomplete.'));
-    }
-
-    $url = \Drupal\Core\Url::fromRoute('<front>')->toString();
-    $response = new RedirectResponse($url);
-    $response->send();
-  }
-}
-
-function checkIfRecordExistUnsubscribebyTokens($subscription_token) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_token' => $subscription_token
-    ])
-  );
-  if ($record) {
-    return $record;
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function checkIfRecordExistUnsubscribe($email, $subscription_token) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_email' => $email,
-      'field_page_notify_token' => $subscription_token
-    ])
-  );
-  if ($record) {
-    return $record;
-  }
-  else {
-    return FALSE;
-  }
-}
diff --git a/src/Form/GeneralSettingsForm.php b/src/Form/GeneralSettingsForm.php
deleted file mode 100644
index 049ae93d90b2d7cf051a6887f89d96471154bb0f..0000000000000000000000000000000000000000
--- a/src/Form/GeneralSettingsForm.php
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * @see \Drupal\Core\Form\FormBase
- * @see \Drupal\Core\Form\ConfigFormBase
- */
-class GeneralSettingsForm extends FormBase
-{
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration()
-  {
-    return [
-      'markup' => [
-        'format' => 'full_html',
-        'value' => '',
-      ],
-    ] + parent::defaultConfiguration();
-  }
-
-  /**
-   * @var int
-   */
-  protected static $sequenceCounter = 0;
-
-  /**
-   * {@inheritdoc}
-   */
-
-  /**
-   * Update form processing information.
-   *
-   * Display the method being called and it's sequence in the form
-   * processing.
-   *
-   * @param string $method_name
-   *   The method being invoked.
-   */
-  private function displayMethodInvocation($method_name)
-  {
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state)
-  {
-    $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-
-    $moduleHandler = \Drupal::service('module_handler');
-    if ($moduleHandler->moduleExists('recaptcha')) {
-      $form['page_notify_recaptcha'] = [
-        '#type' => 'checkbox',
-        '#title' => t('Enable/Disable reCaptcha'),
-        '#description' => $this->t('Strongly recommened to use reCaptcha or Captcha for this module.'),
-        '#default_value' => $notify_settings['page_notify_recaptcha'] ? $notify_settings['page_notify_recaptcha'] : 0,
-        '#weight' => 0,
-      ];
-    }
-    if ($moduleHandler->moduleExists('captcha')) {
-      $form['page_notify_captcha'] = [
-        '#type' => 'checkbox',
-        '#title' => t('Enable/Disable Captcha'),
-        '#description' => $this->t('Strongly recommened to use reCaptcha or Captcha for this module.'),
-        '#default_value' => $notify_settings['page_notify_captcha'] ? $notify_settings['page_notify_captcha'] : 0,
-        '#weight' => 0,
-      ];
-    }
-    $form['page_notify_subscribers_count'] = [
-      '#type' => 'checkbox',
-      '#title' => t('Show number of subscribers on node edit.'),
-      '#description' => $this->t('It will output number of subscribers on the node when editing that node.'),
-      '#default_value' => $notify_settings['page_notify_subscribers_count'] ? $notify_settings['page_notify_subscribers_count'] : 0,
-      '#weight' => 1,
-    ];
-    $form['enable_message_subscription_not_available'] = [
-      '#type' => 'checkbox',
-      '#title' => t('Display message when functionality is not available.'),
-      '#description' => $this->t('The message displayed in "Subscription is not available web message" field in Messages configuration settings.'),
-      '#default_value' => $notify_settings['enable_message_subscription_not_available'] ? $notify_settings['enable_message_subscription_not_available'] : 0,
-      '#weight' => 1,
-    ];
-    $form['actions'] = [
-      '#type' => 'actions',
-    ];
-    $form['actions']['submit'] = [
-      '#type' => 'submit',
-      '#value' => 'Save Confirmation',
-    ];
-    $buildInfo = $form_state->getBuildInfo();
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId()
-  {
-    $this->displayMethodInvocation('getFormId');
-    return 'notify_general_congig_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state)
-  {
-    $this->displayMethodInvocation('validateForm');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state)
-  {
-    $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-
-    if ($notify_settings) {
-      $query = \Drupal::database()->update('page_notify_settings')
-        ->fields([
-          'page_notify_settings_group_name' => 'page_notify_general_settings',
-          'page_notify_recaptcha' => $form_state->getValue('page_notify_recaptcha'),
-          'page_notify_captcha' => $form_state->getValue('page_notify_captcha'),
-          'page_notify_subscribers_count' => $form_state->getValue('page_notify_subscribers_count'),
-          'enable_message_subscription_not_available' => $form_state->getValue('enable_message_subscription_not_available'),
-        ])
-        ->condition('page_notify_settings_group_name', 'page_notify_general_settings')
-        ->execute();
-      \Drupal::messenger()->addStatus(t('The configuration options have been saved.'));
-    } else {
-      $query = \Drupal::database()->insert('page_notify_settings')
-        ->fields([
-          'page_notify_settings_group_name' => 'page_notify_general_settings',
-          'page_notify_recaptcha' => $form_state->getValue('page_notify_recaptcha'),
-          'page_notify_captcha' => $form_state->getValue('page_notify_captcha'),
-          'page_notify_subscribers_count' => $form_state->getValue('page_notify_subscribers_count'),
-          'enable_message_subscription_not_available' => $form_state->getValue('enable_message_subscription_not_available'),
-        ]);
-      $query->execute();
-    }
-    \Drupal::messenger()->addStatus(t('The configuration options have been saved.'));
-  }
-}
diff --git a/src/Form/ManualNotificationForm.php b/src/Form/ManualNotificationForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..fe38bb2679de021cb49af7d682a325ccf5294104
--- /dev/null
+++ b/src/Form/ManualNotificationForm.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\page_notifications\Service\NotificationManagerInterface;
+
+/**
+ * Form for manually sending notifications to subscribers.
+ */
+class ManualNotificationForm extends FormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The notification manager service.
+   *
+   * @var \Drupal\page_notifications\Service\NotificationManagerInterface
+   */
+  protected $notificationManager;
+
+  /**
+   * Constructs a new ManualNotificationForm.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\page_notifications\Service\NotificationManagerInterface $notification_manager
+   *   The notification manager service.
+   */
+  public function __construct(
+    EntityTypeManagerInterface $entity_type_manager,
+    NotificationManagerInterface $notification_manager
+  ) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->notificationManager = $notification_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('page_notifications.notification_manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_manual_notification_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    // Get content with active subscribers
+    $subscription_storage = $this->entityTypeManager->getStorage('page_notification_subscription');
+    $node_storage = $this->entityTypeManager->getStorage('node');
+
+    // Get unique node IDs that have active subscribers
+    $query = $subscription_storage->getQuery()
+      ->condition('status', TRUE)
+      ->condition('subscribed_entity_type', 'node')
+      ->accessCheck(FALSE);
+    $result = $query->execute();
+
+    if (empty($result)) {
+      $form['message'] = [
+        '#markup' => $this->t('There are no pages with active subscribers.'),
+      ];
+      return $form;
+    }
+
+    $subscriptions = $subscription_storage->loadMultiple($result);
+    $node_ids = [];
+    foreach ($subscriptions as $subscription) {
+      $node_ids[$subscription->getSubscribedEntityId()] = $subscription->getSubscribedEntityId();
+    }
+
+    // Load nodes and prepare options
+    $nodes = $node_storage->loadMultiple($node_ids);
+    $options = [];
+    foreach ($nodes as $node) {
+      $options[$node->id()] = $node->label();
+    }
+
+    $form['node'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Select Content'),
+      '#options' => $options,
+      '#required' => TRUE,
+      '#description' => $this->t('Select the content to send notifications about.'),
+    ];
+
+    $form['notes'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Notification Notes'),
+      '#description' => $this->t('Enter any additional notes to include in the notification email.'),
+      '#rows' => 4,
+    ];
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Send Notification'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $node_id = $form_state->getValue('node');
+    $notes = $form_state->getValue('notes');
+
+    try {
+      $node = $this->entityTypeManager->getStorage('node')->load($node_id);
+      if ($node) {
+        // Store notes in tempstore or pass through event system
+        \Drupal::state()->set('page_notifications_manual_notes_' . $node->id(), $notes);
+
+        $this->notificationManager->notifySubscribers($node);
+        $this->messenger()->addStatus($this->t('Notifications have been queued for sending.'));
+      }
+    }
+    catch (\Exception $e) {
+      $this->messenger()->addError($this->t('There was a problem sending the notifications.'));
+      \Drupal::logger('page_notifications')->error($e->getMessage());
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/ManualSubscriptionAddForm.php b/src/Form/ManualSubscriptionAddForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..f5386b69119490dc1ad57110d24b2d81f55fb77a
--- /dev/null
+++ b/src/Form/ManualSubscriptionAddForm.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Component\Utility\EmailValidatorInterface;
+
+/**
+ * Form for manually adding subscriptions.
+ */
+class ManualSubscriptionAddForm extends FormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The messenger service.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * The email validator.
+   *
+   * @var \Drupal\Component\Utility\EmailValidatorInterface
+   */
+  protected $emailValidator;
+
+  /**
+   * Constructs a new ManualSubscriptionAddForm.
+   */
+  public function __construct(
+    EntityTypeManagerInterface $entity_type_manager,
+    MessengerInterface $messenger,
+    EmailValidatorInterface $email_validator
+  ) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->messenger = $messenger;
+    $this->emailValidator = $email_validator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('messenger'),
+      $container->get('email.validator')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_manual_subscription_add';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['node'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => $this->t('Content'),
+      '#description' => $this->t('Select the content to subscribe to.'),
+      '#target_type' => 'node',
+      '#required' => TRUE,
+      '#selection_handler' => 'default:node_enhanced',
+      '#selection_settings' => [
+        'target_bundles' => NULL,
+      ],
+    ];
+
+    $form['emails'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Email Addresses'),
+      '#description' => $this->t('Enter email addresses, one per line. These subscribers will be automatically verified.'),
+      '#required' => TRUE,
+      '#rows' => 10,
+    ];
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Add Subscriptions'),
+      '#button_type' => 'primary',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $emails = explode("\n", $form_state->getValue('emails'));
+    $invalid_emails = [];
+
+    foreach ($emails as $email) {
+      $email = trim($email);
+      if (!empty($email) && !$this->emailValidator->isValid($email)) {
+        $invalid_emails[] = $email;
+      }
+    }
+
+    if (!empty($invalid_emails)) {
+      $form_state->setErrorByName('emails', $this->t('The following email addresses are invalid: @emails', [
+        '@emails' => implode(', ', $invalid_emails),
+      ]));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $node_id = $form_state->getValue('node');
+    $emails = array_filter(array_map('trim', explode("\n", $form_state->getValue('emails'))));
+    $added = 0;
+    $skipped = 0;
+
+    try {
+      foreach ($emails as $email) {
+        if (empty($email)) {
+          continue;
+        }
+
+        // Check for existing subscription
+        $existing = $this->entityTypeManager
+          ->getStorage('page_notification_subscription')
+          ->loadByProperties([
+            'email' => $email,
+            'subscribed_entity_id' => $node_id,
+            'subscribed_entity_type' => 'node',
+          ]);
+
+        if (!empty($existing)) {
+          $skipped++;
+          continue;
+        }
+
+        // Create new subscription
+        $subscription = $this->entityTypeManager
+          ->getStorage('page_notification_subscription')
+          ->create([
+            'email' => $email,
+            'subscribed_entity_id' => $node_id,
+            'subscribed_entity_type' => 'node',
+            'token' => bin2hex(random_bytes(32)),
+            'unsubscribe_token' => bin2hex(random_bytes(32)),
+            'status' => TRUE, // Automatically verified
+          ]);
+
+        $subscription->save();
+        $added++;
+      }
+
+      if ($added > 0) {
+        $this->messenger->addStatus($this->t('Successfully added @count subscription(s).', [
+          '@count' => $added,
+        ]));
+      }
+
+      if ($skipped > 0) {
+        $this->messenger->addWarning($this->t('Skipped @count existing subscription(s).', [
+          '@count' => $skipped,
+        ]));
+      }
+    }
+    catch (\Exception $e) {
+      $this->messenger->addError($this->t('An error occurred while adding subscriptions.'));
+      $this->getLogger('page_notifications')->error($e->getMessage());
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/MessagesForm.php b/src/Form/MessagesForm.php
deleted file mode 100644
index 8bd937fa6defdb2a7e1fb3f98ea0e43614077fff..0000000000000000000000000000000000000000
--- a/src/Form/MessagesForm.php
+++ /dev/null
@@ -1,322 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * @see \Drupal\Core\Form\FormBase
- * @see \Drupal\Core\Form\ConfigFormBase
- */
-class MessagesForm extends FormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return [
-      'markup' => [
-        'format' => 'full_html',
-        'value' => '',
-      ],
-    ] + parent::defaultConfiguration();
-  }
-
-  /**
-   * Counter keeping track of the sequence of method invocation.
-   *
-   * @var int
-   */
-  protected static $sequenceCounter = 0;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct() {
-
-  }
-
-  /**
-   * Update form processing information.
-   *
-   * Display the method being called and it's sequence in the form
-   * processing.
-   *
-   * @param string $method_name
-   *   The method being invoked.
-   */
-  private function displayMethodInvocation($method_name) {
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-    $template = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    $form['intro'] = [
-      '#markup' => $this->t("<p>Here you can configure, customize your Email Confirmation message and others like status messages that are displayed on pages.</p>")
-    ];
-    $form['from_email'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('The "from" email'),
-      '#description' => $this->t('Leave empty to use site email.'),
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['from_email']) ? $template['from_email'] : '',
-      ];
-    $form['checkbox_field'] = [
-      '#title'  => 'Checkbox field',
-      '#description' => $this->t('Enter machine name only! Field that enables email notifications to be sent.'),
-      '#type' => 'textfield',
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['checkbox_field']) ? $template['checkbox_field'] : '',
-    ];
-    $form['notes_field'] = [
-      '#title'  => 'Notes field',
-      '#description' => $this->t('Enter machine name only! Field that contains notes or message for subscribers.'),
-      '#type' => 'textfield',
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['notes_field']) ? $template['notes_field'] : '',
-    ];
-    $form['node_timestamp'] = [
-      '#title'  => 'Timestamp',
-      '#description' => $this->t('Enter machine name only! Enter field name that will store timestamp for last sent email of the node.'),
-      '#type' => 'textfield',
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['node_timestamp']) ? $template['node_timestamp'] : '',
-    ];
-    $form['body'] = [
-      '#type' => 'text_format',
-      '#title' => 'Page Notifications header text',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['body']) ? $template['body'] : 'Subscribe to [notify_node_title]',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_node_url]'),
-    ];
-    $form['verification_email_subject'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Subject of Verification email'),
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['verification_email_subject']) ? $template['verification_email_subject'] : 'Subscription Confirmation – [notify_node_title]',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_user_email]'),
-    ];
-    $form['verification_email_text'] = [
-      '#type' => 'text_format',
-      '#title' => 'Body of verification email',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['verification_email_text']) ? $template['verification_email_text'] : '
-      <p>Hello [notify_user_email],</p>
-      <p>Please confirm your subscription&nbsp;<a href="[notify_verify_url]">here</a>.</p>
-      <p>Once complete, you will receive a "Now Subscribed" email notification.</p>
-      <p>Thank you!</p>',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_node_url]; [notify_user_email]; [notify_verify_url]'),
-    ];
-    $form['confirmation_email_subject'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Subject of Confirmation email'),
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['confirmation_email_subject']) ? $template['confirmation_email_subject'] : 'You are now subscribed to - [notify_node_title]',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_user_email]'),
-    ];
-    $form['confirmation_email_text'] = [
-      '#type' => 'text_format',
-      '#title' => 'Confirmation email',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['confirmation_email_text']) ? $template['confirmation_email_text'] : '
-      <p>Hello [notify_user_email],</p>
-      <p>You are now subscribed to <a href="[notify_node_url]">[notify_node_title]</a>.<br />
-      <a href="[notify_unsubscribe_url]">Unsubscribe</a> or visit <a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>
-      <p>Thank you!</p>',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_node_url]; [notify_user_email]; [notify_unsubscribe_url]; [notify_user_subscribtions]'),
-    ];
-    $form['sent_verify_web_page_message'] = [
-      '#type' => 'text_format',
-      '#title' => 'Web message that verification email was sent.',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['sent_verify_web_page_message']) ? $template['sent_verify_web_page_message'] : '
-      <p>Hey [notify_user_email],</p>
-      <p>Please check your email to finalize your subscription!</p>
-      <p style="font-size:9px">*If you didn’t get an e-mail, please check the spam folder</p>',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_user_email];'),
-    ];
-    $form['record_exist_verify_web_page_message'] = [
-      '#type' => 'text_format',
-      '#title' => 'Web message that subscription exist for that email',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['record_exist_verify_web_page_message']) ? $template['record_exist_verify_web_page_message'] : '
-      <p>Hey [notify_user_email],</p>
-      <p>You already subscribed to this page!</p>
-      <p><a href="[notify_unsubscribe_url]">Unsubscribe from this page</a></p>',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_node_url]; [notify_user_email]; [notify_unsubscribe_url].'),
-    ];
-    $form['error_web_page_message'] = [
-      '#type' => 'text_format',
-      '#title' => 'Web message for error',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['error_web_page_message']) ? $template['error_web_page_message'] : '
-      <p>Hey [notify_user_email],</p>
-      <p>There was an error on this page!</p>',
-      '#description' => $this->t('This message will show when recaptcha validation is incorrect.'),
-    ];
-    $form['subscription_not_available_web_page_message'] = [
-      '#type' => 'text_format',
-      '#title' => '"Subscription is not available" web message',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['subscription_not_available_web_page_message']) ? $template['subscription_not_available_web_page_message'] : '<p>Subscription is not available for this page.</p>',
-      '#description' => $this->t('This message will be visible on pages that are not nodes and if block is not hidden.'),
-    ];
-
-    $form['confirmation_web_page_message'] = [
-      '#type' => 'text_format',
-      '#title' => 'Confirmation of subscription - web message',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['confirmation_web_page_message']) ? $template['confirmation_web_page_message'] : '
-      <p>Hey [notify_user_email],</p>
-      <p>You are all set!</p>
-      <p>Thank you for subscribing!</p>
-      <p><a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>',
-      '#description' => $this->t('Avaliable tokens: [notify_node_title]; [notify_node_url]; [notify_user_email]; [notify_unsubscribe_url]; [notify_user_subscribtions]'),
-    ];
-    $form['general_email_template_subject'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Subject of E-mail'),
-      '#size' => 60,
-      '#maxlength' => 128,
-      '#default_value' => isset($template['general_email_template_subject']) ? $template['general_email_template_subject'] : '[notify_node_title] – Notification of New Update',
-      '#description' => $this->t('This is the Subject of Verification email that is sent to subscribers. Avaliable tokens: [notify_node_title]; [notify_user_email]'),
-    ];
-    $form['general_email_template'] = [
-      '#type' => 'text_format',
-      '#title' => 'Email body template for E-mail.',
-      '#format' => 'full_html',
-      '#default_value' => isset($template['general_email_template']) ? $template['general_email_template'] : '
-      <p>Hello [notify_user_email],</p>
-      <p>The "<a href="[notify_node_url]">[notify_node_title]</a>." has been updated.<br />
-      If you would like to unsubscribe to this page please go <a href="[notify_user_email]">here</a> or visit <a href="[notify_user_subscribtions]">Manage your subscriptions</a>.</p>
-      <p>[notify_notes]</p>
-      <p>Thank you!</p>',
-      '#description' => $this->t('This is the body of the Notify general email that is sent to subscribers. Avaliable tokens: [notify_node_title]; [notify_node_url]; [notify_user_email]; [notify_unsubscribe_url]; [notify_user_subscribtions]; [notify_notes]'),
-    ];
-    $form['#cache']['max-age'] = 0;
-    $form['actions'] = [
-      '#type' => 'actions',
-    ];
-    $form['actions']['submit'] = [
-      '#type' => 'submit',
-      '#value' => 'Submit',
-    ];
-    $buildInfo = $form_state->getBuildInfo();
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    $this->displayMethodInvocation('getFormId');
-    return 'form_api_example_build_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $this->displayMethodInvocation('validateForm');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->configuration['body'] = $form_state->getValue('body');
-    $this->configuration['verification_email_subject'] = $form_state->getValue('verification_email_subject');
-    $this->configuration['verification_email_text'] = $form_state->getValue('verification_email_text');
-    $this->configuration['confirmation_email_subject'] = $form_state->getValue('confirmation_email_subject');
-    $this->configuration['confirmation_email_text'] = $form_state->getValue('confirmation_email_text');
-    $this->configuration['sent_verify_web_page_message'] = $form_state->getValue('sent_verify_web_page_message');
-    $this->configuration['record_exist_verify_web_page_message'] = $form_state->getValue('record_exist_verify_web_page_message');
-    $this->configuration['error_web_page_message'] = $form_state->getValue('error_web_page_message');
-    $this->configuration['subscription_not_available_web_page_message'] = $form_state->getValue('subscription_not_available_web_page_message');
-    $this->configuration['confirmation_web_page_message'] = $form_state->getValue('confirmation_web_page_message');
-    $this->configuration['general_email_template_subject'] = $form_state->getValue('general_email_template_subject');
-    $this->configuration['general_email_template'] = $form_state->getValue('general_email_template');
-
-    $notify_body_info = $form_state->getValue('body');
-    $notify_verification_email_subject = $form_state->getValue('verification_email_subject');
-    $notify_verification_email_text = $form_state->getValue('verification_email_text');
-    $notify_confirmation_email_subject = $form_state->getValue('confirmation_email_subject');
-    $notify_confirmation_email_text = $form_state->getValue('confirmation_email_text');
-    $notify_sent_verify_web_page_message = $form_state->getValue('sent_verify_web_page_message');
-    $notify_record_exist_verify_web_page_message = $form_state->getValue('record_exist_verify_web_page_message');
-    $notify_error_web_page_message = $form_state->getValue('error_web_page_message');
-    $notify_subscription_not_available_web_page_message = $form_state->getValue('subscription_not_available_web_page_message');
-    $notify_confirmation_web_page_message = $form_state->getValue('confirmation_web_page_message');
-    $notify_general_email_template_subject = $form_state->getValue('general_email_template_subject');
-    $notify_general_email_template = $form_state->getValue('general_email_template');
-
-    $template_exist = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    if ($template_exist) {
-      $request_time = \Drupal::time()->getRequestTime();
-      $query = \Drupal::database()->delete('page_notify_email_template');
-      $query->condition('template_id', $template_exist['template_id']);
-      $query->execute();
-      $query_insert = \Drupal::database()->insert('page_notify_email_template')
-        ->fields([
-          'from_email' => $form_state->getValue('from_email'),
-          'checkbox_field' => $form_state->getValue('checkbox_field'),
-          'notes_field' => $form_state->getValue('notes_field'),
-          'node_timestamp' => $form_state->getValue('node_timestamp'),
-          'created' => $request_time,
-          'body' => $notify_body_info['value'],
-          'verification_email_subject' => $form_state->getValue('verification_email_subject'),
-          'verification_email_text' => $notify_verification_email_text['value'],
-          'confirmation_email_subject' => $form_state->getValue('confirmation_email_subject'),
-          'confirmation_email_text' => $notify_confirmation_email_text['value'],
-          'sent_verify_web_page_message' => $notify_sent_verify_web_page_message['value'],
-          'record_exist_verify_web_page_message' => $notify_record_exist_verify_web_page_message['value'],
-          'error_web_page_message' => $notify_error_web_page_message['value'],
-          'subscription_not_available_web_page_message' => $notify_subscription_not_available_web_page_message['value'],
-          'confirmation_web_page_message' => $notify_confirmation_web_page_message['value'],
-          'general_email_template_subject' => $form_state->getValue('general_email_template_subject'),
-          'general_email_template' => $notify_general_email_template['value'],
-        ]);
-      $query_insert->execute();
-      if($query_insert){
-        \Drupal::messenger()->addStatus(t('The configuration options have been saved.'));
-      }
-    }
-    else {
-      $request_time = Drupal::time()->getRequestTime();
-      $query = \Drupal::database()->insert('page_notify_email_template')
-        ->fields([
-          'from_email' => $form_state->getValue('from_email'),
-          'checkbox_field' => $form_state->getValue('checkbox_field'),
-          'notes_field' => $form_state->getValue('notes_field'),
-          'node_timestamp' => $form_state->getValue('node_timestamp'),
-          'created' => $request_time,
-          'body' => $notify_body_info['value'],
-          'verification_email_subject' => $form_state->getValue('verification_email_subject'),
-          'verification_email_text' => $notify_verification_email_text['value'],
-          'confirmation_email_subject' => $form_state->getValue('confirmation_email_subject'),
-          'confirmation_email_text' => $notify_confirmation_email_text['value'],
-          'sent_verify_web_page_message' => $notify_sent_verify_web_page_message['value'],
-          'record_exist_verify_web_page_message' => $notify_record_exist_verify_web_page_message['value'],
-          'error_web_page_message' => $notify_error_web_page_message['value'],
-          'subscription_not_available_web_page_message' => $notify_subscription_not_available_web_page_message['value'],
-          'confirmation_web_page_message' => $notify_confirmation_web_page_message['value'],
-          'general_email_template_subject' => $form_state->getValue('general_email_template_subject'),
-          'general_email_template' => $notify_general_email_template['value'],
-        ]);
-      $query->execute();
-      if($query){
-        \Drupal::messenger()->addStatus(t('The configuration options have been saved.'));
-      }
-    }
-  }
-}
diff --git a/src/Form/MigrationForm.php b/src/Form/MigrationForm.php
deleted file mode 100644
index 2efc9960b8cacd29a7690b730e0955949767c2b7..0000000000000000000000000000000000000000
--- a/src/Form/MigrationForm.php
+++ /dev/null
@@ -1,275 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\Element\EntityAutocomplete;
-use Drupal\node\Entity\Node;
-
-/**
- * @see \Drupal\Core\Form\FormBase
- */
-class MigrationForm extends FormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_notifications_migration_form';
-  }
-
-  /**
-   * The node storage.
-   *
-   * @var \Drupal\node\NodeStorage
-   */
-  protected $nodeStorage;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
-    $this->nodeStorage = $entity_type_manager->getStorage('node');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-
-    if ($form_state->has('page_num') && $form_state->get('page_num') == 2) {
-      return self::fapiExamplePageTwo($form, $form_state);
-    }
-
-    $form_state->set('page_num', 1);
-    $form['general_settings_header'] = [
-      '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>Step 1 - Node selection.</h2>
-      <p>This migration allows to move subscribers from one node to another.</p>
-      "),
-      '#weight' => -1,
-    ];
-    $form['subscription_export'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('From Node'),
-      '#description' => $this->t('Node title from where subscriptions needs to be moved'),
-      '#default_value' => $form_state->getValue('subscription_export', ''),
-      '#required' => TRUE,
-      '#autocomplete_route_name' => 'page_notifications.autocomplete.subscriptions',
-      '#maxlength' => 1024,
-    ];
-    $form['subscription_import'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('To Node'),
-      '#description' => $this->t('Node title to where subscriptions needs to be moved'),
-      '#default_value' => $form_state->getValue('subscription_import', ''),
-      '#required' => TRUE,
-      '#autocomplete_route_name' => 'page_notifications.autocomplete.subscriptions',
-      '#maxlength' => 1024,
-    ];
-    $form['actions'] = [
-      '#type' => 'actions',
-    ];
-    $form['actions']['next'] = [
-      '#type' => 'submit',
-      '#button_type' => 'primary',
-      '#value' => $this->t('Next'),
-      '#submit' => ['::pageNotificationsSubscriptionsMigrationForm'],
-      //'#validate' => ['::fapiExampleMultistepFormNextValidate'],
-    ];
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $page_values = $form_state->get('page_values');
-    $node_subscriptions_list = $form_state->getValue('page-notifications-list');
-    $count = 0;
-    foreach ($node_subscriptions_list as $key => $value) {
-      if ($value['field_page_notify_node_id'] != 0) {
-        $node = \Drupal\node\Entity\Node::load($value['field_page_notify_node_id']);
-        $new_title = 'Subscription to - ' . $page_values['subscription_import'] . ' - ' . $node->field_page_notify_token->getString();
-        $node->set('title', $new_title);
-        $node->set('field_page_notify_node_id',  $page_values['subscription_import']);
-        $node->save();
-        $count++;
-      }
-    }
-    $morethen = ($count > 2) ? '1 Subscription' : ' '.$count.' Subscriptions';
-    $this->messenger()->addMessage($this->t('Updated total of: @count', ['@count' => $morethen]));
-  }
-
-  /**
-   * Provides custom validation handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.a
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function fapiExampleMultistepFormNextValidate(array &$form, FormStateInterface $form_state) {
-    /*if ($birth_year != '' && ($birth_year < 1900 || $birth_year > 2000)) {
-      // Set an error for the form element with a key of "birth_year".
-      $form_state->setErrorByName('birth_year', $this->t('Enter a year between 1900 and 2000.'));
-    }*/
-  }
-
-  /**
-   * Provides custom submission handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function pageNotificationsSubscriptionsMigrationForm(array &$form, FormStateInterface $form_state) {
-
-    $subscription_export_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($form_state->getValue('subscription_export'));
-    $subscription_import_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($form_state->getValue('subscription_import'));
-    $form_state
-      ->set('page_values', [
-        'subscription_export' => $subscription_export_id,
-        'subscription_import' => $subscription_import_id,
-      ])
-      ->set('page_num', 2)
-      ->setRebuild(TRUE);
-  }
-
-  /**
-   * Builds the second step form (page 2).
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return array
-   *   The render array defining the elements of the form.
-   */
-  public function fapiExamplePageTwo(array &$form, FormStateInterface $form_state) {
-    $vals = $form_state->getStorage();
-    $subscription_export_node = $vals['page_values']['subscription_export'];
-
-    $nids = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('type', 'page_notify_subscriptions')
-      ->condition('status', 1)
-      ->condition('field_page_notify_node_id', $subscription_export_node , '=')
-      ->sort('created', 'DESC')
-      ->pager(10)
-      ->execute();
-    $nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
-    $nids = array_keys($nodes);
-    $export_records_count = count($nids);
-    if($export_records_count == 0){
-      $form['general_settings_header'] = [
-        '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>No subscriptions found for this node.</h2>"),
-        '#weight' => -1,
-      ];
-      $form['back'] = [
-        '#type' => 'submit',
-        '#value' => $this->t('Back'),
-        '#submit' => ['::fapiExamplePageTwoBack'],
-        '#limit_validation_errors' => [],
-      ];
-    } else {
-      $morethen = ($export_records_count > 2) ? 'Subscription' : 'Subscriptions';
-      $form['general_settings_header'] = [
-        '#markup' => $this->t("<h2 id='page-notifications-config-page-header'>Step 2 - Review. Total of ".$export_records_count . " " . $morethen . " to transfer:</h2>"),
-        '#weight' => -1,
-      ];
-      $form['pagenotifycheckall'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Select / Unselect all'),
-        '#default_value' => 1,
-        '#weight' => 0,
-      );
-      $form['page-notifications-list'] = array(
-        '#type' => 'table',
-        '#title' => 'List of Nodes',
-        '#header' => ["Checkbox", "Title", "Email", "Title (Node ID)"],
-    		'#multiple' => TRUE,
-      );
-
-      $i=0;
-      foreach($nids as $nid) {
-        $node = \Drupal\node\Entity\Node::load($nid);
-        $contenttitle=$node->title->value;
-        $receivername = $node->getOwner()->getDisplayName();
-        $field_page_notify_email = $node->get('field_page_notify_email')->getValue();
-        $field_page_notify_node_id = $node->get('field_page_notify_node_id')->getValue();
-        $field_page_notify_token = $node->get('field_page_notify_token')->getValue();
-        \Drupal::entityTypeManager()->getStorage('node')->resetCache(array($nid));
-
-        $subscribed_node = \Drupal\node\Entity\Node::load($field_page_notify_node_id[0]['value']);
-        $subscribed_node_url_str = $subscribed_node->toUrl()->toString();
-        $subscribed_node_title = $subscribed_node->getTitle();
-        $truncated_subscribed_node_title = (strlen($subscribed_node_title) > 20) ? substr($subscribed_node_title, 0, 20) . '...' : $subscribed_node_title;
-        $subscribed_node_link = '<a href="'.$subscribed_node_url_str.'">'.$truncated_subscribed_node_title.'</a>';
-
-        $form['page-notifications-list'][$i]['field_page_notify_node_id'] = array(
-          '#type' => 'checkbox',
-          '#return_value' => $nid,
-          '#default_value' => 1,
-          '#attributes' => array('checked' => 'checked')
-          );
-        $form['page-notifications-list'][$i]['Title'] = array(
-          '#type' => 'label',
-          '#title' => t($contenttitle),
-        );
-        $form['page-notifications-list'][$i]['Email'] = array(
-          '#type' => 'label',
-          '#title' => t($field_page_notify_email[0]['value']),
-        );
-        $form['page-notifications-list'][$i]['wiew_node'] = array(
-          '#markup' => t($subscribed_node_link . ' (' . $field_page_notify_node_id[0]['value'] . ')'),
-        );
-        $i++;
-      }
-      $build['pager'] = array(
-        '#markup' => 'pager',
-      );
-
-      $form['back'] = [
-        '#type' => 'submit',
-        '#value' => $this->t('Back'),
-        '#submit' => ['::fapiExamplePageTwoBack'],
-        '#limit_validation_errors' => [],
-      ];
-      $form['submit'] = [
-        '#type' => 'submit',
-        '#button_type' => 'primary',
-        '#value' => $this->t('Submit'),
-      ];
-    }
-    return $form;
-  }
-
-  /**
-   * Provides custom submission handler for 'Back' button (page 2).
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function fapiExamplePageTwoBack(array &$form, FormStateInterface $form_state) {
-    $form_state
-      ->setValues($form_state->get('page_values'))
-      ->set('page_num', 1)
-      ->setRebuild(TRUE);
-  }
-}
diff --git a/src/Form/ModalSubscriptionForm.php b/src/Form/ModalSubscriptionForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..853c466d3e0b5dd486a02bc7c399206b4a019012
--- /dev/null
+++ b/src/Form/ModalSubscriptionForm.php
@@ -0,0 +1,289 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\page_notifications\Service\NotificationManagerInterface;
+use Drupal\page_notifications\Service\SpamPrevention;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Psr\Log\LoggerInterface;
+use Drupal\Core\Flood\FloodInterface;
+use Drupal\page_notifications\Traits\FloodControlTrait;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\Ajax\MessageCommand;
+use Drupal\Core\Ajax\ReplaceCommand;
+
+/**
+ * Provides a subscription form for modal display.
+ */
+class ModalSubscriptionForm extends FormBase {
+  use FloodControlTrait;
+
+  /**
+   * The notification manager service.
+   *
+   * @var \Drupal\page_notifications\Service\NotificationManagerInterface
+   */
+  protected $notificationManager;
+
+  /**
+   * The spam prevention service.
+   *
+   * @var \Drupal\page_notifications\Service\SpamPrevention
+   */
+  protected $spamPrevention;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The logger instance.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * Constructs a new ModalSubscriptionForm.
+   */
+  public function __construct(
+    NotificationManagerInterface $notification_manager,
+    SpamPrevention $spam_prevention,
+    ConfigFactoryInterface $config_factory,
+    LoggerInterface $logger,
+    FloodInterface $flood
+  ) {
+    $this->notificationManager = $notification_manager;
+    $this->spamPrevention = $spam_prevention;
+    $this->configFactory = $config_factory;
+    $this->logger = $logger;
+    $this->setFloodService($flood);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('page_notifications.notification_manager'),
+      $container->get('page_notifications.spam_prevention'),
+      $container->get('config.factory'),
+      $container->get('logger.factory')->get('page_notifications'),
+      $container->get('flood')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_modal_subscription_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL) {
+    if (!$entity) {
+      return [
+        '#markup' => $this->t('No content found for subscription.'),
+      ];
+    }
+
+    $form['#prefix'] = '<div id="modal-subscription-form-wrapper">';
+    $form['#suffix'] = '</div>';
+
+    // Store the entity in the form state
+    $form_state->set('entity', $entity);
+
+    $form['email'] = [
+      '#type' => 'email',
+      '#title' => $this->t('Email address'),
+      '#required' => TRUE,
+      '#description' => $this->t('Enter your email address to receive notifications when this content is updated.'),
+    ];
+
+    // Add success message to form state if provided
+    if ($form_state->get('success_message')) {
+      return [
+        '#theme' => 'page_notifications_modal_success',
+        '#message' => $form_state->get('success_message'),
+      ];
+    }
+
+    // Add spam prevention based on configuration
+    $config = $this->configFactory->get('page_notifications.settings');
+    $captcha_type = $config->get('spam_prevention.captcha_type');
+
+    if ($captcha_type === 'math') {
+      if (!$form_state->getUserInput()) {
+        $challenge = $this->spamPrevention->generateMathChallenge();
+        $form['math_challenge_data'] = [
+          '#type' => 'hidden',
+          '#value' => json_encode($challenge),
+        ];
+      }
+      else {
+        $challenge = json_decode($form_state->getUserInput()['math_challenge_data'] ?? '{}', TRUE);
+      }
+
+      if (!empty($challenge)) {
+        $form['math_challenge_data'] = [
+          '#type' => 'hidden',
+          '#value' => json_encode($challenge),
+        ];
+
+        $form['math_challenge'] = [
+          '#type' => 'number',
+          '#title' => $challenge['question'],
+          '#required' => TRUE,
+          '#description' => $this->t('Please solve this simple math problem to prevent spam.'),
+        ];
+      }
+    }
+    elseif ($captcha_type === 'recaptcha' && $this->spamPrevention->isRecaptchaAvailable()) {
+      $form['captcha'] = [
+        '#type' => 'captcha',
+        '#captcha_type' => 'recaptcha/reCAPTCHA',
+      ];
+    }
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Subscribe'),
+      '#ajax' => [
+        'callback' => '::submitModalAjax',
+        'event' => 'click',
+        'progress' => [
+          'type' => 'throbber',
+          'message' => $this->t('Processing...'),
+        ],
+      ],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * AJAX callback for modal form submission.
+   */
+  public function submitModalAjax(array &$form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+
+    if ($form_state->hasAnyErrors()) {
+      $response->addCommand(new ReplaceCommand(
+        '#modal-subscription-form-wrapper',
+        [
+          '#type' => 'container',
+          '#attributes' => ['id' => 'modal-subscription-form-wrapper'],
+          'status_messages' => [
+            '#type' => 'status_messages',
+          ],
+          'form' => $form,
+        ]
+      ));
+      return $response;
+    }
+
+    try {
+      $entity = $form_state->get('entity');
+      if (!$entity) {
+        throw new \Exception('No entity found for subscription.');
+      }
+
+      $email = $form_state->getValue('email');
+      $subscription = $this->notificationManager->createSubscription($email, $entity);
+
+      // Get success message from block configuration
+      $block_config = $form_state->get('block_configuration');
+      $success_message = $block_config['success_message'] ?? $this->t('Thank you for subscribing. Please check your email to confirm your subscription.');
+
+      $form_state->set('success_message', $success_message);
+      $success_content = [
+        '#theme' => 'page_notifications_modal_success',
+        '#message' => $success_message,
+        '#attached' => [
+          'library' => ['page_notifications/modal']
+        ]
+      ];
+
+      $response->addCommand(new ReplaceCommand(
+        '#modal-subscription-form-wrapper',
+        $success_content
+      ));
+    }
+    catch (\Exception $e) {
+      $this->logger->error('Subscription error: @message', ['@message' => $e->getMessage()]);
+
+      $response->addCommand(new ReplaceCommand(
+        '#modal-subscription-form-wrapper',
+        [
+          '#type' => 'container',
+          '#attributes' => ['id' => 'modal-subscription-form-wrapper'],
+          'status_messages' => [
+            '#type' => 'status_messages',
+            '#message_list' => [
+              'error' => [$this->t('There was a problem creating your subscription. Please try again later.')],
+            ],
+          ],
+          'form' => $form,
+        ]
+      ));
+    }
+
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $email = $form_state->getValue('email');
+
+    // Check flood control before other validation
+    if (!$this->checkFloodControl($email, $form_state)) {
+      return;
+    }
+
+    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
+      $form_state->setErrorByName('email', $this->t('Please enter a valid email address.'));
+      return;
+    }
+
+    // Validate math challenge if enabled
+    $config = $this->configFactory->get('page_notifications.settings');
+    $captcha_type = $config->get('spam_prevention.captcha_type');
+
+    if ($captcha_type === 'math') {
+      $challenge_data = $form_state->getValue('math_challenge_data');
+      if ($challenge_data) {
+        $challenge = json_decode($challenge_data, TRUE);
+        $response = $form_state->getValue('math_challenge');
+
+        if (!$this->spamPrevention->validateMathResponse($response, $challenge)) {
+          $form_state->setErrorByName('math_challenge', $this->t('The answer to the math challenge is incorrect.'));
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Empty as everything is handled in the AJAX callback
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/PageNotificationsBlockForm.php b/src/Form/PageNotificationsBlockForm.php
deleted file mode 100644
index fc8faba599c373e8bc16dfb24c0d320206482c17..0000000000000000000000000000000000000000
--- a/src/Form/PageNotificationsBlockForm.php
+++ /dev/null
@@ -1,496 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Mail\MailManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Component\Utility\EmailValidator;
-use Drupal\Core\Block\BlockPluginInterface;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\Core\Render\Markup;
-use Drupal\Core\Url;
-use \Drupal\node\Entity\Node;
-use Drupal\taxonomy\Entity\Term;
-use \Drupal\Component\Utility\UrlHelper;
-use Drupal\filter\Element\ProcessedText;
-use Drupal\Core\Render\BubbleableMetadata;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\Component\Utility\Html;
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\CssCommand;
-use Drupal\Core\Ajax\HtmlCommand;
-use Drupal\Core\Ajax\InvokeCommand;
-
-/**
- * Submit a form without a page reload.
- */
-class PageNotificationsBlockForm extends FormBase
-{
-
-  /**
-   * The mail manager.
-   *
-   * @var \Drupal\Core\Mail\MailManagerInterface
-   */
-  protected $mailManager;
-
-  /**
-   * The email validator.
-   *
-   * @var \Drupal\Component\Utility\EmailValidator
-   */
-  protected $emailValidator;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a new EmailExampleGetFormPage.
-   *
-   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
-   *   The mail manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Component\Utility\EmailValidator $email_validator
-   *   The email validator.
-   */
-  public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, EmailValidator $email_validator)
-  {
-    $this->mailManager = $mail_manager;
-    $this->languageManager = $language_manager;
-    $this->emailValidator = $email_validator;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container)
-  {
-    $form = new static(
-      $container->get('plugin.manager.mail'),
-      $container->get('language_manager'),
-      $container->get('email.validator')
-    );
-    $form->setMessenger($container->get('messenger'));
-    $form->setStringTranslation($container->get('string_translation'));
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId()
-  {
-    return 'page_notifications_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state)
-  {
-    $preload_template = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-    $host = \Drupal::request()->getSchemeAndHttpHost();
-    $current_uri_row = \Drupal::request()->getRequestUri();
-    $current_uri_pieces = explode("?", $current_uri_row);
-    $current_uri = $current_uri_pieces[0];
-    $reload_page_link = $host . $current_uri;
-    $page_url = '';
-    $page_id = '';
-    
-    $form['#prefix'] = '<div id="page-notifications-block-container">';
-    $form['#suffix'] = '</div>';
-    $route_match = \Drupal::routeMatch();
-
-    if ($route_match->getRouteName() == 'entity.node.canonical') {
-      $node = \Drupal::routeMatch()->getParameter('node');
-      if ($node instanceof \Drupal\node\NodeInterface) {
-        $node = \Drupal\node\Entity\Node::load($node->id());
-        $page_title = $node->getTitle();
-        $page_id = $node->id() . '-' . 'node';
-        $page_entity_type = 'node';
-        $node_node_url = $node->toUrl()->setAbsolute()->toString();
-      }
-    } elseif ($route_match->getRouteName() == 'entity.taxonomy_term.canonical') {
-      $term_id = $route_match->getRawParameter('taxonomy_term');
-      $term = Term::load($term_id);
-      $page_id = $term_id . '-' . 'term';
-      $page_entity_type = 'term';
-      $page_title = $term->getName();
-      $page_url = $term->toUrl()->setAbsolute()->toString();
-    } else {
-      $page_title = '';
-      $page_url = '';
-      $page_id = '';
-    }
-
-    if ($page_url == '' && $page_id == '') {
-      $form['container'] = [
-        '#type' => 'container',
-        '#attributes' => ['id' => 'page-notifications-block-container'],
-      ];
-      $form['block_header'] = [
-        '#type' => 'processed_text',
-        '#text' => $this->t($preload_template['subscription_not_available_web_page_message']),
-        "#processed" => true,
-        '#format' => 'full_html',
-      ];
-    } else {
-      $block_header_replacements = [
-        '[notify_user_name]' => '',
-        '[notify_user_email]' => '',
-        '[notify_verify_url]' => '',
-        '[notify_subscribe_url]' => '',
-        '[notify_unsubscribe_url]' => '',
-        '[notify_user_subscribtions]' => '',
-        '[notify_node_title]' => $page_title,
-        '[notify_node_url]' => $page_url,
-        '[notify_notes]' => '',
-      ];
-      $tokanized_notify_body = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($preload_template['body'], $block_header_replacements);
-      $form['container'] = [
-        '#type' => 'container',
-        '#attributes' => ['id' => 'page-notifications-block-container'],
-      ];
-      $form['container']['box'] = [
-        '#type' => 'processed_text',
-        '#text' => $this->t($tokanized_notify_body),
-        "#processed" => true,
-        '#format' => 'full_html',
-      ];
-      $form['current_node'] = [
-        '#type' => 'hidden',
-        '#value' => $page_id,
-      ];
-      $form['current_path'] = [
-        '#type' => 'hidden',
-        '#value' => $page_url,
-      ];
-      $form['email_notify'] = [
-        '#type' => 'email',
-        '#title' => $this->t('Enter your E-mail Address:'),
-        '#required' => true,
-      ];
-      $moduleHandler = \Drupal::service('module_handler');
-      if ($moduleHandler->moduleExists('recaptcha') && $notify_settings['page_notify_recaptcha'] == "1") {
-        $form['#attached']['library'][] = 'page_notifications/recaptcha';
-        $form['recaptcha'] = [
-          '#markup' => pageNotificationsRecaptchaHtml(),
-        ];
-      }
-      if ($moduleHandler->moduleExists('captcha') && $notify_settings['page_notify_captcha'] == "1") {
-        $captcha_type = \Drupal::config('captcha.settings')->get('default_challenge');
-        $form['my_captcha_element'] = [
-          '#type' => 'captcha',
-          '#captcha_type' => $captcha_type,
-        ];
-      }
-    }
-
-    $form['submit'] = [
-      '#type' => 'submit',
-      '#ajax' => [
-        'callback' => '::promptCallback',
-        'wrapper' => 'page-notifications-block-container',
-      ],
-      '#value' => $this->t('Submit'),
-    ];
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state)
-  {
-    if (!$this->emailValidator->isValid($form_state->getValue('email_notify')) || $form_state->getValue('email_notify') != strip_tags($form_state->getValue('email_notify'))) {
-      $form_state->setErrorByName('email', $this->t('That e-mail address is not valid.'));
-    }
-
-  }
-
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state)
-  {
-  }
-
-  /**
-   * Callback for submit_driven example.
-   *
-   * Select the 'box' element, change the markup in it, and return it as a
-   * renderable array.
-   *
-   * @return array
-   *   Renderable array (the box element)
-   */
-  public function promptCallback(array &$form, FormStateInterface $form_state)
-  {
-    $response = new AjaxResponse();
-    $element = $form['container'];
-    $host = \Drupal::request()->getSchemeAndHttpHost();
-    $template_info = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    $input = $form_state->getUserInput();
-    if (!is_null($input['g-recaptcha-response'])) {
-      if ($input['g-recaptcha-response'] == "") {
-        $form_state->setErrorByName('g-recaptcha-response', $this->t('Please enter recaptcha.'));
-      }
-    }
-    $errors = $form_state->getErrors();
-
-    if (!$errors) {
-      // if email and node not correct
-      if ($form_state->getValue('email_notify') != strip_tags($form_state->getValue('email_notify')) || $form_state->getValue('current_node') != strip_tags($form_state->getValue('current_node'))) {
-        $email_notify_strip_tags = strip_tags($form_state->getValue('email_notify'));
-        $replacements = [
-          '[notify_user_name]' => '',
-          '[notify_user_email]' => $email_notify_strip_tags,
-          '[notify_verify_url]' => '',
-          '[notify_subscribe_url]' => '',
-          '[notify_unsubscribe_url]' => '',
-          '[notify_user_subscribtions]' => '',
-          '[notify_node_title]' => '',
-          '[notify_node_url]' => '',
-          '[notify_notes]' => '',
-        ];
-        $tokanized_error_web_page_message = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['error_web_page_message'], $replacements);
-        $element['box'] = [
-          '#type' => 'processed_text',
-          '#text' => $this->t($tokanized_error_web_page_message),
-          '#format' => 'full_html',
-        ];
-      } else { // if email and node correct
-        if ($form_state->getValue('email_notify') && $form_state->getValue('current_node')) {
-          $email_notify = $form_state->getValue('email_notify');
-          $current_node = $form_state->getValue('current_node');
-          $record_exist = \Drupal::service('load.databaseinnfo.service')->checkIfRecordExistNode($email_notify, $current_node);
-
-          if ($record_exist) {
-            $token_pieces = explode("-", $record_exist['field_page_notify_token']);
-            if (count($token_pieces) == 2) {
-              $record_exist_node_id = $record_exist['field_page_notify_node_id'];
-              $record_exist_token = $token_pieces[0];
-              $record_exist_entity_type = $token_pieces[1];
-            } elseif (count($token_pieces) == 1) {
-              $record_exist_node_id = $record_exist['field_page_notify_node_id'];
-              $record_exist_token = $token_pieces[0];
-              $record_exist_entity_type = 'node';
-            } else {
-              $record_exist_node_id = $record_exist['field_page_notify_node_id'];
-              $record_exist_entity_type = 'node';
-            }
-
-            $unsubscribe_link = $host . "/page-notifications/unsubscribe/" . $record_exist['field_page_notify_token'];
-            if ($record_exist_entity_type == 'node') {
-              $node = \Drupal\node\Entity\Node::load($record_exist_node_id);
-              $page_title = $node->getTitle();
-              $subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-            } elseif ($record_exist_entity_type == 'term') {
-              $node = Term::load($record_exist_node_id);
-              $page_title = $node->getName();
-              $subscribed_node_url = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $node->tid->value], ['absolute' => TRUE])->toString();
-            } else {
-              $node = NULL;
-            }
-
-            $replacements = [
-              '[notify_user_name]' => '',
-              '[notify_user_email]' => $email_notify,
-              '[notify_verify_url]' => '',
-              '[notify_subscribe_url]' => '',
-              '[notify_unsubscribe_url]' => $unsubscribe_link ? $unsubscribe_link : '',
-              '[notify_user_subscribtions]' => '',
-              '[notify_node_title]' => $page_title ? $page_title : '',
-              '[notify_node_url]' => $subscribed_node_url ? $subscribed_node_url : '',
-              '[notify_notes]' => '',
-            ];
-            $tokanized_web_message = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['record_exist_verify_web_page_message'], $replacements);
-            $element['box'] = [
-              '#type' => 'processed_text',
-              '#text' => $this->t($tokanized_web_message),
-              '#format' => 'full_html',
-            ];
-          } else { // this is when record doesn't exist
-            $current_page_node_pieces = explode("-", $current_node);
-            if (count($current_page_node_pieces) == 2) {
-              $current_node_id = $current_page_node_pieces[0];
-              $current_entity_type = $current_page_node_pieces[1];
-            } else {
-              $current_node_id = $current_node;
-              $current_entity_type = 'node';
-            }
-
-            if ($current_entity_type == 'node') {
-              $node = \Drupal\node\Entity\Node::load($current_node_id);
-              $current_page_title = $node->getTitle();
-              $current_subscribed_node_url = $node->toUrl()->setAbsolute()->toString();
-            } elseif ($current_entity_type == 'term') {
-              $node = Term::load($current_node_id);
-              $current_page_title = $node->getName();
-              $current_subscribed_node_url = \Drupal\Core\Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $node->tid->value], ['absolute' => TRUE])->toString();
-            } else {
-              $node = NULL;
-            }
-            $unsubscribe_link = '';
-            if ($template_info['from_email']) {
-              $from = $template_info['from_email'];
-            } else {
-              $from = \Drupal::config('system.site')->get('mail');
-            }
-            $current_uri_row = \Drupal::request()->getRequestUri();
-            $current_uri_pieces = explode("?", $current_uri_row);
-            $current_uri = $current_uri_pieces[0];
-            //$reload_page_link = $host . $current_uri;
-            $subscription_token = page_notifications_generateRandomString();
-            $confrm_url = $host . "/page-notifications/confirmation/" . $email_notify . "/" . $current_node . "-" . $subscription_token;
-
-            $subject_replacements = [
-              '[notify_user_name]' => '',
-              '[notify_user_email]' => $email_notify,
-              '[notify_verify_url]' => '',
-              '[notify_subscribe_url]' => '',
-              '[notify_unsubscribe_url]' => '',
-              '[notify_user_subscribtions]' => '',
-              '[notify_node_title]' => $current_page_title,
-              '[notify_node_url]' => '',
-              '[notify_notes]' => '',
-            ];
-            $body_replacements = [
-              '[notify_user_name]' => '',
-              '[notify_user_email]' => $email_notify,
-              '[notify_verify_url]' => $confrm_url,
-              '[notify_subscribe_url]' => '',
-              '[notify_unsubscribe_url]' => '',
-              '[notify_user_subscribtions]' => '',
-              '[notify_node_title]' => $current_page_title,
-              '[notify_node_url]' => $current_subscribed_node_url,
-              '[notify_notes]' => '',
-            ];
-            $tokanized_subject = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['verification_email_subject'], $subject_replacements);
-            $tokanized_body = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['verification_email_text'], $body_replacements);
-            $tokanized_web_page_message = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['sent_verify_web_page_message'], $body_replacements);
-            // send an email
-            $message['to'] = $email_notify;
-            $message['subject'] = $tokanized_subject;
-            $message['body'] = $tokanized_body;
-            $result = \Drupal::service('plugin.manager.mail')->mail(
-              'page_notifications',
-              'verification_email',
-              $email_notify,
-              \Drupal::languageManager()->getDefaultLanguage()->getId(),
-              $message
-            );
-
-            if ($result['result'] !== true) {
-              $element['box'] = [
-                '#type' => 'processed_text',
-                '#text' => $this->t('There was an error sending you an email verification. Please contact ' . $from . ' for assistance.'),
-                '#format' => 'full_html',
-              ];
-            } else {
-              $element['box'] = [
-                '#type' => 'processed_text',
-                '#text' => $this->t($tokanized_web_page_message),
-                '#format' => 'full_html',
-              ];
-            }
-          } //end of when record doesn't exist and email needs to be sent
-        }
-      }
-    } else {
-      $replacements = [
-        '[notify_user_name]' => '',
-        '[notify_user_email]' => '',
-        '[notify_verify_url]' => '',
-        '[notify_subscribe_url]' => '',
-        '[notify_unsubscribe_url]' => '',
-        '[notify_user_subscribtions]' => '',
-        '[notify_node_title]' => '',
-        '[notify_node_url]' => '',
-        '[notify_notes]' => '',
-      ];
-      $tokanized_error_web_page_message = \Drupal::service('load.databaseinnfo.service')->page_notifications_process_tokens($template_info['error_web_page_message'], $replacements);
-      $element['box'] = [
-        '#type' => 'processed_text',
-        '#text' => $this->t($tokanized_error_web_page_message),
-        '#format' => 'full_html',
-      ];
-    }
-    return $element;
-  } //promptCallback
-} //class
-
-/**
- * Summary of Drupal\page_notifications\Form\pageNotificationsRecaptchaHtml
- * @return string
- */
-function pageNotificationsRecaptchaHtml()
-{
-  $site_key = \Drupal::config('recaptcha.settings')->get('site_key');
-  if (!is_null($site_key)) {
-    define('RECAPTCHA_SITEKEY', $site_key);
-    return '<div class="g-recaptcha mb-3" data-sitekey="' . RECAPTCHA_SITEKEY . '"></div>';
-  }
-}
-
-/**
- * Summary of Drupal\page_notifications\Form\pageNotificationsPost
- * @param string $url
- * @param array $postdata
- * @return bool|string
- */
-function pageNotificationsPost(string $url, array $postdata)
-{
-  $content = http_build_query($postdata);
-  $opts = [
-    'http' =>
-      [
-        'method' => 'POST',
-        'header' => 'Content-Type: application/x-www-form-urlencoded',
-        'content' => $content
-      ]
-  ];
-  $context = stream_context_create($opts);
-  $result = file_get_contents($url, false, $context);
-  return $result;
-}
-
-function pageNotificationsRecaptchaVerify()
-{
-  if (!isset($_POST['g-recaptcha-response'])) {
-    return false;
-  }
-  $response = filter_input(INPUT_POST, 'g-recaptcha-response', FILTER_SANITIZE_STRING);
-  $secret_key = \Drupal::config('recaptcha.settings')->get('secret_key');
-  define('RECAPTCHA_SECRETKEY', $secret_key);
-  define('RECAPTCHA_URL', 'https://www.google.com/recaptcha/api/siteverify');
-  $json = pageNotificationsPost(RECAPTCHA_URL, ['secret' => RECAPTCHA_SECRETKEY, 'response' => $response]);
-  $data = json_decode($json, true);
-  if ($data['success'] == 1) {
-    return true;
-  }
-  return false;
-}
-
-function page_notifications_generateRandomString($length = 10)
-{
-  $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  $charactersLength = strlen($characters);
-  $randomString = '';
-  for ($i = 0; $i < $length; $i++) {
-    $randomString .= $characters[rand(0, $charactersLength - 1)];
-  }
-  return $randomString;
-}
diff --git a/src/Form/PurgeSubscriptionsConfirmForm.php b/src/Form/PurgeSubscriptionsConfirmForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..ee37e5befee31a16d5d0b6043e93e3c4d152fd42
--- /dev/null
+++ b/src/Form/PurgeSubscriptionsConfirmForm.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PurgeSubscriptionsConfirmForm extends ConfirmFormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new PurgeSubscriptionsConfirmForm.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_purge_subscriptions_confirm';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    $count = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->accessCheck(FALSE)
+      ->count()
+      ->execute();
+
+    return $this->t('Are you sure you want to delete all @count subscriptions?', [
+      '@count' => $count,
+    ]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->t('This action cannot be undone. All subscription data will be permanently deleted.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('page_notifications.settings');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $batch = [
+      'title' => $this->t('Deleting all subscriptions...'),
+      'operations' => [
+        [[$this, 'purgeSubscriptionsBatch'], []],
+      ],
+      'finished' => [[$this, 'purgeSubscriptionsFinished']],
+    ];
+    batch_set($batch);
+    $form_state->setRedirect('page_notifications.settings');
+  }
+
+  /**
+   * Batch operation to purge subscriptions.
+   */
+  public function purgeSubscriptionsBatch(&$context) {
+    if (!isset($context['sandbox']['progress'])) {
+      $context['sandbox']['progress'] = 0;
+      $context['sandbox']['current_id'] = 0;
+      $context['sandbox']['max'] = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->getQuery()
+        ->accessCheck(FALSE)
+        ->count()
+        ->execute();
+    }
+
+    $subscription_ids = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('id', $context['sandbox']['current_id'], '>')
+      ->sort('id')
+      ->range(0, 50)
+      ->accessCheck(FALSE)
+      ->execute();
+
+    if (!empty($subscription_ids)) {
+      $storage = $this->entityTypeManager->getStorage('page_notification_subscription');
+      $entities = $storage->loadMultiple($subscription_ids);
+      $storage->delete($entities);
+
+      $context['sandbox']['current_id'] = end($subscription_ids);
+      $context['sandbox']['progress'] += count($subscription_ids);
+    }
+
+    $context['finished'] = empty($subscription_ids) ? 1 : $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+
+  /**
+   * Batch finished callback.
+   */
+  public function purgeSubscriptionsFinished($success, $results, $operations) {
+    if ($success) {
+      $this->messenger()->addStatus($this->t('Successfully deleted all subscriptions.'));
+    }
+    else {
+      $this->messenger()->addError($this->t('An error occurred while deleting subscriptions.'));
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..2fab3c68e16f394a3bb628bb3c97c14bb3b2b704
--- /dev/null
+++ b/src/Form/SettingsForm.php
@@ -0,0 +1,433 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\filter\FilterFormatInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Link;
+
+/**
+ * Configures Page Notifications settings.
+ */
+class SettingsForm extends ConfigFormBase {
+
+  /**
+   * The mail manager.
+   *
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a SettingsForm object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The factory for configuration objects.
+   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+   *   The mail manager service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *  The module handler service.
+   */
+  public function __construct(
+    ConfigFactoryInterface $config_factory,
+    MailManagerInterface $mail_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    ModuleHandlerInterface $module_handler
+  ) {
+    // Check Drupal version
+    if (version_compare(\Drupal::VERSION, '11.0', '>=')) {
+      parent::__construct($config_factory, \Drupal::service('config.typed'));
+  } else {
+      parent::__construct($config_factory);
+  }
+    $this->mailManager = $mail_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('plugin.manager.mail'),
+      $container->get('entity_type.manager'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_settings';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['page_notifications.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildForm($form, $form_state);
+    $config = $this->config('page_notifications.settings');
+
+    // Get available text formats for the current user
+    $formats = filter_formats(\Drupal::currentUser());
+    $format_options = [];
+    foreach ($formats as $format) {
+      $format_options[$format->id()] = $format->label();
+    }
+
+
+    $form['email_settings'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Email Settings'),
+      '#open' => TRUE,
+    ];
+
+
+    $verification_body = $config->get('email_templates.verification_body');
+    $notification_body = $config->get('email_templates.notification_body');
+    $already_subscribed_body = $config->get('email_templates.already_subscribed_body');
+
+    $form['email_settings']['from_email'] = [
+      '#type' => 'email',
+      '#title' => $this->t('From Email Address'),
+      '#description' => $this->t('The email address that notifications will be sent from. If left empty, the site default will be used.'),
+      '#default_value' => $config->get('notification_settings.from_email'),
+    ];
+
+    $form['email_settings']['token_expiration'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Token Expiration'),
+      '#description' => $this->t('Number of hours before verification tokens expire. Enter 0 to never expire unverified subscriptions.'),
+      '#default_value' => $config->get('notification_settings.token_expiration') ?? 48,
+      '#min' => 1,
+      '#required' => TRUE,
+    ];
+
+    $form['email_templates'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Email Templates'),
+      '#open' => TRUE,
+    ];
+
+    $form['email_templates']['verification_subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Verification Email Subject'),
+      '#default_value' => $config->get('email_templates.verification_subject') ?? 'Verify your subscription to [node:title]',
+      '#required' => TRUE,
+    ];
+
+    $form['email_templates']['verification_body'] = [
+      '#type' => 'text_format',
+      '#title' => $this->t('Verification Email Body'),
+      '#default_value' => is_array($verification_body) ? $verification_body['value'] : $verification_body,
+      '#format' => is_array($verification_body) ? $verification_body['format'] : NULL,
+      '#description' => $this->t('Available tokens: [subscription:verify-url], [subscription:email], [node:title], [node:url]'),
+      '#required' => TRUE,
+      '#rows' => 10,
+    ];
+
+    $form['email_templates']['notification_subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Update Notification Subject'),
+      '#default_value' => $config->get('email_templates.notification_subject') ?? '[node:title] has been updated',
+      '#required' => TRUE,
+    ];
+
+    $form['email_templates']['notification_body'] = [
+      '#type' => 'text_format',
+      '#title' => $this->t('Update Notification Body'),
+      '#default_value' => is_array($notification_body) ? $notification_body['value'] : $notification_body,
+      '#format' => is_array($notification_body) ? $notification_body['format'] : NULL,
+      '#description' => $this->t('Available tokens: [subscription:email], [node:title], [node:url], [node:changed], [subscription:unsubscribe-url]'),
+      '#required' => TRUE,
+      '#rows' => 10,
+    ];
+
+    $form['email_templates']['already_subscribed_subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Already Subscribed Email Subject'),
+      '#default_value' => $config->get('email_templates.already_subscribed_subject') ?? 'You are already subscribed to [node:title]',
+      '#required' => TRUE,
+    ];
+
+    $form['email_templates']['already_subscribed_body'] = [
+      '#type' => 'text_format',
+      '#title' => $this->t('Already Subscribed Email Body'),
+      '#default_value' => is_array($already_subscribed_body) ? $already_subscribed_body['value'] : $already_subscribed_body,
+      '#format' => is_array($already_subscribed_body) ? $already_subscribed_body['format'] : NULL,
+      '#description' => $this->t('Available tokens: [subscription:email], [node:title], [node:url], [subscription:unsubscribe-url]'),
+      '#required' => TRUE,
+      '#rows' => 10,
+    ];
+
+    $form['security'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Security Settings'),
+      '#open' => TRUE,
+    ];
+
+    $form['security']['require_verification'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Require Email Verification'),
+      '#description' => $this->t('If checked, users must verify their email address before the subscription becomes active.'),
+      '#default_value' => $config->get('security.require_verification') ?? TRUE,
+    ];
+
+     // Flood control settings
+     $form['security']['flood_control'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Flood Control Settings'),
+      '#open' => TRUE,
+      '#tree' => TRUE,
+    ];
+
+    $form['security']['flood_control']['ip_limit'] = [
+      '#type' => 'number',
+      '#title' => $this->t('IP-based attempt limit'),
+      '#description' => $this->t('Maximum number of subscription attempts allowed from a single IP address.'),
+      '#default_value' => $config->get('security.flood_control.ip_limit') ?? 200,
+      '#min' => 1,
+      '#required' => TRUE,
+    ];
+
+    $form['security']['flood_control']['ip_window'] = [
+      '#type' => 'number',
+      '#title' => $this->t('IP-based time window'),
+      '#description' => $this->t('Time window in hours for IP-based subscription attempts. Set to 0 to disable IP-based flood control.'),
+      '#default_value' => $config->get('security.flood_control.ip_window') ?? 1,
+      '#min' => 0, // Changed from 1 to 0
+      '#required' => TRUE,
+      '#field_suffix' => $this->t('hours'),
+    ];
+
+    $form['security']['flood_control']['identifier_limit'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Email-based attempt limit'),
+      '#description' => $this->t('Maximum number of subscription attempts allowed for the same email address.'),
+      '#default_value' => $config->get('security.flood_control.identifier_limit') ?? 50,
+      '#min' => 1,
+      '#required' => TRUE,
+    ];
+
+    $form['security']['flood_control']['identifier_window'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Email-based time window'),
+      '#description' => $this->t('Time window in hours for email-based subscription attempts. Set to 0 to disable email-based flood control.'),
+      '#default_value' => $config->get('security.flood_control.identifier_window') ?? 1,
+      '#min' => 0, // Changed from 1 to 0
+      '#required' => TRUE,
+      '#field_suffix' => $this->t('hours'),
+    ];
+
+    // Add spam prevention section
+    $form['spam_prevention'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Spam Prevention'),
+      '#open' => TRUE,
+    ];
+
+    $captcha_options = [
+      'none' => $this->t('None'),
+      'math' => $this->t('Simple Math Challenge'),
+    ];
+
+    // Add reCAPTCHA option if the captcha module is installed
+    if ($this->moduleHandler->moduleExists('captcha')) {
+      $captcha_options['recaptcha'] = $this->t('reCAPTCHA');
+    }
+
+    $form['spam_prevention']['captcha_type'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Captcha Type'),
+      '#options' => $captcha_options,
+      '#default_value' => $config->get('spam_prevention.captcha_type') ?? 'none',
+      '#description' => $this->t('Select the type of spam prevention to use on the subscription form.'),
+    ];
+
+    $form['spam_prevention']['math_operator'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Math Challenge Operator'),
+      '#options' => [
+        '+' => $this->t('Addition (+)'),
+        '*' => $this->t('Multiplication (*)'),
+      ],
+      '#default_value' => $config->get('spam_prevention.math_operator') ?? '+',
+      '#description' => $this->t('Select the operator to use for the math challenge.'),
+      '#states' => [
+        'visible' => [
+          ':input[name="captcha_type"]' => ['value' => 'math'],
+        ],
+      ],
+    ];
+
+    if ($this->moduleHandler->moduleExists('captcha')) {
+      $form['spam_prevention']['use_recaptcha'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Use reCAPTCHA if available'),
+        '#default_value' => $config->get('spam_prevention.use_recaptcha') ?? FALSE,
+        '#description' => $this->t('Enable this to use reCAPTCHA if the captcha module is configured to use it.'),
+        '#states' => [
+          'visible' => [
+            ':input[name="captcha_type"]' => ['value' => 'recaptcha'],
+          ],
+        ],
+      ];
+    }
+
+    $form['danger_zone'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Danger Zone'),
+      '#description' => $this->t('These actions cannot be undone.'),
+      '#open' => FALSE,
+      '#weight' => 100,
+    ];
+
+    $subscription_count = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->accessCheck(FALSE)
+      ->count()
+      ->execute();
+
+      $form['danger_zone']['purge_subscriptions'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Delete all subscriptions (@count total)', ['@count' => $subscription_count]),
+        '#url' => Url::fromRoute('page_notifications.purge_subscriptions_confirm'),
+        '#attributes' => [
+          'class' => ['button', 'button--danger', 'use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => json_encode([
+            'width' => 700,
+          ]),
+        ],
+        '#disabled' => ($subscription_count === 0),
+      ];
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->getValue('from_email') && !$this->mailManager->validateAddress($form_state->getValue('from_email'))) {
+      $form_state->setErrorByName('from_email', $this->t('The email address is not valid.'));
+    }
+  }
+
+  public function purgeSubscriptions(array &$form, FormStateInterface $form_state) {
+    $batch = [
+      'title' => $this->t('Deleting all subscriptions...'),
+      'operations' => [
+        [[$this, 'purgeSubscriptionsBatch'], []],
+      ],
+      'finished' => [[$this, 'purgeSubscriptionsFinished']],
+    ];
+    batch_set($batch);
+  }
+
+  public function purgeSubscriptionsBatch(&$context) {
+    if (!isset($context['sandbox']['progress'])) {
+      $context['sandbox']['progress'] = 0;
+      $context['sandbox']['current_id'] = 0;
+      $context['sandbox']['max'] = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->getQuery()
+        ->accessCheck(FALSE)
+        ->count()
+        ->execute();
+    }
+
+    // Process subscriptions in chunks of 50
+    $subscription_ids = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('id', $context['sandbox']['current_id'], '>')
+      ->sort('id')
+      ->range(0, 50)
+      ->accessCheck(FALSE)
+      ->execute();
+
+    if (!empty($subscription_ids)) {
+      $storage = $this->entityTypeManager->getStorage('page_notification_subscription');
+      $entities = $storage->loadMultiple($subscription_ids);
+      $storage->delete($entities);
+
+      $context['sandbox']['current_id'] = end($subscription_ids);
+      $context['sandbox']['progress'] += count($subscription_ids);
+    }
+
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+
+  public function purgeSubscriptionsFinished($success, $results, $operations) {
+    if ($success) {
+      $this->messenger()->addStatus($this->t('Successfully deleted all subscriptions.'));
+    }
+    else {
+      $this->messenger()->addError($this->t('An error occurred while deleting subscriptions.'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValues();
+
+    $this->config('page_notifications.settings')
+      ->set('notification_settings.from_email', $values['from_email'])
+      ->set('notification_settings.token_expiration', $values['token_expiration'])
+      ->set('email_templates.verification_subject', $values['verification_subject'])
+      ->set('email_templates.verification_body', $values['verification_body'])
+      ->set('email_templates.already_subscribed_subject', $values['already_subscribed_subject'])
+->set('email_templates.already_subscribed_body', $values['already_subscribed_body'])
+      ->set('email_templates.notification_subject', $values['notification_subject'])
+      ->set('email_templates.notification_body', $values['notification_body'])
+      ->set('security.require_verification', $values['require_verification'])
+      ->set('security.flood_control.ip_limit', $values['flood_control']['ip_limit'])
+      ->set('security.flood_control.ip_window', $values['flood_control']['ip_window'])
+      ->set('security.flood_control.identifier_limit', $values['flood_control']['identifier_limit'])
+      ->set('security.flood_control.identifier_window', $values['flood_control']['identifier_window'])
+      ->set('spam_prevention.captcha_type', $values['captcha_type'])
+      ->set('spam_prevention.math_operator', $values['math_operator'])
+      ->set('spam_prevention.use_recaptcha', $values['use_recaptcha'] ?? FALSE)
+      ->save();
+
+    parent::submitForm($form, $form_state);
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/SubscriptionDeleteForm.php b/src/Form/SubscriptionDeleteForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a2a8dd144f0718e07e9e2d2bbf7e5e5dc05ba77
--- /dev/null
+++ b/src/Form/SubscriptionDeleteForm.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Entity\ContentEntityDeleteForm;
+
+class SubscriptionDeleteForm extends ContentEntityDeleteForm {
+}
\ No newline at end of file
diff --git a/src/Form/SubscriptionForm.php b/src/Form/SubscriptionForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b0b840f5ded924240acc054a9c75f0f647cde48
--- /dev/null
+++ b/src/Form/SubscriptionForm.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\page_notifications\Service\NotificationManagerInterface;
+use Drupal\page_notifications\Service\SpamPrevention;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Psr\Log\LoggerInterface;
+use Drupal\Core\Flood\FloodInterface;
+use Drupal\page_notifications\Traits\FloodControlTrait;
+
+/**
+ * Provides a subscription form.
+ */
+class SubscriptionForm extends FormBase {
+  use FloodControlTrait;
+
+  /**
+   * The notification manager service.
+   *
+   * @var \Drupal\page_notifications\Service\NotificationManagerInterface
+   */
+  protected $notificationManager;
+
+  /**
+   * The spam prevention service.
+   *
+   * @var \Drupal\page_notifications\Service\SpamPrevention
+   */
+  protected $spamPrevention;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The logger instance.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * Constructs a new SubscriptionForm.
+   *
+   * @param \Drupal\page_notifications\Service\NotificationManagerInterface $notification_manager
+   *   The notification manager service.
+   * @param \Drupal\page_notifications\Service\SpamPrevention $spam_prevention
+   *   The spam prevention service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   The logger instance.
+   * @param \Drupal\Core\Flood\FloodInterface $flood
+   *   The flood service.
+   */
+  public function __construct(
+    NotificationManagerInterface $notification_manager,
+    SpamPrevention $spam_prevention,
+    ConfigFactoryInterface $config_factory,
+    LoggerInterface $logger,
+    FloodInterface $flood
+  ) {
+    $this->notificationManager = $notification_manager;
+    $this->spamPrevention = $spam_prevention;
+    $this->logger = $logger;
+    $this->configFactory = $config_factory;
+    $this->setFloodService($flood);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('page_notifications.notification_manager'),
+      $container->get('page_notifications.spam_prevention'),
+      $container->get('config.factory'),
+      $container->get('logger.factory')->get('page_notifications'),
+      $container->get('flood')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_subscription_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL) {
+    $form_state->set('entity', $entity);
+
+    $form['email'] = [
+      '#type' => 'email',
+      '#title' => $this->t('Email address'),
+      '#required' => TRUE,
+      '#description' => $this->t('Enter your email address to receive notifications when this content is updated.'),
+    ];
+
+    // Add spam prevention based on configuration
+    $config = $this->configFactory->get('page_notifications.settings');
+    $captcha_type = $config->get('spam_prevention.captcha_type');
+
+    if ($captcha_type === 'math') {
+      // Store challenge data in a hidden field to persist through form submissions
+      if (!$form_state->getUserInput()) {
+        // Only generate new challenge if this is the initial form build
+        $challenge = $this->spamPrevention->generateMathChallenge();
+
+        $form['math_challenge_data'] = [
+          '#type' => 'hidden',
+          '#value' => json_encode($challenge),
+        ];
+      }
+      else {
+        // Use existing challenge data from form input
+        $challenge = json_decode($form_state->getUserInput()['math_challenge_data'] ?? '{}', TRUE);
+      }
+
+      if (!empty($challenge)) {
+        $form['math_challenge_data'] = [
+          '#type' => 'hidden',
+          '#value' => json_encode($challenge),
+        ];
+
+        $form['math_challenge'] = [
+          '#type' => 'number',
+          '#title' => $challenge['question'],
+          '#required' => TRUE,
+          '#description' => $this->t('Please solve this simple math problem to prevent spam.'),
+        ];
+      }
+    }
+    elseif ($captcha_type === 'recaptcha' && $this->spamPrevention->isRecaptchaAvailable()) {
+      $form['captcha'] = [
+        '#type' => 'captcha',
+        '#captcha_type' => 'recaptcha/reCAPTCHA',
+      ];
+    }
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Subscribe'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $email = $form_state->getValue('email');
+
+    // Check flood control before other validation
+    if (!$this->checkFloodControl($email, $form_state)) {
+      return;
+    }
+
+    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
+      $form_state->setErrorByName('email', $this->t('Please enter a valid email address.'));
+      return;
+    }
+
+    // Validate math challenge if enabled
+    $config = $this->configFactory->get('page_notifications.settings');
+    $captcha_type = $config->get('spam_prevention.captcha_type');
+
+    if ($captcha_type === 'math') {
+      $challenge_data = $form_state->getValue('math_challenge_data');
+      if ($challenge_data) {
+        $challenge = json_decode($challenge_data, TRUE);
+        $response = $form_state->getValue('math_challenge');
+
+        if (!$this->spamPrevention->validateMathResponse($response, $challenge)) {
+          $form_state->setErrorByName('math_challenge', $this->t('The answer to the math challenge is incorrect.'));
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $entity = $form_state->get('entity');
+    $email = $form_state->getValue('email');
+
+    try {
+      // Register flood control event
+      $this->registerFloodControl($email);
+
+      $subscription = $this->notificationManager->createSubscription($email, $entity);
+      $this->messenger()->addStatus($this->t('Thank you for subscribing. Please check your email to confirm your subscription.'));
+
+      // Clear the email field after successful submission
+      $form_state->setValue('email', '');
+      $form_state->setUserInput(['email' => '']);
+    }
+    catch (\Exception $e) {
+      $this->messenger()->addError($this->t('There was a problem creating your subscription. Please try again later.'));
+      $this->logger->error('Subscription creation failed: @message', ['@message' => $e->getMessage()]);
+    }
+
+    // Set form to rebuild
+    $form_state->setRebuild(TRUE);
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/SubscriptionMigrateForm.php b/src/Form/SubscriptionMigrateForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..416c9c08fe238eec2e91b1c093f0f7e1b2668319
--- /dev/null
+++ b/src/Form/SubscriptionMigrateForm.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace Drupal\page_notifications\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form for migrating subscriptions between nodes.
+ */
+class SubscriptionMigrateForm extends FormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new SubscriptionMigrateForm.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_notifications_subscription_migrate_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    // Check if there are any nodes with subscriptions
+    $subscription_count = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('subscribed_entity_type', 'node')
+      ->condition('status', TRUE)
+      ->count()
+      ->accessCheck(FALSE)
+      ->execute();
+
+    if ($subscription_count === 0) {
+      $form['message'] = [
+        '#markup' => $this->t('There are no nodes with active subscriptions.'),
+      ];
+      return $form;
+    }
+
+    $form['description'] = [
+      '#markup' => $this->t('This form will migrate all active subscriptions from one node to another.'),
+    ];
+
+    $form['source_node'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => $this->t('FROM: Source Node'),
+      '#description' => $this->t('Select the node from which to migrate subscriptions.'),
+      '#target_type' => 'node',
+      '#required' => TRUE,
+      '#selection_handler' => 'default:node_with_subscriptions',
+    ];
+
+    $form['target_node'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => $this->t('TO: Target Node'),
+      '#description' => $this->t('Select the node to which subscriptions will be migrated.'),
+      '#target_type' => 'node',
+      '#required' => TRUE,
+      '#selection_handler' => 'default:node_enhanced',
+      '#selection_settings' => [
+        'target_bundles' => NULL,
+      ],
+    ];
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Migrate Subscriptions'),
+      '#button_type' => 'primary',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $source_nid = $form_state->getValue('source_node');
+    $target_nid = $form_state->getValue('target_node');
+
+    if ($source_nid === $target_nid) {
+      $form_state->setError($form['target_node'], $this->t('Source and target nodes must be different.'));
+      return;
+    }
+
+    // Verify the source node still has subscriptions (in case they were deleted)
+    $subscription_count = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('subscribed_entity_id', $source_nid)
+      ->condition('subscribed_entity_type', 'node')
+      ->condition('status', TRUE)
+      ->count()
+      ->accessCheck(FALSE)
+      ->execute();
+
+    if ($subscription_count === 0) {
+      $form_state->setError($form['source_node'], $this->t('The source node has no active subscriptions to migrate.'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $source_nid = $form_state->getValue('source_node');
+    $target_nid = $form_state->getValue('target_node');
+
+    try {
+      $batch = [
+        'title' => $this->t('Migrating subscriptions'),
+        'operations' => [
+          [
+            [$this, 'processMigration'],
+            [$source_nid, $target_nid],
+          ],
+        ],
+        'finished' => [$this, 'migrationFinished'],
+      ];
+
+      batch_set($batch);
+    }
+    catch (\Exception $e) {
+      $this->messenger()->addError($this->t('An error occurred while preparing the migration: @error', [
+        '@error' => $e->getMessage(),
+      ]));
+    }
+  }
+
+  /**
+   * Batch operation callback for migrating subscriptions.
+   */
+  public function processMigration($source_nid, $target_nid, &$context) {
+    if (!isset($context['sandbox']['progress'])) {
+      $context['sandbox']['progress'] = 0;
+      $context['sandbox']['current_id'] = 0;
+      $context['sandbox']['max'] = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->getQuery()
+        ->condition('subscribed_entity_id', $source_nid)
+        ->condition('subscribed_entity_type', 'node')
+        ->count()
+        ->accessCheck(FALSE)
+        ->execute();
+    }
+
+    // Process subscriptions in chunks of 50
+    $subscription_ids = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('subscribed_entity_id', $source_nid)
+      ->condition('subscribed_entity_type', 'node')
+      ->condition('id', $context['sandbox']['current_id'], '>')
+      ->sort('id')
+      ->range(0, 50)
+      ->accessCheck(FALSE)
+      ->execute();
+
+    foreach ($subscription_ids as $id) {
+      /** @var \Drupal\page_notifications\Entity\Subscription $subscription */
+      $subscription = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->load($id);
+
+      // Simply update the entity ID
+      $subscription->setSubscribedEntityId($target_nid);
+      $subscription->save();
+
+      $context['sandbox']['progress']++;
+      $context['sandbox']['current_id'] = $id;
+    }
+
+    if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+    }
+  }
+
+  /**
+   * Batch finished callback.
+   */
+  public function migrationFinished($success, $results, $operations) {
+    if ($success) {
+      $this->messenger()->addStatus($this->t('Successfully migrated all subscriptions to the new node.'));
+    }
+    else {
+      $this->messenger()->addError($this->t('An error occurred while migrating subscriptions.'));
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/Form/UserSubscriptionsPage.php b/src/Form/UserSubscriptionsPage.php
deleted file mode 100644
index 2d14f5a3b8c7c0764cd3c883648f1ada2989330a..0000000000000000000000000000000000000000
--- a/src/Form/UserSubscriptionsPage.php
+++ /dev/null
@@ -1,350 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Mail\MailManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Component\Utility\EmailValidator;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\Core\Url;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Drupal\node\Entity\Node;
-
-/**
- * Provides a form with two steps.
- *
- * This example demonstrates a multistep form with text input elements. We
- * extend FormBase which is the simplest form base class used in Drupal.
- *
- * @see \Drupal\Core\Form\FormBase
- */
-class UserSubscriptionsPage extends FormBase {
-  /**
-   * The mail manager.
-   *
-   * @var \Drupal\Core\Mail\MailManagerInterface
-   */
-  protected $mailManager;
-
-  /**
-   * The email validator.
-   *
-   * @var \Drupal\Component\Utility\EmailValidator
-   */
-  protected $emailValidator;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a new EmailUnsubscribePage.
-   *
-   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
-   *   The mail manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Component\Utility\EmailValidator $email_validator
-   *   The email validator.
-   */
-  public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, EmailValidator $email_validator) {
-    $this->mailManager = $mail_manager;
-    $this->languageManager = $language_manager;
-    $this->emailValidator = $email_validator;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $form = new static(
-      $container->get('plugin.manager.mail'),
-      $container->get('language_manager'),
-      $container->get('email.validator')
-    );
-    $form->setMessenger($container->get('messenger'));
-    $form->setStringTranslation($container->get('string_translation'));
-    return $form;
-  }
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page-notifications-user-subscriptions-list';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state,  $subscription_token = NULL) {
-
-    if($subscription_token != strip_tags($subscription_token) || is_null($subscription_token)) {
-      \Drupal::messenger()->addError(t('You link might be broken or incomplete. Please make sure you dont have extra space in your address link.'));
-    } else {
-      $host = \Drupal::request()->getSchemeAndHttpHost();
-      if (is_string($subscription_token) && !is_null($subscription_token)) {
-        $part_subscription_token = explode("-", $subscription_token);
-        if (strlen($part_subscription_token[1]) == 10) {
-          if ($form_state->has('page_num') && $form_state->get('page_num') == 2) {
-            return self::userSubscriptionsPageTwo($form, $form_state);
-          }
-          $form_state->set('page_num', 1);
-          $form['subscription_token'] = [
-            '#type' => 'hidden',
-            '#value' => $subscription_token,
-          ];
-          $form['email_check'] = [
-            '#type' => 'textfield',
-            '#title' => $this->t('Enter your E-mail Address:'),
-            '#description' => $this->t('Please enter your email for varification.'),
-            '#default_value' => $form_state->getValue('email_check', ''),
-            '#required' => TRUE,
-          ];
-          $form['actions'] = [
-            '#type' => 'actions',
-          ];
-          $form['actions']['next'] = [
-            '#type' => 'submit',
-            '#button_type' => 'primary',
-            '#value' => $this->t('Next'),
-            '#submit' => ['::userSubscriptionsPageNextSubmit'],
-            '#validate' => ['::userSubscriptionsPageNextValidate'],
-          ];
-        }
-
-      } else {
-        $form['intro'] = [
-          '#markup' => $this->t('<p>You link might be broken or incomplete.</p>'),
-        ];
-      }
-    }
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-
-  }
-
-  /**
-   * Provides custom validation handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function userSubscriptionsPageNextValidate(array &$form, FormStateInterface $form_state) {
-    if (!$this->emailValidator->isValid($form_state->getValue('email_check')) || is_null($form_state->getValue('email_check'))) {
-      $form_state->setErrorByName('email_check', $this->t('That e-mail address is not valid.'));
-    } else {
-      $email_check = strip_tags($form_state->getValue('email_check'));
-      $subscription_token = strip_tags($form_state->getValue('subscription_token'));
-      if(is_string($email_check) && is_string($subscription_token)) {
-        $record = \Drupal::service('load.databaseinnfo.service')->verifyByNodeAndEmail($form_state->getValue('email_check'), $form_state->getValue('subscription_token'));
-        if ($record == true) {
-          $email_verify = $email_check;
-        } else {
-          $form_state->setErrorByName('email_check', $this->t('Incorect E-Mail.'));
-          \Drupal::messenger()->addError(t("We don't have subscription for this email."));
-        }
-      }
-    }
-  }
-
-  /**
-   * Provides custom submission handler for page 1.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function userSubscriptionsPageNextSubmit(array &$form, FormStateInterface $form_state) {
-    $email_check = $form_state->getValue('email_check');
-    $form_state
-      ->set('page_values', [
-        'email_check' => $form_state->getValue('email_check'),
-        'subscription_token' => $form_state->getValue('subscription_token'),
-      ])
-      ->set('page_num', 2)
-      ->setRebuild(TRUE);
-  }
-
-  /**
-   * Builds the second step form (page 2).
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return array
-   *   The render array defining the elements of the form.
-   */
-  public function userSubscriptionsPageTwo(array &$form, FormStateInterface $form_state) {
-    $part_subscription_token = explode("-", $form_state->getValue('subscription_token'));
-    $node_id_unsubscribe = $part_subscription_token[0];
-    $subscription_token = $part_subscription_token[1];
-    $page_notify_user_token = \Drupal::service('load.databaseinnfo.service')->pageNotifyGetUserToken($form_state->getValue('email_check'));
-    $user_token = $page_notify_user_token['field_page_notify_token_user_id'];
-
-    if ($user_token && !is_null($user_token)) {
-      $header = array(
-        array('data' => $this->t('Watching pages list'), 'field' => 'title', 'sort' => 'asc'),
-        array('data' => $this->t('')),
-      );
-      $query = \Drupal::entityQuery('node')
-        ->accessCheck(FALSE)
-        ->condition('type', 'page_notify_subscriptions')
-        ->condition('field_page_notify_token_user_id', $user_token, '=')
-        ->condition('status', 1)
-        ->sort('created', 'DESC')
-        ->pager(10);
-      $records = $query->execute();
-      $rows = array();
-      foreach ($records as $record) {
-        $node_record = \Drupal\node\Entity\Node::load($record);
-        $field_token_notify = $node_record->get("field_page_notify_token")->getValue();
-        $field_node_id_notify = $node_record->get("field_page_notify_node_id")->getValue();
-
-        $subscriptions_record = \Drupal\node\Entity\Node::load($field_node_id_notify[0]['value']);
-          $rows[] = array('data' => array(
-            'title' => new FormattableMarkup('<a href="@page_url">@page_title</a>',
-              [
-                '@page_title' => $subscriptions_record->getTitle(),
-                '@page_url' => $subscriptions_record->toUrl()->toString(),
-              ]),
-            'cancel_one' => new FormattableMarkup('<a id="notify-cancel-@token" href="/nojs/cancel_subscription/@token" class="use-ajax btn btn-default notify-cancel-subscription">@name</a>',
-                ['@name' => 'Stop Watching', '@token' => $field_token_notify[0]['value']]
-              ),
-          ));
-
-        if ($rows) {
-          $cancelall =  '<a id="notify-cancel-all" href="/nojs/cancel_all/' . $user_token .'" class="use-ajax btn btn-default notify-cancel-all-subscription">Unsubscribe from all</a>';
-          $header = array(
-            array('data' => $this->t('Page Name'), 'field' => 'title', 'sort' => 'asc'),
-            array('data' => $this->t($cancelall)),
-          );
-        } else {
-          $build = array();
-        }
-      }
-
-      /*$page_name = '<h1>Manage Your Page Watching Subscriptions</h1>';
-      $build['page_name'] = [
-        '#markup' => $page_name,
-        '#attributes' => [
-          'class' => ['page-notifications-user-list-page-name'],
-        ],
-      ];*/
-      $build['config_table'] = array(
-        '#theme' => 'table',
-        '#header' => $header,
-        '#rows' => $rows,
-        '#empty' => t('No records found'),
-        '#attributes' => [
-          'class' => ['page-notifications-block-subscriberpage'],
-          'id' => 'page-notifications-block-subscriberpage',
-          'no_striping' => TRUE,
-        ],
-      );
-      $build['pager'] = array(
-        '#type' => 'pager'
-      );
-      return $build;
-
-    } else {
-      $url = \Drupal\Core\Url::fromRoute('<front>')->toString();
-      $response = new RedirectResponse($url);
-      $response->send();
-      \Drupal::messenger()->addError(t('You link might be broken or incomplete.'));
-    }
-    //return $form;
-  }
-
-  public function rebuildFormSubmit(array &$form, FormStateInterface $form_state) {
-    $this->displayMethodInvocation('rebuildFormSubmit');
-    $form_state->setRebuild(TRUE);
-  }
-
-  public function cancel_subscription($token) {
-      $response = new AjaxResponse();
-      page_notifications_user_delete_record($token);
-      $response->addCommand(new ReplaceCommand('#notify-cancel-' . $token, '<span class="notify-cancel-cancelled">Cancelled</span>'));
-      return $response;
-  }
-
-  public function cancel_all($user_token) {
-      $response = new AjaxResponse();
-      page_notifications_user_delete_all_records($user_token);
-      $response->addCommand(new ReplaceCommand('#notify-cancel-all', '<span class="notify-cancel-all-cancelled">All cancelled</span>'));
-      $response->addCommand(new ReplaceCommand('#notify-cancel-', "Cancelled"));
-      return $response;
-  }
-}
-
-function checkForRecord($subscription_token_url, $email) {
-  $record = current(\Drupal::entityTypeManager()->getStorage('node')
-    ->loadByProperties([
-      'field_page_notify_token' => $subscription_token_url,
-      'field_page_notify_email' => $email
-    ])
-  );
-  if ($record) {
-    return $record;
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function getAllRecords($email) {
-  $query = \Drupal::entityQuery('node')
-    ->accessCheck(FALSE)
-    ->condition('status', 1)
-    ->condition('field_page_notify_email', $email, '=');
-  $records = $query->execute();
-  foreach ($records as $key => $record) {
-    $node = \Drupal\node\Entity\Node::load($record);
-    $nodes[] = $node;
-  }
-  if ($nodes) {
-    return $nodes;
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function page_notifications_user_delete_record($token) {
-  $num_deleted = \Drupal::entityQuery("node")
-    ->accessCheck(FALSE)
-    ->condition("type", "page_notify_subscriptions")
-    ->condition("field_page_notify_token", $token)
-    ->accessCheck(FALSE)
-    ->execute();
-  $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-  $entities = $storage_handler->loadMultiple($num_deleted);
-  $storage_handler->delete($entities);
-}
-
-function page_notifications_user_delete_all_records($user_token) {
-  $num_deleted = \Drupal::entityQuery("node")
-    ->accessCheck(FALSE)
-    ->condition("type", "page_notify_subscriptions")
-    ->condition("field_page_notify_token_user_id", $user_token)
-    ->accessCheck(FALSE)
-    ->execute();
-  $storage_handler = \Drupal::entityTypeManager()->getStorage("node");
-  $entities = $storage_handler->loadMultiple($num_deleted);
-  $storage_handler->delete($entities);
-}
diff --git a/src/LoadDataBaseInfo.php b/src/LoadDataBaseInfo.php
deleted file mode 100644
index a4f009d671130ef7f535b8a599092c583d3a4fd9..0000000000000000000000000000000000000000
--- a/src/LoadDataBaseInfo.php
+++ /dev/null
@@ -1,321 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications;
-
-/**
- * Class LoadDataBaseInfo.
- *
- * @package Drupal\page_notifications\src
- */
-
-class LoadDataBaseInfo
-{
-
-  public function get_notify_email_template()
-  {
-    $sql = "SELECT * FROM page_notify_email_template ORDER BY template_id DESC LIMIT 1";
-    $result = \Drupal::database()->query($sql);
-    $template = [];
-    if ($result) {
-      while ($row = $result->fetchAssoc()) {
-        $template = [
-          'template_id' => $row['template_id'],
-          'body' => $row['body'],
-          'from_email' => $row['from_email'],
-          'checkbox_field' => $row['checkbox_field'],
-          'notes_field' => $row['notes_field'],
-          'node_timestamp' => $row['node_timestamp'],
-          'verification_email_subject' => $row['verification_email_subject'],
-          'verification_email_text' => $row['verification_email_text'],
-          'confirmation_email_subject' => $row['confirmation_email_subject'],
-          'confirmation_email_text' => $row['confirmation_email_text'],
-          'sent_verify_web_page_message' => $row['sent_verify_web_page_message'],
-          'record_exist_verify_web_page_message' => $row['record_exist_verify_web_page_message'],
-          'error_web_page_message' => $row['error_web_page_message'],
-          'subscription_not_available_web_page_message' => $row['subscription_not_available_web_page_message'],
-          'confirmation_web_page_message' => $row['confirmation_web_page_message'],
-          'general_email_template_subject' => $row['general_email_template_subject'],
-          'general_email_template' => $row['general_email_template'],
-        ];
-      }
-      return $template;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function get_notify_settings()
-  {
-    $sql = "SELECT * FROM page_notify_settings ORDER BY page_notify_id DESC LIMIT 1";
-    $result = \Drupal::database()->query($sql);
-    $settings = [];
-    if ($result) {
-      while ($row = $result->fetchAssoc()) {
-        $settings = [
-          'page_notify_id' => $row['page_notify_id'],
-          'page_notify_recaptcha' => $row['page_notify_recaptcha'],
-          'page_notify_captcha' => $row['page_notify_captcha'],
-          'page_notify_subscribers_count' => $row['page_notify_subscribers_count'],
-          'enable_message_subscription_not_available' => $row['enable_message_subscription_not_available'],
-          'page_notify_settings_enable_content_type' => $row['page_notify_settings_enable_content_type'],
-          'page_notify_settings_enable_view' => $row['page_notify_settings_enable_view'],
-        ];
-      }
-      return $settings;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function page_notifications_process_tokens($text, $replacements = null)
-  {
-    $find = array(
-      '[notify_user_name]',
-      '[notify_user_email]',
-      '[notify_verify_url]',
-      '[notify_subscribe_url]',
-      '[notify_unsubscribe_url]',
-      '[notify_user_subscribtions]',
-      '[notify_node_title]',
-      '[notify_node_url]',
-      '[notify_notes]',
-    );
-    if (!is_null($replacements)) {
-      $replace = $replacements;
-    } else {
-      $replace = array(
-        '',
-        '',
-        '',
-        '',
-        '',
-        '',
-        '',
-        '',
-        ''
-      );
-    }
-
-    $new_text = str_replace($find, $replace, $text);
-    return $new_text;
-  }
-
-  public function pageNotifyGetUserToken($email_notify)
-  {
-    $record = current(
-      \Drupal::entityTypeManager()->getStorage('node')
-        ->loadByProperties([
-          'field_page_notify_email' => $email_notify,
-        ])
-    );
-    if ($record && !is_null($record)) {
-      $field_token_notify_user_id = $record->get("field_page_notify_token_user_id")->getValue();
-      $record = [
-        'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-      ];
-      return $record;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function checkIfUserHasSubscription($email_notify)
-  {
-    $record = current(
-      \Drupal::entityTypeManager()->getStorage('node')
-        ->loadByProperties([
-          'field_page_notify_email' => $email_notify,
-        ])
-    );
-    if ($record && !is_null($record)) {
-      $field_token_notify_user_id = $record->get("field_page_notify_token_user_id")->getValue();
-      $user_record = [
-        'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-      ];
-      return TRUE;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function verifyByTokenAndEmail($email, $subscription_token)
-  {
-    $record = current(
-      \Drupal::entityTypeManager()->getStorage('node')
-        ->loadByProperties([
-          'field_page_notify_email' => $email,
-          'field_page_notify_token' => $subscription_token,
-        ])
-    );
-    if ($record && $record->isPublished() == true) {
-      return true;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function checkIfRecordExistNode($email, $node)
-  {
-    $node_pieces = explode("-", $node);
-    $node_id = $node_pieces[0];
-    $node_entity_type = $node_pieces[1];
-
-    $query = current(
-      \Drupal::entityTypeManager()->getStorage('node')
-        ->loadByProperties([
-          'field_page_notify_email' => $email,
-          'field_page_notify_node_id' => $node_id
-        ])
-    );
-
-
-    if ($query && $query->isPublished() == true) {
-      $field_token_notify_user_id = $query->get("field_page_notify_token_user_id")->getValue();
-      $field_email_notify = $query->get("field_page_notify_email")->getValue();
-      $field_token_notify = $query->get("field_page_notify_token")->getValue();
-      $field_node_id_notify = $query->get("field_page_notify_node_id")->getValue();
-      $records = [
-        'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-        'field_page_notify_email' => $field_email_notify[0]['value'],
-        'field_page_notify_token' => $field_token_notify[0]['value'],
-        'field_page_notify_node_id' => $field_node_id_notify[0]['value'],
-      ];
-
-      $field_page_notify_token_pieces = explode("-", $records['field_page_notify_token']);
-      $field_page_notify_node_id_pieces = explode("-", $records['field_page_notify_node_id']);
-
-      if ($field_page_notify_token_pieces[1] && $field_page_notify_token_pieces[1] == $node_entity_type && $field_page_notify_node_id_pieces[0] == $node_id) {
-        return $records;
-      } elseif (!$field_page_notify_token_pieces[1] && $field_page_notify_node_id_pieces[0] == $node_id) {
-        return $records;
-      } else {
-        return FALSE;
-      }
-    } else {
-      return FALSE;
-    }
-  }
-  public function checkIfRecordExistByToken($email, $token)
-  {
-    $record = current(
-      \Drupal::entityTypeManager()->getStorage('node')
-        ->loadByProperties([
-          'field_page_notify_email' => $email,
-          'field_page_notify_token' => $token
-        ])
-    );
-    if ($record && $record->isPublished() == true) {
-      $field_token_notify_user_id = $record->get("field_page_notify_token_user_id")->getValue();
-      $field_email_notify = $record->get("field_page_notify_email")->getValue();
-      $field_token_notify = $record->get("field_page_notify_token")->getValue();
-      $field_node_id_notify = $record->get("field_page_notify_node_id")->getValue();
-      $user_record = [
-        'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-        'field_page_notify_email' => $field_email_notify[0]['value'],
-        'field_page_notify_token' => $field_token_notify[0]['value'],
-        'field_page_notify_node_id' => $field_node_id_notify[0]['value'],
-      ];
-      return $user_record;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function getAllUserRecords($user_token, $email)
-  {
-    $query = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('status', 1)
-      ->condition('field_page_notify_email', $email, '=')
-      ->condition('field_page_notify_token_user_id', $user_token, '=');
-    $records = $query->execute();
-    if ($records && !is_null($records)) {
-      $user_records = [];
-      foreach ($records as $record) {
-        $node_record = \Drupal\node\Entity\Node::load($record);
-        if ($node_record && $node_record->isPublished() == true) {
-          $field_token_notify_user_id = $node_record->get("field_page_notify_token_user_id")->getValue();
-          $field_email_notify = $node_record->get("field_page_notify_email")->getValue();
-          $field_token_notify = $node_record->get("field_page_notify_token")->getValue();
-          $field_node_id_notify = $node_record->get("field_page_notify_node_id")->getValue();
-          $user_record = [
-            'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-            'field_page_notify_email' => $field_email_notify[0]['value'],
-            'field_page_notify_tokeny' => $field_token_notify[0]['value'],
-            'field_page_notify_node_id' => $field_node_id_notify[0]['value'],
-          ];
-          array_push($user_records, $user_record);
-        }
-      }
-      return $user_records;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function getAllNodeSubscription($node)
-  {
-    $query = \Drupal::entityQuery('node')
-      ->accessCheck(FALSE)
-      ->condition('type', 'page_notify_subscriptions')
-      ->condition('status', 1)
-      ->condition('field_page_notify_node_id', $node, '=');
-    $records = $query->execute();
-
-    if ($records && !is_null($records)) {
-      $node_subscriptions = [];
-      foreach ($records as $record) {
-        $node_record = \Drupal\node\Entity\Node::load($record);
-
-        if ($node_record) {
-          $field_token_notify_user_id = $node_record->get("field_page_notify_token_user_id")->getValue();
-          $field_email_notify = $node_record->get("field_page_notify_email")->getValue();
-          $field_token_notify = $node_record->get("field_page_notify_token")->getValue();
-          $field_node_id_notify = $node_record->get("field_page_notify_node_id")->getValue();
-          $node_record = [
-            'field_page_notify_token_user_id' => $field_token_notify_user_id[0]['value'],
-            'field_page_notify_email' => $field_email_notify[0]['value'],
-            'field_page_notify_token' => $field_token_notify[0]['value'],
-            'field_page_notify_node_id' => $field_node_id_notify[0]['value'],
-            'subscription_node_id' => $node_record->id(),
-          ];
-          array_push($node_subscriptions, $node_record);
-        }
-      }
-
-      return $node_subscriptions;
-    } else {
-      return FALSE;
-    }
-  }
-
-  public function getCurrentPageInfo()
-  {
-    $node = \Drupal::routeMatch()->getParameter('node');
-    if ($node instanceof \Drupal\node\NodeInterface) {
-      $nid = $node->id();
-      $pageinfo = [
-        'current_node' => $nid,
-        'current_path' => '',
-      ];
-    } else {
-      $curr_path = \Drupal::service('path.current')->getPath();
-      $pageinfo = [
-        'current_node' => '',
-        'current_path' => $curr_path,
-      ];
-    }
-    return $pageinfo;
-  }
-
-  public function page_notifications_generateRandom_user_token($length = 6)
-  {
-    $numbers = '0123456789';
-    $numbersLength = strlen($numbers);
-    $randomNumbers = '';
-    for ($i = 0; $i < $length; $i++) {
-      $randomNumbers .= $numbers[rand(0, $numbersLength - 1)];
-    }
-    return $randomNumbers;
-  }
-}
diff --git a/src/Mail/PageNotificationsMailHandler.php b/src/Mail/PageNotificationsMailHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..7683f3f693fd6985e51d62ad427ac6a3bc76c134
--- /dev/null
+++ b/src/Mail/PageNotificationsMailHandler.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Drupal\page_notifications\Mail;
+
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\token\TokenInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
+
+
+/**
+ * Handles mail formatting for page notifications.
+ */
+class PageNotificationsMailHandler {
+
+  use StringTranslationTrait;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The token service.
+   *
+   * @var \Drupal\token\TokenServiceInterface
+   */
+  protected $token;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface
+   */
+  protected $themeManager;
+
+  /**
+   * Constructs a new PageNotificationsMailHandler.
+   */
+  public function __construct(
+    ConfigFactoryInterface $config_factory,
+    RendererInterface $renderer,
+    TokenInterface $token,
+    TranslationInterface $translation,
+    ThemeManagerInterface $theme_manager,
+  ) {
+    $this->configFactory = $config_factory;
+    $this->renderer = $renderer;
+    $this->token = $token;
+    $this->setStringTranslation($translation);
+    $this->themeManager = $theme_manager;
+  }
+
+  /**
+   * Gets customized email footer.
+   */
+  protected function getEmailFooter() {
+    // Allow modules to alter the footer
+    $footer = '';
+    \Drupal::moduleHandler()->alter('page_notifications_email_footer', $footer);
+    return $footer;
+  }
+
+  /**
+   * Implements callback_mail().
+   */
+  public function mail($key, &$message, $params) {
+    $config = $this->configFactory->get('page_notifications.settings');
+
+    switch ($key) {
+      case 'verification':
+        $this->buildVerificationEmail($message, $params);
+        break;
+
+      case 'notification':
+        $this->buildNotificationEmail($message, $params);
+        break;
+
+      case 'already_subscribed':
+        $this->buildAlreadySubscribedEmail($message, $params);
+        break;
+    }
+  }
+
+  /**
+   * Builds a verification email.
+   */
+  protected function buildEmail(array &$message, array $params, string $template_type) {
+    $config = $this->configFactory->get('page_notifications.settings');
+    $subscription = $params['subscription'];
+    $entity = $params['entity'];
+
+    $token_data = [
+      'subscription' => $subscription,
+      'node' => $entity,
+      'notification' => $params['notification'] ?? [],
+    ];
+
+    $subject_key = "email_templates.{$template_type}_subject";
+    $body_key = "email_templates.{$template_type}_body";
+
+    $subject_template = $config->get($subject_key);
+    $body_template = $config->get($body_key)['value'];
+
+    // For subject
+    $message['subject'] = \Drupal::token()->replace(
+        $subject_template,
+        $token_data,
+        ['clear' => TRUE]
+    );
+
+    // For body
+    $body = \Drupal::token()->replace(
+        $body_template,
+        $token_data,
+        ['clear' => TRUE]
+    );
+
+    $themed_content = $this->wrapEmailContent(
+        $body,
+        $template_type,
+        $subscription,
+        $entity
+    );
+
+    // Set HTML mail parameters in multiple ways for maximum compatibility
+    $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
+    $message['format'] = 'text/html';
+    $message['params']['format'] = 'text/html';
+    $message['params']['plain'] = FALSE;
+    $message['params']['convert'] = FALSE;
+
+    // Additional parameters that some mail modules look for
+    $message['params']['html'] = TRUE;
+    $message['params']['plaintext'] = FALSE;
+    $message['params']['ishtml'] = TRUE;
+
+    // Ensure rendered content is properly handled as markup
+    $rendered_content = $this->renderer->render($themed_content);
+    $message['body'] = [$rendered_content];
+  }
+
+  /**
+   * Wraps email content in themed template.
+   */
+  protected function wrapEmailContent($content, $email_type, $subscription, $entity) {
+
+     // Ensure content is properly structured as markup
+    $processed_content = [
+      '#type' => 'markup',
+      '#markup' => $content,
+    ];
+
+    return [
+      '#theme' => 'page_notifications_email_wrapper',
+      '#content' => $processed_content,
+      '#email_type' => $email_type,
+      '#subscription' => $subscription,
+      '#entity' => $entity,
+      '#footer' => $this->getEmailFooter(),
+    ];
+  }
+
+  protected function buildVerificationEmail(array &$message, array $params) {
+    $this->buildEmail($message, $params, 'verification');
+  }
+
+  protected function buildNotificationEmail(array &$message, array $params) {
+    $this->buildEmail($message, $params, 'notification');
+  }
+
+  protected function buildAlreadySubscribedEmail(array &$message, array $params) {
+    $this->buildEmail($message, $params, 'already_subscribed');
+  }
+}
\ No newline at end of file
diff --git a/src/Plugin/Block/PageNotificationsBlock.php b/src/Plugin/Block/PageNotificationsBlock.php
deleted file mode 100644
index cbb10ddcdd5137a00091b8a442d15442356c0338..0000000000000000000000000000000000000000
--- a/src/Plugin/Block/PageNotificationsBlock.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Plugin\Block;
-
-use Drupal\Core\Block\BlockBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Form\FormBuilderInterface;
-use Drupal\page_notifications\Form\PageNotificationsBlockForm;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Block\BlockPluginInterface;
-use Drupal\filter\Element\ProcessedText;
-use Drupal\Component\Render\FormattableMarkup;
-use Drupal\Core\Render\Markup;
-
-/**
- *
- * Provides a 'Page Notifications' block.
- *
- * @Block(
- *   id = "page_notifications",
- *   admin_label = @Translation("Page Notifications"),
- *   category = @Translation("Page Notifications")
- * )
- */
-
-class PageNotificationsBlock extends BlockBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return [
-      'markup' => [
-        'format' => 'full_html',
-        'value' => '',
-      ],
-    ] + parent::defaultConfiguration();
-  }
-
-  /**
-   * {@inheritdoc}
-   *
-   * This method defines form elements for custom block configuration. Standard
-   * block configuration fields are added by BlockBase::buildConfigurationForm()
-   * (block title and title visibility) and BlockFormController::form() (block
-   * visibility settings).
-   *
-   * @see \Drupal\block\BlockBase::buildConfigurationForm()
-   * @see \Drupal\block\BlockFormController::form()
-   */
-  public function blockForm($form, FormStateInterface $form_state) {
-    $blockManager = \Drupal::service('plugin.manager.block');
-    $contextRepository = \Drupal::service('context.repository');
-    $definitions = $blockManager->getDefinitionsForContexts(
-        $contextRepository->getAvailableContexts()
-    );
-    $buildInfo = $form_state->getBuildInfo();
-    return $form;
-  }
-
-
-  /**
-   * {@inheritdoc}
-   *
-   * This method processes the blockForm() form fields when the block
-   * configuration form is submitted.
-   *
-   * The blockValidate() method can be used to validate the form submission.
-   */
-  public function blockSubmit($form, FormStateInterface $form_state) {
-
-  }
-
-
-  /**
-   * Form builder service.
-   *
-   * @var \Drupal\Core\Form\FormBuilderInterface
-   */
-  protected $formBuilder;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->formBuilder = $form_builder;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('form_builder')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function build() {
-    /*$config = $this->getConfiguration();
-    $preload_template = \Drupal::service('load.databaseinnfo.service')->get_notify_email_template();
-    $notify_settings = \Drupal::service('load.databaseinnfo.service')->get_notify_settings();
-    $route_match = \Drupal::routeMatch();
-
-    $output['body'] = [
-      '#type' => 'processed_text',
-      '#text' => $this->t($preload_template['subscription_not_available_web_page_message']),
-      "#processed" => true,
-      '#format' => $preload_template['subscription_not_available_web_page_message'],
-    ];*/
-
-    $output['form'] = $this->formBuilder->getForm(PageNotificationsBlockForm::class);
-    return $output;
-  }
-
-  /**
-     * {@inheritdoc}
-     */
-    public function getCacheMaxAge() {
-        return 0;
-    }
-
-}
diff --git a/src/Plugin/Block/SubscriptionBlock.php b/src/Plugin/Block/SubscriptionBlock.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5620e0915c0dba19b4de003b15d636fe3bb3a51
--- /dev/null
+++ b/src/Plugin/Block/SubscriptionBlock.php
@@ -0,0 +1,318 @@
+<?php
+
+namespace Drupal\page_notifications\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Provides a subscription block.
+ *
+ * @Block(
+ *   id = "page_notifications_subscription",
+ *   admin_label = @Translation("Page Notifications Subscription"),
+ *   category = @Translation("Page Notifications")
+ * )
+ */
+class SubscriptionBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * The logger factory.
+   *
+   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
+   */
+  protected $loggerFactory;
+
+  /**
+   * Constructs a new SubscriptionBlock instance.
+   *
+   * @param array $configuration
+   *   The plugin configuration.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder.
+   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
+   *   The logger factory.
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    EntityTypeManagerInterface $entity_type_manager,
+    FormBuilderInterface $form_builder,
+    LoggerChannelFactoryInterface $logger_factory
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->formBuilder = $form_builder;
+    $this->loggerFactory = $logger_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('form_builder'),
+      $container->get('logger.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'block_description' => $this->t('Subscribe to receive notifications when this page is updated.'),
+      'button_text' => $this->t('Subscribe'),
+      'button_classes' => 'button button--primary',
+      'form_classes' => 'subscription-form',
+      'show_description' => TRUE,
+      'use_modal' => FALSE,
+      'modal_title' => $this->t('Subscribe to Updates'),
+      'success_message' => $this->t('Thank you for subscribing. Please check your email to confirm your subscription.'),
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    $form = parent::blockForm($form, $form_state);
+    $config = $this->getConfiguration();
+
+    $form['appearance'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Appearance Settings'),
+      '#open' => TRUE,
+    ];
+
+    $form['appearance']['show_description'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Show block description'),
+      '#default_value' => $config['show_description'],
+    ];
+
+    $form['appearance']['block_description'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Block Description'),
+      '#description' => $this->t('The text shown above the subscription form.'),
+      '#default_value' => $config['block_description'],
+      '#states' => [
+        'visible' => [
+          ':input[name="settings[appearance][show_description]"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+
+    $form['appearance']['button_text'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Button Text'),
+      '#description' => $this->t('The text shown on the subscribe button.'),
+      '#default_value' => $config['button_text'],
+    ];
+
+    $form['appearance']['use_modal'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Use modal dialog'),
+      '#description' => $this->t('Display the subscription form in a modal dialog.'),
+      '#default_value' => $config['use_modal'],
+    ];
+
+    $form['appearance']['modal_title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Modal Title'),
+      '#description' => $this->t('The title displayed at the top of the modal dialog.'),
+      '#default_value' => $config['modal_title'],
+      '#states' => [
+        'visible' => [
+          ':input[name="settings[appearance][use_modal]"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+    $form['appearance']['success_message'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Success Message'),
+      '#description' => $this->t('Message shown after successful subscription.'),
+      '#default_value' => $config['success_message'],
+      '#rows' => 2,
+    ];
+
+    $form['styling'] = [
+      '#type' => 'details',
+      '#title' => $this->t('CSS Classes'),
+      '#open' => TRUE,
+    ];
+
+    $form['styling']['button_classes'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Button Classes'),
+      '#description' => $this->t('CSS classes to add to the subscribe button (space-separated).'),
+      '#default_value' => $config['button_classes'],
+    ];
+
+    $form['styling']['form_classes'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Form Classes'),
+      '#description' => $this->t('CSS classes to add to the subscription form wrapper (space-separated).'),
+      '#default_value' => $config['form_classes'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    $this->configuration['block_description'] = $form_state->getValue(['appearance', 'block_description']);
+    $this->configuration['button_text'] = $form_state->getValue(['appearance', 'button_text']);
+    $this->configuration['button_classes'] = $form_state->getValue(['styling', 'button_classes']);
+    $this->configuration['form_classes'] = $form_state->getValue(['styling', 'form_classes']);
+    $this->configuration['show_description'] = $form_state->getValue(['appearance', 'show_description']);
+    $this->configuration['use_modal'] = $form_state->getValue(['appearance', 'use_modal']);
+    $this->configuration['modal_title'] = $form_state->getValue(['appearance', 'modal_title']);
+    $this->configuration['success_message'] = $form_state->getValue(['appearance', 'success_message']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    try {
+      // Get node from route match or current path
+      $node = \Drupal::routeMatch()->getParameter('node');
+      if (!$node) {
+        $path_args = explode('/', \Drupal::service('path.current')->getPath());
+        if (isset($path_args[2]) && is_numeric($path_args[2])) {
+          $node = \Drupal::entityTypeManager()->getStorage('node')->load($path_args[2]);
+        }
+      }
+
+      if (!$node) {
+        return [];
+      }
+
+      $build = [];
+
+      if ($this->configuration['show_description']) {
+        $build['description'] = [
+          '#type' => 'html_tag',
+          '#tag' => 'p',
+          '#value' => $this->configuration['block_description'],
+        ];
+      }
+
+      if ($this->configuration['use_modal']) {
+        // Modal trigger button
+        $url = Url::fromRoute('page_notifications.modal_form', [
+          'entity_type' => $node->getEntityTypeId(),
+          'entity' => $node->id(),
+        ]);
+
+        $build['modal_button'] = [
+          '#type' => 'link',
+          '#title' => $this->configuration['button_text'],
+          '#url' => Url::fromRoute('page_notifications.modal_form', [
+            'entity_type' => $node->getEntityTypeId(),
+            'entity' => $node->id(),
+          ]),
+          '#attributes' => [
+            'class' => array_merge(['use-ajax'], explode(' ', $this->configuration['button_classes'])),
+            'data-dialog-type' => 'modal',
+            'data-dialog-options' => json_encode([
+              'width' => 500,
+              'title' => $this->configuration['modal_title'],
+              'block_configuration' => [
+                'success_message' => $this->configuration['success_message'],
+              ],
+            ]),
+          ],
+        ];
+
+        // Attach required libraries
+        $build['#attached']['library'][] = 'page_notifications/modal';
+      } else {
+        $form = $this->formBuilder->getForm('\Drupal\page_notifications\Form\SubscriptionForm', $node);
+        $form['#attributes']['class'][] = $this->configuration['form_classes'];
+        $form['actions']['submit']['#value'] = $this->configuration['button_text'];
+        $form['actions']['submit']['#attributes']['class'] = explode(' ', $this->configuration['button_classes']);
+        $build['form'] = $form;
+      }
+
+       // Add cache contexts and tags
+      $build['#cache'] = [
+        'contexts' => [
+          'url.path',
+          'route',
+        ],
+        'tags' => [
+          'node:' . $node->id(),
+        ],
+        'max-age' => 0 // Disable caching for this block
+      ];
+
+      return $build;
+    }
+    catch (\Exception $e) {
+      $this->loggerFactory->get('page_notifications')->error(
+        'Error building subscription block: @message',
+        ['@message' => $e->getMessage()]
+      );
+      return [];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function blockAccess(AccountInterface $account) {
+    try {
+      $node = \Drupal::routeMatch()->getParameter('node');
+      if (!$node) {
+        return AccessResult::forbidden();
+      }
+
+      return AccessResult::allowedIfHasPermission($account, 'access content');
+    }
+    catch (\Exception $e) {
+      $this->loggerFactory->get('page_notifications')->error(
+        'Error checking block access: @message',
+        ['@message' => $e->getMessage()]
+      );
+      return AccessResult::forbidden();
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/Plugin/EntityReferenceSelection/NodeEnhancedSelection.php b/src/Plugin/EntityReferenceSelection/NodeEnhancedSelection.php
new file mode 100644
index 0000000000000000000000000000000000000000..0ec26cadc8384d49f6a04c5139f8fb915cce9106
--- /dev/null
+++ b/src/Plugin/EntityReferenceSelection/NodeEnhancedSelection.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\page_notifications\Plugin\EntityReferenceSelection;
+
+use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;
+
+/**
+ * Provides enhanced node selection with additional display information.
+ *
+ * @EntityReferenceSelection(
+ *   id = "default:node_enhanced",
+ *   label = @Translation("Node enhanced selection"),
+ *   entity_types = {"node"},
+ *   group = "default",
+ *   weight = 1
+ * )
+ */
+class NodeEnhancedSelection extends NodeSelection {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
+    $target_type = $this->configuration['target_type'];
+
+    $query = $this->buildEntityQuery($match, $match_operator);
+    if ($limit > 0) {
+      $query->range(0, $limit);
+    }
+
+    $result = $query->execute();
+
+    if (empty($result)) {
+      return [];
+    }
+
+    $options = [];
+    $entities = $this->entityTypeManager->getStorage($target_type)->loadMultiple($result);
+
+    foreach ($entities as $entity_id => $entity) {
+      $bundle = $entity->bundle();
+      $type_label = $entity->type->entity->label();
+
+      $label = sprintf(
+        '%s (ID: %d, Type: %s)',
+        $entity->label(),
+        $entity_id,
+        $type_label
+      );
+
+      $options[$bundle][$entity_id] = $label;
+    }
+
+    return $options;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Plugin/EntityReferenceSelection/NodeWithSubscriptionsSelection.php b/src/Plugin/EntityReferenceSelection/NodeWithSubscriptionsSelection.php
new file mode 100644
index 0000000000000000000000000000000000000000..61c31077cad95a2c60e660be2b3f9fe589f9f455
--- /dev/null
+++ b/src/Plugin/EntityReferenceSelection/NodeWithSubscriptionsSelection.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\page_notifications\Plugin\EntityReferenceSelection;
+
+use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;
+use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides specific access control for node entities with subscriptions.
+ *
+ * @EntityReferenceSelection(
+ *   id = "default:node_with_subscriptions",
+ *   label = @Translation("Node with subscriptions selection"),
+ *   entity_types = {"node"},
+ *   group = "default",
+ *   weight = 1
+ * )
+ */
+class NodeWithSubscriptionsSelection extends NodeSelection {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
+    $query = parent::buildEntityQuery($match, $match_operator);
+
+    // Get nodes that have active subscriptions
+    $subscription_query = $this->entityTypeManager
+      ->getStorage('page_notification_subscription')
+      ->getQuery()
+      ->condition('subscribed_entity_type', 'node')
+      ->condition('status', TRUE)
+      ->accessCheck(FALSE);
+
+    $subscriptions = $subscription_query->execute();
+
+    if (!empty($subscriptions)) {
+      // Get unique node IDs from subscriptions
+      $node_ids = [];
+      $subscriptions = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->loadMultiple($subscriptions);
+
+      foreach ($subscriptions as $subscription) {
+        $node_ids[] = $subscription->getSubscribedEntityId();
+      }
+
+      // Filter query to only include nodes with subscriptions
+      $query->condition('nid', $node_ids, 'IN');
+    }
+    else {
+      // If no subscriptions exist, return no results
+      $query->condition('nid', 0);
+    }
+
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
+    $target_type = $this->configuration['target_type'];
+
+    $query = $this->buildEntityQuery($match, $match_operator);
+    if ($limit > 0) {
+      $query->range(0, $limit);
+    }
+
+    $result = $query->execute();
+
+    if (empty($result)) {
+      return [];
+    }
+
+    $options = [];
+    $entities = $this->entityTypeManager->getStorage($target_type)->loadMultiple($result);
+
+    foreach ($entities as $entity_id => $entity) {
+      $bundle = $entity->bundle();
+      $type_label = $entity->type->entity->label();
+
+      // Get subscription count for this node
+      $subscription_count = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->getQuery()
+        ->condition('subscribed_entity_id', $entity_id)
+        ->condition('subscribed_entity_type', 'node')
+        ->condition('status', TRUE)
+        ->count()
+        ->accessCheck(FALSE)
+        ->execute();
+
+      $label = sprintf(
+        '%s (ID: %d, Type: %s, Subscriptions: %d)',
+        $entity->label(),
+        $entity_id,
+        $type_label,
+        $subscription_count
+      );
+
+      $options[$bundle][$entity_id] = $label;
+    }
+
+    return $options;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Plugin/QueueWorker/NotificationQueue.php b/src/Plugin/QueueWorker/NotificationQueue.php
new file mode 100644
index 0000000000000000000000000000000000000000..dd659819a311bd12aaa2a1c470b8ae33296fa1e9
--- /dev/null
+++ b/src/Plugin/QueueWorker/NotificationQueue.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Drupal\page_notifications\Plugin\QueueWorker;
+
+use Drupal\Core\Queue\QueueWorkerBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Process notification queue.
+ *
+ * @QueueWorker(
+ *   id = "page_notifications_queue",
+ *   title = @Translation("Page Notifications Queue"),
+ *   cron = {"time" = 60}
+ * )
+ */
+class NotificationQueue extends QueueWorkerBase implements ContainerFactoryPluginInterface {
+  use StringTranslationTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The mail manager.
+   *
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The logger factory.
+   *
+   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
+   */
+  protected $loggerFactory;
+
+  /**
+   * Constructs a new NotificationQueue worker.
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    array $plugin_definition,
+    EntityTypeManagerInterface $entity_type_manager,
+    MailManagerInterface $mail_manager,
+    ConfigFactoryInterface $config_factory,
+    LoggerChannelFactoryInterface $logger_factory
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->mailManager = $mail_manager;
+    $this->configFactory = $config_factory;
+    $this->loggerFactory = $logger_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('plugin.manager.mail'),
+      $container->get('config.factory'),
+      $container->get('logger.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processItem($data) {
+    try {
+      // Load the subscription
+      $subscription = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->load($data['subscription_id']);
+
+      if (!$subscription || !$subscription->isActive()) {
+        $this->loggerFactory->get('page_notifications')
+          ->notice('Skipping notification for inactive or deleted subscription: @id',
+            ['@id' => $data['subscription_id']]);
+        return;
+      }
+
+      // Load the entity
+      $entity = $this->entityTypeManager
+        ->getStorage($data['entity_type'])
+        ->load($data['entity_id']);
+
+      if (!$entity) {
+        $this->loggerFactory->get('page_notifications')
+          ->error('Cannot send notification: Entity not found (@type: @id)',
+            ['@type' => $data['entity_type'], '@id' => $data['entity_id']]);
+        return;
+      }
+
+      $config = $this->configFactory->get('page_notifications.settings');
+
+      // Prepare mail parameters
+      $params = [
+        'subscription' => $subscription,
+        'entity' => $entity,
+        'token' => $subscription->getToken(),
+      ];
+
+      $langcode = $subscription->getLanguageCode() ?? LanguageInterface::LANGCODE_DEFAULT;
+      $from_email = $config->get('notification_settings.from_email');
+
+      // Send the email
+      $this->mailManager->mail(
+        'page_notifications',           // module
+        'notification',                 // key
+        $subscription->getEmail(),      // to
+        $langcode,                     // language
+        $params,                       // params
+        $from_email ?: NULL            // from
+      );
+
+      // Log the attempt regardless of the result
+      $this->loggerFactory->get('page_notifications')
+        ->info('Processed notification for @email regarding @type @id', [
+          '@email' => $subscription->getEmail(),
+          '@type' => $entity->getEntityTypeId(),
+          '@id' => $entity->id(),
+        ]);
+
+    }
+    catch (\Exception $e) {
+      // Log any unexpected errors
+      $this->loggerFactory->get('page_notifications')
+        ->error('Error processing notification: @message',
+          ['@message' => $e->getMessage()]);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/Routing/PageNotificationsDynamicRoutes.php b/src/Routing/PageNotificationsDynamicRoutes.php
deleted file mode 100644
index df9a170ac42211108e8f7fb44f8879d36a7a170d..0000000000000000000000000000000000000000
--- a/src/Routing/PageNotificationsDynamicRoutes.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Routing;
-
-use Symfony\Component\Routing\Route;
-
-/**
- * Defines dynamic routes for our tab menu items.
- *
- * These routes support the links created in page_notifications.links.task.yml.
- *
- * @see page_notifications.links.task.yml
- * @see https://www.drupal.org/docs/8/api/routing-system/providing-dynamic-routes
- */
-class PageNotificationsDynamicRoutes {
-
-  /**
-   * Returns an array of route objects.
-   *
-   * @return \Symfony\Component\Routing\Route[]
-   *   An array of route objects.
-   */
-  public function routes() {
-    $routes = [];
-
-    $tabs = [
-      'tabs' => 'General configuration',
-      'tabs/second' => 'Migrate Subscribtions',
-      'tabs/third' => 'Migrate Subscribtions Content Type',
-      //'tabs/fourth' => 'Page Notifications - Node Subscribtions List',
-      'tabs/default/second' => 'Messages configuration',
-      //'tabs/default/third' => 'Third',
-    ];
-
-    foreach ($tabs as $path => $title) {
-      $machine_name = 'page_notifications.' . str_replace('/', '_', $path);
-      $routes[$machine_name] = new Route(
-
-        '/admin/page-notifications/' . $path,
-        [
-          '_controller' => '\Drupal\page_notifications\Controller\PageNotificationsController::tabsPage',
-          '_title' => $title,
-          'path' => $path,
-          'title' => $title,
-        ],
-        [
-          '_access' => 'TRUE',
-        ]
-      );
-    }
-
-    return $routes;
-  }
-
-}
diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php
deleted file mode 100644
index 38a94978035f539bb1e96562e17328adce383290..0000000000000000000000000000000000000000
--- a/src/Routing/RouteSubscriber.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace Drupal\page_notifications\Routing;
-
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Listens to the dynamic route events.
- *
- * The \Drupal\Core\Routing\RouteSubscriberBase class contains an event
- * listener that listens to this event. We alter existing routes by
- * implementing the alterRoutes(RouteCollection $collection) method of
- * this class.
- *
- * @see https://www.drupal.org/docs/8/api/routing-system/altering-existing-routes-and-adding-new-routes-based-on-dynamic-ones
- */
-class RouteSubscriber extends RouteSubscriberBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function alterRoutes(RouteCollection $collection) {
-    $route = $collection->get('page_notifications.path_override');
-    $route->setPath('/admin/page-notifications/menu-altered-path');
-    $route->setDefault('_title', 'Menu item altered by RouteSubscriber::alterRoutes');
-  }
-
-}
diff --git a/src/Service/CronManager.php b/src/Service/CronManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..af929686ad3147437d08587af606a5351ff76209
--- /dev/null
+++ b/src/Service/CronManager.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Drupal\page_notifications\Service;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Queue\QueueWorkerManagerInterface;
+use Drupal\Core\Queue\QueueFactory;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Component\Datetime\TimeInterface;
+
+/**
+ * Service for handling page notifications cron operations.
+ */
+class CronManager {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The queue factory.
+   *
+   * @var \Drupal\Core\Queue\QueueFactory
+   */
+  protected $queueFactory;
+
+  /**
+   * The queue worker manager.
+   *
+   * @var \Drupal\Core\Queue\QueueWorkerManagerInterface
+   */
+  protected $queueWorkerManager;
+
+  /**
+   * The logger factory.
+   *
+   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
+   */
+  protected $loggerFactory;
+
+  /**
+   * The time service.
+   *
+   * @var \Drupal\Component\Datetime\TimeInterface
+   */
+  protected $time;
+
+  /**
+   * Constructs a new CronManager.
+   */
+  public function __construct(
+    EntityTypeManagerInterface $entity_type_manager,
+    ConfigFactoryInterface $config_factory,
+    QueueFactory $queue_factory,
+    QueueWorkerManagerInterface $queue_worker_manager,
+    LoggerChannelFactoryInterface $logger_factory,
+    TimeInterface $time
+  ) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->configFactory = $config_factory;
+    $this->queueFactory = $queue_factory;
+    $this->queueWorkerManager = $queue_worker_manager;
+    $this->loggerFactory = $logger_factory;
+    $this->time = $time;
+  }
+
+  /**
+   * Processes cron tasks.
+   */
+  public function processCron() {
+    $this->processQueue();
+    $this->cleanupExpiredSubscriptions();
+  }
+
+  /**
+   * Process the notification queue.
+   */
+  protected function processQueue() {
+    $queue = $this->queueFactory->get('page_notifications_queue');
+    $queue_worker = $this->queueWorkerManager->createInstance('page_notifications_queue');
+
+    $time_limit = 30;
+    $end = $this->time->getRequestTime() + $time_limit;
+    $items_processed = 0;
+
+    while ($this->time->getRequestTime() < $end && ($item = $queue->claimItem())) {
+      try {
+        $queue_worker->processItem($item->data);
+        $queue->deleteItem($item);
+        $items_processed++;
+
+        if ($items_processed >= 50) {
+          break;
+        }
+      }
+      catch (\Exception $e) {
+        $queue->releaseItem($item);
+        $this->loggerFactory->get('page_notifications')->error(
+          'Error processing notification: @message',
+          ['@message' => $e->getMessage()]
+        );
+      }
+    }
+  }
+
+  /**
+   * Clean up expired unverified subscriptions.
+   */
+  protected function cleanupExpiredSubscriptions() {
+    $config = $this->configFactory->get('page_notifications.settings');
+    $expiration_hours = $config->get('notification_settings.token_expiration');
+
+    // Only cleanup if expiration is set (greater than 0)
+    if ($expiration_hours > 0) {
+      $storage = $this->entityTypeManager->getStorage('page_notification_subscription');
+
+      $expired_ids = $storage->getQuery()
+        ->condition('status', FALSE)
+        ->condition('created', $this->time->getRequestTime() - ($expiration_hours * 3600), '<')
+        ->accessCheck(FALSE)
+        ->execute();
+
+      if (!empty($expired_ids)) {
+        $storage->delete($storage->loadMultiple($expired_ids));
+        $this->loggerFactory->get('page_notifications')->notice(
+          'Cleaned up @count expired unverified subscriptions',
+          ['@count' => count($expired_ids)]
+        );
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/Service/MigrationService.php b/src/Service/MigrationService.php
new file mode 100644
index 0000000000000000000000000000000000000000..6500a3f6b72d6f48c42f04c55165181ef348e408
--- /dev/null
+++ b/src/Service/MigrationService.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace Drupal\page_notifications\Service;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+
+/**
+ * Service for migrating subscriptions from Page Notifications v3 to v4.
+ */
+class MigrationService {
+  use StringTranslationTrait;
+  use DependencySerializationTrait;
+
+  /**
+   * Creates a batch for migrating subscriptions.
+   *
+   * @return array
+   *   The batch definition.
+   */
+  public static function createMigrationBatch() {
+    // Get total count of subscriptions to migrate
+    $count = \Drupal::database()->select('node', 'n')
+      ->condition('n.type', 'page_notify_subscriptions')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    if (!$count) {
+      \Drupal::logger('page_notifications')->notice('No v3 subscriptions found to migrate.');
+      return NULL;
+    }
+
+    \Drupal::logger('page_notifications')->notice('Found @count v3 subscriptions to migrate.', ['@count' => $count]);
+
+    $batch = [
+      'title' => t('Migrating Page Notifications subscriptions'),
+      'init_message' => t('Starting subscription migration...'),
+      'progress_message' => t('Processed @current out of @total subscriptions.'),
+      'error_message' => t('Error occurred during migration.'),
+      'operations' => [],
+      'finished' => [static::class, 'migrationFinished'],
+    ];
+
+    // Process subscriptions in batches of 25
+    for ($i = 0; $i < $count; $i += 25) {
+      $batch['operations'][] = [
+        [static::class, 'migrateSubscriptionsBatch'],
+        [$i, min(25, $count - $i)]
+      ];
+    }
+
+    return $batch;
+  }
+
+  /**
+   * Migrates a batch of subscriptions.
+   */
+  public static function migrateSubscriptionsBatch($start, $limit, &$context) {
+    try {
+      $database = \Drupal::database();
+
+      // Query v3 subscriptions
+      $query = $database->select('node', 'n');
+      $query->join('node_field_data', 'nfd', 'n.nid = nfd.nid');
+      $query->fields('n', ['nid'])
+        ->fields('nfd', ['created'])
+        ->condition('n.type', 'page_notify_subscriptions')
+        ->range($start, $limit);
+
+      // Join with field tables
+      $query->join('node__field_page_notify_email', 'e', 'n.nid = e.entity_id');
+      $query->join('node__field_page_notify_node_id', 'nid', 'n.nid = nid.entity_id');
+
+      $query->fields('e', ['field_page_notify_email_value']);
+      $query->fields('nid', ['field_page_notify_node_id_value']);
+
+      $results = $query->execute();
+
+      $subscription_storage = \Drupal::entityTypeManager()->getStorage('page_notification_subscription');
+      $time = \Drupal::time()->getRequestTime();
+
+      foreach ($results as $row) {
+        // Generate new tokens
+        $verify_token = bin2hex(random_bytes(16));
+        $unsubscribe_token = bin2hex(random_bytes(32));
+
+        \Drupal::logger('page_notifications')->debug('Migrating subscription for email: @email, node: @nid', [
+          '@email' => $row->field_page_notify_email_value,
+          '@nid' => $row->field_page_notify_node_id_value,
+        ]);
+
+        // Create new v4 subscription entity
+        $subscription = $subscription_storage->create([
+          'email' => $row->field_page_notify_email_value,
+          'subscribed_entity_id' => $row->field_page_notify_node_id_value,
+          'subscribed_entity_type' => 'node',
+          'token' => $verify_token,
+          'unsubscribe_token' => $unsubscribe_token,
+          'status' => TRUE,
+          'created' => $row->created ?? $time,
+          'changed' => $time,
+          'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
+        ]);
+
+        try {
+          $subscription->save();
+
+          // Update progress
+          if (!isset($context['results']['subscriptions'])) {
+            $context['results']['subscriptions'] = 0;
+          }
+          $context['results']['subscriptions']++;
+
+          \Drupal::logger('page_notifications')->debug('Successfully migrated subscription @id', [
+            '@id' => $subscription->id(),
+          ]);
+        }
+        catch (\Exception $e) {
+          \Drupal::logger('page_notifications')->error('Failed to save subscription: @error', [
+            '@error' => $e->getMessage(),
+          ]);
+        }
+      }
+
+      $context['message'] = t('Migrated @count subscriptions', [
+        '@count' => $limit,
+      ]);
+    }
+    catch (\Exception $e) {
+      \Drupal::logger('page_notifications')->error(
+        'Failed to migrate subscriptions batch: @message',
+        ['@message' => $e->getMessage()]
+      );
+      throw $e;
+    }
+  }
+
+  /**
+   * Batch finished callback.
+   */
+  public static function migrationFinished($success, $results, $operations) {
+    if ($success) {
+      // Verify migration
+      $old_count = \Drupal::database()->select('node', 'n')
+        ->condition('n.type', 'page_notify_subscriptions')
+        ->countQuery()
+        ->execute()
+        ->fetchField();
+
+      $new_count = \Drupal::entityTypeManager()
+        ->getStorage('page_notification_subscription')
+        ->getQuery()
+        ->accessCheck(FALSE)
+        ->count()
+        ->execute();
+
+      $message = t('Migration completed. Migrated @migrated subscriptions (@old v3 subscriptions, @new v4 subscriptions).', [
+        '@migrated' => $results['subscriptions'] ?? 0,
+        '@old' => $old_count,
+        '@new' => $new_count,
+      ]);
+
+      \Drupal::logger('page_notifications')->notice($message);
+      \Drupal::messenger()->addStatus($message);
+
+      if ($old_count != $new_count) {
+        $warning = t('Warning: Number of migrated subscriptions (@new) does not match original count (@old).', [
+          '@new' => $new_count,
+          '@old' => $old_count,
+        ]);
+        \Drupal::logger('page_notifications')->warning($warning);
+        \Drupal::messenger()->addWarning($warning);
+      }
+    }
+    else {
+      $message = t('Migration failed. Please check the logs for details.');
+      \Drupal::logger('page_notifications')->error($message);
+      \Drupal::messenger()->addError($message);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/Service/NotificationManager.php b/src/Service/NotificationManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..306bb6a626a18532bf662651f45d8e726a75fd68
--- /dev/null
+++ b/src/Service/NotificationManager.php
@@ -0,0 +1,398 @@
+<?php
+
+namespace Drupal\page_notifications\Service;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Queue\QueueFactory;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Messenger\MessengerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+
+/**
+ * Service for handling page notification operations.
+ */
+class NotificationManager implements NotificationManagerInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The mail manager.
+   *
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The queue factory.
+   *
+   * @var \Drupal\Core\Queue\QueueFactory
+   */
+  protected $queueFactory;
+
+  /**
+   * The logger factory.
+   *
+   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
+   */
+  protected $loggerFactory;
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * The time service.
+   *
+   * @var \Drupal\Component\Datetime\TimeInterface
+   */
+  protected $time;
+
+  /**
+   * The messenger service.
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+/**
+   * Constructs a new NotificationManager.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+   *   The mail manager.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
+   *   The queue factory.
+   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
+   *   The logger factory.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher.
+   * @param \Drupal\Component\Datetime\TimeInterface $time
+   *   The time service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
+   *   The string translation service.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *  The messenger service.
+   */
+  public function __construct(
+    ConfigFactoryInterface $config_factory,
+    MailManagerInterface $mail_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    QueueFactory $queue_factory,
+    LoggerChannelFactoryInterface $logger_factory,
+    EventDispatcherInterface $event_dispatcher,
+    TimeInterface $time,
+    TranslationInterface $translation,
+    MessengerInterface $messenger
+  ) {
+    $this->configFactory = $config_factory;
+    $this->mailManager = $mail_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->queueFactory = $queue_factory;
+    $this->loggerFactory = $logger_factory;
+    $this->eventDispatcher = $event_dispatcher;
+    $this->time = $time;
+    $this->setStringTranslation($translation);
+    $this->messenger = $messenger;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createSubscription(string $email, EntityInterface $entity, ?string $langcode = null) {
+    try {
+      // Check for existing subscription
+      $existing_subscriptions = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->loadByProperties([
+          'email' => $email,
+          'subscribed_entity_id' => $entity->id(),
+          'subscribed_entity_type' => $entity->getEntityTypeId(),
+        ]);
+  
+      if (!empty($existing_subscriptions)) {
+        /** @var \Drupal\page_notifications\Entity\SubscriptionInterface $subscription */
+        $subscription = reset($existing_subscriptions);
+  
+        // Handle different subscription states
+        if ($subscription->isActive()) {
+          // Already verified subscription - send "already subscribed" email
+          $this->sendAlreadySubscribedEmail($subscription, $entity);
+          $this->messenger->addStatus($this->t('You are already subscribed to this content.'));
+          return $subscription;
+        }
+        
+        // Check if token is expired
+        if ($this->isTokenExpired($subscription)) {
+          // Generate new token and update subscription
+          $subscription->setToken($this->generateToken());
+          $subscription->setCreatedTime($this->time->getRequestTime());
+          $subscription->save();
+        }
+        
+        // Resend verification email for unverified subscriptions
+        if ($this->requiresVerification()) {
+          $this->sendVerificationEmail($subscription);
+          $this->messenger->addStatus($this->t('A new verification email has been sent to your address.'));
+        }
+        
+        return $subscription;
+      }
+  
+      // Create new subscription if none exists
+      $subscription = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->create([
+          'email' => $email,
+          'subscribed_entity_id' => $entity->id(),
+          'subscribed_entity_type' => $entity->getEntityTypeId(),
+          'token' => $this->generateToken(),
+          'unsubscribe_token' => $this->generateToken(),
+          'status' => !$this->requiresVerification(),
+        ]);
+  
+      $subscription->save();
+  
+      if ($this->requiresVerification()) {
+        $this->sendVerificationEmail($subscription);
+      }
+  
+      return $subscription;
+    }
+    catch (\Exception $e) {
+      $this->loggerFactory->get('page_notifications')
+        ->error('Failed to create subscription: @message', ['@message' => $e->getMessage()]);
+      throw $e;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function verifySubscription(string $token) {
+    try {
+      // Find subscription by token
+      $subscriptions = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->loadByProperties(['token' => $token]);
+
+      if (!empty($subscriptions)) {
+        /** @var \Drupal\page_notifications\Entity\SubscriptionInterface $subscription */
+        $subscription = reset($subscriptions);
+
+        // Get entity details before setting active
+        $entity_id = $subscription->getSubscribedEntityId();
+        $entity_type = $subscription->getSubscribedEntityType();
+
+        // Activate the subscription
+        $subscription->setActive(TRUE);
+        $subscription->save();
+
+        $this->messenger->addStatus($this->t('Thank you! Your subscription has been verified.'));
+
+        // Load the entity and get its URL
+        try {
+          $entity = $this->entityTypeManager
+            ->getStorage($entity_type)
+            ->load($entity_id);
+
+          if ($entity && $entity->hasLinkTemplate('canonical')) {
+            return new RedirectResponse($entity->toUrl()->toString());
+          }
+        }
+        catch (\Exception $e) {
+          \Drupal::logger('page_notifications')->error('Verification redirect error: @message', ['@message' => $e->getMessage()]);
+        }
+      }
+      else {
+        $this->messenger->addError($this->t('Sorry, this verification link is invalid or has expired.'));
+      }
+    }
+    catch (\Exception $e) {
+      $this->messenger->addError($this->t('An error occurred while verifying your subscription.'));
+      \Drupal::logger('page_notifications')->error('Verification error: @message', ['@message' => $e->getMessage()]);
+    }
+
+    // Fallback to homepage if anything goes wrong
+    return new RedirectResponse('/');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function notifySubscribers(EntityInterface $entity) {
+    try {
+      $subscriptions = $this->entityTypeManager
+        ->getStorage('page_notification_subscription')
+        ->loadByProperties([
+          'subscribed_entity_id' => $entity->id(),
+          'subscribed_entity_type' => $entity->getEntityTypeId(),
+          'status' => TRUE,
+        ]);
+
+      foreach ($subscriptions as $subscription) {
+        $this->queueNotification($subscription, $entity);
+      }
+    }
+    catch (\Exception $e) {
+      $this->loggerFactory->get('page_notifications')
+        ->error('Failed to notify subscribers: @message', ['@message' => $e->getMessage()]);
+      throw $e;
+    }
+  }
+
+  /**
+   * Sends an "already subscribed" email notification.
+   *
+   * @param \Drupal\page_notifications\Entity\SubscriptionInterface $subscription
+   *   The subscription entity.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being subscribed to.
+   */
+  protected function sendAlreadySubscribedEmail($subscription, $entity) {
+    $config = $this->configFactory->get('page_notifications.settings');
+    
+    $params = [
+      'subscription' => $subscription,
+      'entity' => $entity,
+    ];
+
+    $this->mailManager->mail(
+      'page_notifications',
+      'already_subscribed',
+      $subscription->getEmail(),
+      $subscription->getLanguageCode(),
+      $params,
+      $config->get('notification_settings.from_email')
+    );
+  }
+
+  /**
+   * Retrieves the queue for processing notifications.
+   *
+   * @return \Drupal\Core\Queue\QueueInterface
+   *   The queue.
+   */
+  protected function getQueue() {
+    return $this->queueFactory->get('page_notifications_queue');
+  }
+
+  /**
+   * Queues a notification for processing.
+   *
+   * @param \Drupal\page_notifications\Entity\SubscriptionInterface $subscription
+   *   The subscription entity.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity that was updated.
+   */
+  protected function queueNotification($subscription, EntityInterface $entity) {
+    $queue = $this->getQueue();
+    $queue->createItem([
+      'subscription_id' => $subscription->id(),
+      'entity_id' => $entity->id(),
+      'entity_type' => $entity->getEntityTypeId(),
+    ]);
+  }
+
+  /**
+   * Sends a verification email to the subscriber.
+   *
+   * @param \Drupal\page_notifications\Entity\SubscriptionInterface $subscription
+   *   The subscription entity.
+   */
+  protected function sendVerificationEmail($subscription) {
+    $config = $this->configFactory->get('page_notifications.settings');
+    $entity = $this->entityTypeManager
+      ->getStorage($subscription->getSubscribedEntityType())
+      ->load($subscription->getSubscribedEntityId());
+
+    $params = [
+      'subscription' => $subscription,
+      'entity' => $entity,
+      'verify_url' => Url::fromRoute('page_notifications.subscription.verify', [
+        'token' => $subscription->getToken(),
+      ])->setAbsolute(TRUE)
+        ->toString(),
+    ];
+
+    $this->mailManager->mail(
+      'page_notifications',
+      'verification',
+      $subscription->getEmail(),
+      $subscription->getLanguageCode(),
+      $params,
+      $config->get('notification_settings.from_email')
+    );
+  }
+
+  /**
+   * Generates a unique token for subscription verification.
+   *
+   * @return string
+   *   The generated token.
+   */
+  protected function generateToken() {
+    return bin2hex(random_bytes(32));
+  }
+
+  /**
+   * Checks if verification is required based on configuration.
+   *
+   * @return bool
+   *   TRUE if verification is required, FALSE otherwise.
+   */
+  protected function requiresVerification() {
+    return $this->configFactory
+      ->get('page_notifications.settings')
+      ->get('security.require_verification') ?? TRUE;
+  }
+
+  /**
+   * Checks if a subscription token has expired.
+   *
+   * @param \Drupal\page_notifications\Entity\SubscriptionInterface $subscription
+   *   The subscription entity.
+   *
+   * @return bool
+   *   TRUE if the token has expired, FALSE otherwise.
+   */
+  protected function isTokenExpired($subscription) {
+    if ($subscription->isActive()) {
+      return FALSE;
+    }
+
+    $config = $this->configFactory->get('page_notifications.settings');
+    $expiration_hours = $config->get('notification_settings.token_expiration') ?? 48;
+    $expiration_timestamp = $subscription->getCreatedTime() + ($expiration_hours * 3600);
+
+    return $this->time->getRequestTime() > $expiration_timestamp;
+  }
+
+}
\ No newline at end of file
diff --git a/src/Service/NotificationManagerInterface.php b/src/Service/NotificationManagerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5bf5a5b33288c5de4d939e38951abf59cb9922b
--- /dev/null
+++ b/src/Service/NotificationManagerInterface.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\page_notifications\Service;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Interface for notification management service.
+ */
+interface NotificationManagerInterface {
+
+  /**
+   * Creates a new subscription.
+   *
+   * @param string $email
+   *   The subscriber's email address.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being subscribed to.
+   * @param string|null $langcode
+   *   The language code for the subscription. Defaults to site's default language.
+   *
+   * @return \Drupal\page_notifications\Entity\SubscriptionInterface
+   *   The created subscription entity.
+   *
+   * @throws \Exception
+   *   If the subscription cannot be created.
+   */
+  public function createSubscription(string $email, EntityInterface $entity, string $langcode = LanguageInterface::LANGCODE_DEFAULT);
+
+  /**
+   * Verifies a subscription using a token.
+   *
+   * @param string $token
+   *   The verification token.
+   *
+   * @return bool
+   *   TRUE if verification was successful, FALSE otherwise.
+   */
+  public function verifySubscription(string $token);
+
+  /**
+   * Notifies subscribers about updates to an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity that was updated.
+   *
+   * @throws \Exception
+   *   If notifications cannot be sent.
+   */
+  public function notifySubscribers(EntityInterface $entity);
+
+}
\ No newline at end of file
diff --git a/src/Service/SpamPrevention.php b/src/Service/SpamPrevention.php
new file mode 100644
index 0000000000000000000000000000000000000000..c063d3bd6f7a8f84375fac744086029d3d4b355b
--- /dev/null
+++ b/src/Service/SpamPrevention.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Drupal\page_notifications\Service;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\SessionManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Service for handling spam prevention in Page Notifications.
+ */
+class SpamPrevention {
+  use StringTranslationTrait;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The session manager.
+   *
+   * @var \Drupal\Core\Session\SessionManagerInterface
+   */
+  protected $sessionManager;
+
+  /**
+   * Constructs a new SpamPrevention object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
+   *   The session manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(
+    ConfigFactoryInterface $config_factory,
+    ModuleHandlerInterface $module_handler,
+    SessionManagerInterface $session_manager,
+    TranslationInterface $string_translation
+  ) {
+    $this->configFactory = $config_factory;
+    $this->moduleHandler = $module_handler;
+    $this->sessionManager = $session_manager;
+    $this->setStringTranslation($string_translation);
+  }
+
+  /**
+   * Generates a math challenge.
+   *
+   * @return array
+   *   An array containing the challenge question, numbers, operator and answer.
+   */
+  public function generateMathChallenge() {
+    $config = $this->configFactory->get('page_notifications.settings');
+    $operator = $config->get('spam_prevention.math_operator') ?? '+';
+
+    // Generate two random numbers between 1 and 10
+    $num1 = rand(1, 10);
+    $num2 = rand(1, 10);
+
+    // Calculate the answer
+    $answer = $operator === '+' ? $num1 + $num2 : $num1 * $num2;
+
+    return [
+      'num1' => $num1,
+      'num2' => $num2,
+      'operator' => $operator,
+      'question' => $this->t('What is @num1 @operator @num2?', [
+        '@num1' => $num1,
+        '@operator' => $operator,
+        '@num2' => $num2,
+      ]),
+      'answer' => $answer,
+    ];
+  }
+
+  /**
+   * Validates a math challenge response.
+   *
+   * @param int $response
+   *   The user's response to the challenge.
+   * @param array $challenge
+   *   The original challenge array containing num1, num2, and operator.
+   *
+   * @return bool
+   *   TRUE if the response is correct, FALSE otherwise.
+   */
+  public function validateMathResponse($response, array $challenge) {
+    $answer = $challenge['operator'] === '+'
+      ? $challenge['num1'] + $challenge['num2']
+      : $challenge['num1'] * $challenge['num2'];
+
+    return (int) $response === (int) $answer;
+  }
+  /**
+   * Checks if reCAPTCHA is available and configured.
+   *
+   * @return bool
+   *   TRUE if reCAPTCHA is available and configured, FALSE otherwise.
+   */
+  public function isRecaptchaAvailable() {
+    return $this->moduleHandler->moduleExists('captcha') &&
+           $this->moduleHandler->moduleExists('recaptcha') &&
+           $this->configFactory->get('recaptcha.settings')->get('site_key');
+  }
+}
\ No newline at end of file
diff --git a/src/Token/SubscriptionToken.php b/src/Token/SubscriptionToken.php
new file mode 100644
index 0000000000000000000000000000000000000000..79cb38c711d170aac478f8abab0195cd0f2815bf
--- /dev/null
+++ b/src/Token/SubscriptionToken.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace Drupal\page_notifications\Token;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Url;
+use Drupal\node\NodeInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Implements hook_token_info() and hook_tokens().
+ */
+class SubscriptionToken {
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static();
+  }
+
+  /**
+   * Implements hook_token_info().
+   */
+  public function hookTokenInfo() {
+    $type = 'page_notification_subscription';
+
+    $info['types'][$type] = [
+      'name' => $this->t('Page Notification Subscription'),
+      'description' => $this->t('Tokens related to page notification subscriptions'),
+      'needs-data' => $type,
+    ];
+
+    $info['tokens'][$type] = [
+      'verify-url' => [
+        'name' => $this->t('Verification URL'),
+        'description' => $this->t('The URL to verify the subscription'),
+      ],
+      'unsubscribe-url' => [
+        'name' => $this->t('Unsubscribe URL'),
+        'description' => $this->t('The URL to unsubscribe from notifications'),
+      ],
+      'email' => [
+        'name' => $this->t('Email'),
+        'description' => $this->t('The subscriber\'s email address'),
+      ],
+    ];
+
+    // For backwards compatibility, add alias
+    $info['types']['subscription'] = [
+      'name' => $this->t('Subscription (Legacy)'),
+      'description' => $this->t('Legacy tokens for page notification subscriptions. Use page_notification_subscription instead.'),
+      'needs-data' => $type,
+    ];
+    $info['tokens']['subscription'] = $info['tokens'][$type];
+
+    return $info;
+  }
+
+  /**
+   * Implements hook_tokens().
+   */
+  public function hookTokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+    $replacements = [];
+
+    // TODO find why some sites its 'page_notification_subscription' and other its 'subscription'
+    if (($type == 'page_notification_subscription' || $type == 'subscription') && (!empty($data['page_notification_subscription']) || !empty($data['subscription']))) {
+      $subscription = !empty($data['page_notification_subscription']) ? 
+        $data['page_notification_subscription'] : 
+        $data['subscription'];
+      $bubbleable_metadata->addCacheableDependency($subscription);
+
+      foreach ($tokens as $name => $original) {
+        switch ($name) {
+          case 'verify-url':
+            $replacements[$original] = Url::fromRoute('page_notifications.subscription.verify',
+              ['token' => $subscription->getToken()],
+              ['absolute' => TRUE]
+            )->toString();
+            $bubbleable_metadata->addCacheableDependency($subscription);
+            break;
+
+            case 'unsubscribe-url':
+              $replacements[$original] = Url::fromRoute('page_notifications.subscription.unsubscribe',
+                [
+                  'subscription' => $subscription->id(),
+                  'token' => $subscription->getUnsubscribeToken(),
+                ],
+                ['absolute' => TRUE]
+              )->toString();
+              $bubbleable_metadata->addCacheableDependency($subscription);
+              break;
+
+          case 'email':
+            $replacements[$original] = $subscription->getEmail();
+            $bubbleable_metadata->addCacheableDependency($subscription);
+            break;
+        }
+      }
+      return $replacements;
+    }
+
+    if ($type == 'page_notification_notification' && !empty($data['entity'])) {
+      $entity = $data['entity'];
+      $bubbleable_metadata->addCacheableDependency($entity);
+
+      foreach ($tokens as $name => $original) {
+        switch ($name) {
+          case 'notes':
+            // First check for manual notification notes
+            $notes = \Drupal::state()->get('page_notifications_manual_notes_' . $entity->id(), '');
+
+            // If no manual notes and entity is a node, try to get revision log
+            if (empty($notes) && $entity instanceof NodeInterface) {
+              $notes = $entity->getRevisionLogMessage();
+            }
+
+            $replacements[$original] = $notes;
+
+            // Clean up stored manual notes if they exist
+            if (\Drupal::state()->get('page_notifications_manual_notes_' . $entity->id())) {
+              \Drupal::state()->delete('page_notifications_manual_notes_' . $entity->id());
+            }
+            $bubbleable_metadata->addCacheableDependency($entity);
+            break;
+        }
+      }
+    }
+
+    return $replacements;
+  }
+}
\ No newline at end of file
diff --git a/src/Traits/FloodControlTrait.php b/src/Traits/FloodControlTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..3e055417fb8302afbaa037970d058085b10e4294
--- /dev/null
+++ b/src/Traits/FloodControlTrait.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace Drupal\page_notifications\Traits;
+
+use Drupal\Core\Flood\FloodInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides flood control functionality for forms.
+ */
+trait FloodControlTrait {
+
+  /**
+   * The flood service.
+   *
+   * @var \Drupal\Core\Flood\FloodInterface|null
+   */
+  protected ?FloodInterface $flood = NULL;
+
+  /**
+   * Sets the flood service.
+   *
+   * @param \Drupal\Core\Flood\FloodInterface $flood
+   *   The flood service.
+   */
+  public function setFloodService(FloodInterface $flood) {
+    $this->flood = $flood;
+  }
+
+  /**
+   * Gets the flood service.
+   *
+   * @return \Drupal\Core\Flood\FloodInterface
+   *   The flood service.
+   */
+  protected function getFlood(): FloodInterface {
+    if (!$this->flood) {
+      $this->flood = \Drupal::service('flood');
+    }
+    return $this->flood;
+  }
+
+  /**
+   * Gets the flood control configuration.
+   *
+   * @return array
+   *   An array containing flood control settings.
+   */
+  protected function getFloodControlConfig() {
+    $config = $this->configFactory()->get('page_notifications.settings');
+
+    return [
+      'ip_limit' => $config->get('security.flood_control.ip_limit') ?? 200,
+      'ip_window' => ($config->get('security.flood_control.ip_window') ?? 1) * 3600,
+      'identifier_limit' => $config->get('security.flood_control.identifier_limit') ?? 50,
+      'identifier_window' => ($config->get('security.flood_control.identifier_window') ?? 1) * 3600,
+    ];
+  }
+
+  /**
+ * Checks if the current request should be allowed through flood control.
+ *
+ * @param string $identifier
+ *   The identifier to check (typically an email).
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   The form state.
+ *
+ * @return bool
+ *   TRUE if the request should be allowed, FALSE otherwise.
+ */
+protected function checkFloodControl($identifier, FormStateInterface $form_state) {
+  $ip = \Drupal::request()->getClientIp();
+  $settings = $this->getFloodControlConfig();
+  $flood = $this->getFlood();
+
+  // Skip IP-based flood control if window is set to 0
+  if ($settings['ip_window'] > 0) {
+    $ip_event = 'page_notifications.subscribe_ip.' . $ip;
+    if (!$flood->isAllowed($ip_event, $settings['ip_limit'], $settings['ip_window'])) {
+      $form_state->setErrorByName('', $this->t('Too many subscription attempts from this IP address. Please try again in @hours hours.',
+        ['@hours' => floor($settings['ip_window'] / 3600)]
+      ));
+      $this->logSecurityEvent('flood_control_ip', ['ip' => $ip]);
+      return FALSE;
+    }
+  }
+
+  // Skip identifier-based flood control if window is set to 0
+  if ($settings['identifier_window'] > 0) {
+    $identifier_event = 'page_notifications.subscribe_identifier.' . $identifier;
+    if (!$flood->isAllowed($identifier_event, $settings['identifier_limit'], $settings['identifier_window'])) {
+      $form_state->setErrorByName('email', $this->t('Too many subscription attempts for this email address. Please try again in @hours hours.',
+        ['@hours' => floor($settings['identifier_window'] / 3600)]
+      ));
+      $this->logSecurityEvent('flood_control_identifier', ['identifier' => $identifier]);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+  /**
+ * Registers a flood event.
+ *
+ * @param string $identifier
+ *   The identifier for the flood event.
+ */
+protected function registerFloodControl($identifier) {
+  $ip = \Drupal::request()->getClientIp();
+  $settings = $this->getFloodControlConfig();
+  $flood = $this->getFlood();
+
+  // Only register IP-based flood events if window is greater than 0
+  if ($settings['ip_window'] > 0) {
+    $ip_event = 'page_notifications.subscribe_ip.' . $ip;
+    $flood->register($ip_event, $settings['ip_window']);
+  }
+
+  // Only register identifier-based flood events if window is greater than 0
+  if ($settings['identifier_window'] > 0) {
+    $identifier_event = 'page_notifications.subscribe_identifier.' . $identifier;
+    $flood->register($identifier_event, $settings['identifier_window']);
+  }
+}
+
+  /**
+   * Logs a security event.
+   *
+   * @param string $type
+   *   The type of security event.
+   * @param array $data
+   *   Additional data to log.
+   */
+  protected function logSecurityEvent($type, array $data) {
+    \Drupal::logger('page_notifications_security')->warning(
+      '@type: @data',
+      ['@type' => $type, '@data' => json_encode($data)]
+    );
+  }
+}
\ No newline at end of file
diff --git a/templates/block--page-notifications-subscription.html.twig b/templates/block--page-notifications-subscription.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..6d02b7f4915e233607155890b7d88a6cd73ca7c5
--- /dev/null
+++ b/templates/block--page-notifications-subscription.html.twig
@@ -0,0 +1,38 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a Page Notifications subscription block.
+ *
+ * Available variables:
+ * - plugin_id: The ID of the block implementation.
+ * - label: The configured label of the block if visible.
+ * - configuration: A list of the block's configuration values.
+ *   - block_description: The configured description text.
+ *   - button_text: The configured button text.
+ *   - button_classes: CSS classes for the button.
+ *   - form_classes: CSS classes for the form wrapper.
+ * - content: The content of the block.
+ * - attributes: HTML attributes for the block wrapper.
+ *
+ * @ingroup themeable
+ */
+#}
+{% set classes = [
+  'block',
+  'block-' ~ configuration.provider|clean_class,
+  'block-' ~ plugin_id|clean_class,
+]%}
+
+<div{{ attributes.addClass(classes) }}>
+  {{ title_prefix }}
+  {% if label %}
+    <h2{{ title_attributes }}>{{ label }}</h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {% block content %}
+    <div{{ content_attributes.addClass('content') }}>
+      {{ content }}
+    </div>
+  {% endblock %}
+</div>
\ No newline at end of file
diff --git a/templates/description.html.twig b/templates/description.html.twig
deleted file mode 100644
index 54192bafae7ccd8426f0c85d1605014b578441d7..0000000000000000000000000000000000000000
--- a/templates/description.html.twig
+++ /dev/null
@@ -1,34 +0,0 @@
-{#
-
-Description text for the Page Notifications.
-
-#}
-
-{% set custom_access = path('page_notifications.custom_access') %}
-{% set permissioned = path('page_notifications.permissioned') %}
-{% set route_only = path('page_notifications.route_only') %}
-{% set tabs = path('page_notifications.tabs') %}
-{% set use_url_arguments = path('page_notifications.use_url_arguments') %}
-{% set title_callbacks = path('page_notifications.title_callbacks') %}
-{% set placeholder_argument = path('page_notifications.placeholder_argument') %}
-{% set path_override = path('page_notifications.path_override') %}
-
-{% trans %}
-
-<p>This page is displayed by the simplest (and base) menu example. Note that
-the title of the page is the same as the link title. There are a number of
-examples here, from the most basic (like this one) to extravagant mappings of
-loaded placeholder arguments. Enjoy!</p>
-
-<ul>
-    <li><a href={{ custom_access }}>Custom Access Notifications</a></li>
-    <li><a href={{ permissioned }}>Permissioned Notifications</a></li>
-    <li><a href={{ route_only }}>Route only Notifications</a></li>
-    <li><a href={{ tabs }}>Tabs</a></li>
-    <li><a href={{ use_url_arguments }}>URL Arguments</a></li>
-    <li><a href={{ title_callbacks }}>Dynamic title</a></li>
-    <li><a href={{ placeholder_argument }}>Placeholder Arguments</a></li>
-    <li><a href={{ path_override }}>Path Override</a></li>
-</ul>
-
-{% endtrans %}
diff --git a/templates/page-notifications-email-wrapper.html.twig b/templates/page-notifications-email-wrapper.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..d03308f8708f53d0e21d38585a4544f6df2ad9d7
--- /dev/null
+++ b/templates/page-notifications-email-wrapper.html.twig
@@ -0,0 +1,38 @@
+{#
+/**
+ * @file
+ * Default template for Page Notifications emails.
+ *
+ * Available variables:
+ * - content: The main email content.
+ * - email_type: The type of email (verification, notification, etc.).
+ * - subscription: The subscription entity.
+ * - entity: The subscribed entity.
+ * - logo_url: The site logo URL.
+ * - site_name: The site name.
+ * - footer: Custom footer content.
+ */
+#}
+<div class="page-notifications-email">
+  {% if logo_url %}
+    <div class="email-header">
+      <img src="{{ logo_url }}" alt="{{ site_name }}" style="max-width: 200px; height: auto;">
+    </div>
+  {% endif %}
+
+  <div class="email-content">
+    {{ content }}
+  </div>
+
+  {% if footer %}
+    <div class="email-footer">
+      {{ footer }}
+    </div>
+  {% else %}
+    <div class="email-footer">
+      <p style="color: #666; font-size: 12px;">
+        © {{ "now"|date("Y") }} {{ site_name }}
+      </p>
+    </div>
+  {% endif %}
+</div>
\ No newline at end of file
diff --git a/templates/page-notifications-modal-success.html.twig b/templates/page-notifications-modal-success.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..dc0ec98023b2f2b9da0462655fab9cf000197234
--- /dev/null
+++ b/templates/page-notifications-modal-success.html.twig
@@ -0,0 +1,10 @@
+<div class="subscription-success">
+  <div class="success-message">
+    {{ message }}
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="button" data-drupal-selector="modal-close">
+      {{ 'Close'|t }}
+    </button>
+  </div>
+</div>
\ No newline at end of file
diff --git a/upgrade-docs.md b/upgrade-docs.md
new file mode 100644
index 0000000000000000000000000000000000000000..85d010765c4ced024f8eaffbf1e55363f56a8421
--- /dev/null
+++ b/upgrade-docs.md
@@ -0,0 +1,31 @@
+# Upgrading from Page Notifications 3.x to 4.x
+
+## Breaking Changes
+- Complete rewrite of the module's architecture
+- New configuration system
+- New subscription storage using custom entities
+
+## Migration Process
+1. Back up your database before upgrading
+2. Install the new version over the old one
+3. Run database updates (`drush updb` or visit /update.php)
+
+## Post-Migration Steps
+After upgrading, you will need to reconfigure your Page Notifications settings:
+
+1. Visit `/admin/config/system/page-notifications`
+2. Configure the following settings:
+   - Email settings
+   - Notification templates
+   - Spam protection settings
+   - Security settings
+
+## Previous Settings
+Your previous settings from v3 will not be automatically migrated. Make note of your current settings before upgrading:
+1. Email templates
+2. From email address
+3. CAPTCHA configuration
+4. Other customizations
+
+## Subscription Data
+All subscription data (email addresses, subscribed content, tokens) will be automatically migrated to the new system.