diff --git a/includes/slm-blocks.php b/includes/slm-blocks.php
new file mode 100644
index 0000000..5216ec1
--- /dev/null
+++ b/includes/slm-blocks.php
@@ -0,0 +1,25 @@
+options} WHERE option_name LIKE '_transient_$like_pattern' OR option_name LIKE '_transient_timeout_$like_pattern'";
+ $wpdb->query($sql);
+
+ add_action('admin_notices', function () {
+ echo '
All rate-limiting transients have been cleared.
';
+ });
+ }
+});
+
+
+// Custom Hooks
+// Hooks added to customize the text or logic:
+
+// slm_invalid_email_message: Customize the invalid email message.
+// slm_success_message: Customize the success message.
+// slm_no_license_message: Customize the "no license found" message.
+// slm_license_email_message: Modify the email message body.
+// slm_license_email_subject: Modify the email subject.
+// slm_form_label: Change the form label text.
+// slm_form_button_text: Change the form button text.
+
+class SLM_Forgot_License {
+ /**
+ * Initialize the class and hooks.
+ */
+ public function __construct() {
+ // Register shortcode.
+ add_shortcode('slm_forgot_license', [$this, 'render_shortcode']);
+ }
+
+ /**
+ * Render the shortcode.
+ *
+ * @return string
+ */
+ public function render_shortcode() {
+ // Check if form is submitted.
+ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['slm_forgot_license_nonce'])) {
+ return $this->handle_form_submission();
+ }
+
+ // Output the form.
+ return $this->render_form();
+ }
+
+ /**
+ * Handle form submission.
+ *
+ * @return string
+ */
+ protected function handle_form_submission() {
+ // Verify nonce for security.
+ if (!isset($_POST['slm_forgot_license_nonce']) ||
+ !wp_verify_nonce($_POST['slm_forgot_license_nonce'], 'slm_forgot_license_action')) {
+ return 'Invalid request. Please try again.
';
+ }
+
+ // Sanitize and validate email.
+ $email = sanitize_email($_POST['slm_forgot_license_email']);
+ if (!is_email($email)) {
+ return apply_filters('slm_invalid_email_message', 'Invalid email address.
');
+ }
+
+ // Rate limiting: prevent repeated submissions from the same IP.
+ $ip = $_SERVER['REMOTE_ADDR'];
+ if ($this->is_rate_limited($ip)) {
+ return 'You are submitting requests too quickly. Please try again later.
';
+ }
+
+ // Retrieve licenses.
+ $licenses = SLM_Utility::get_licenses_by_email($email);
+
+ if (!empty($licenses)) {
+ // Prepare and send the email.
+ $message = apply_filters('slm_license_email_message', $this->generate_email_message($licenses), $licenses);
+ wp_mail($email, apply_filters('slm_license_email_subject', 'Your Licenses'), $message);
+
+ return apply_filters('slm_success_message', 'Your licenses have been sent to your email.
');
+ }
+
+ return apply_filters('slm_no_license_message', 'No licenses found for the provided email.
');
+ }
+
+ /**
+ * Check if the IP is rate-limited.
+ *
+ * @param string $ip
+ * @return bool
+ */
+ protected function is_rate_limited($ip) {
+ // Allow administrators to bypass rate limiting and clear transients.
+ if (is_user_logged_in() && current_user_can('manage_options')) {
+ $this->clear_transients();
+ return false; // Admins are not rate-limited.
+ }
+
+ $key = 'slm_rate_limit_' . $ip;
+ $limit = 3; // Max submissions allowed per hour.
+ $time_frame = HOUR_IN_SECONDS;
+
+ $attempts = get_transient($key);
+
+ if ($attempts === false) {
+ set_transient($key, 1, $time_frame);
+ return false;
+ }
+
+ if ($attempts >= $limit) {
+ return true;
+ }
+
+ set_transient($key, $attempts + 1, $time_frame);
+ return false;
+ }
+
+ /**
+ * Clear all relevant transients.
+ */
+ protected function clear_transients() {
+ global $wpdb;
+
+ // Search and delete all transients related to rate limiting.
+ $like_pattern = '%slm_rate_limit_%';
+ $sql = "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_$like_pattern' OR option_name LIKE '_transient_timeout_$like_pattern'";
+ $wpdb->query($sql);
+ }
+
+
+
+
+ /**
+ * Generate the email message.
+ *
+ * @param array $licenses
+ * @return string
+ */
+ protected function generate_email_message($licenses) {
+ $message = "Here are your licenses:\n\n";
+
+ foreach ($licenses as $license) {
+ $message .= "License Key: {$license['license_key']}\n";
+ // $message .= "Product: {$license['product_ref']}\n";
+ $message .= "Status: {$license['lic_status']}\n\n";
+ }
+
+ return $message;
+ }
+
+ /**
+ * Render the form HTML.
+ *
+ * @return string
+ */
+ protected function render_form() {
+ ob_start();
+ ?>
+
+ ' . __('You must be logged in to view your licenses.', 'slm-plus') . '';
+ }
+
+ // Get the current user's email.
+ $current_user = wp_get_current_user();
+ $email = $current_user->user_email;
+
+ // Retrieve licenses for the user.
+ $licenses = $this->get_licenses_by_email($email);
+
+ // Render the licenses table or a message if none are found.
+ if (empty($licenses)) {
+ return apply_filters('slm_no_license_message', '' . __('No licenses found for your account.', 'slm-plus') . '
');
+ }
+
+ return $this->render_licenses_table($licenses);
+ }
+
+ /**
+ * Retrieve licenses by email from the database.
+ *
+ * @param string $email
+ * @return array
+ */
+ protected function get_licenses_by_email($email) {
+ global $wpdb;
+
+ $table_name = $wpdb->prefix . 'lic_key_tbl'; // Update to match your actual table name.
+ $query = $wpdb->prepare(
+ "SELECT license_key, product_ref, lic_status, date_expiry, date_activated, max_allowed_domains, max_allowed_devices
+ FROM $table_name WHERE email = %s",
+ $email
+ );
+
+ return $wpdb->get_results($query, ARRAY_A);
+ }
+
+ /**
+ * Render the licenses table HTML.
+ *
+ * @param array $licenses
+ * @return string
+ */
+ protected function render_licenses_table($licenses) {
+ ob_start();
+ ?>
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+ prepare(
+ "SELECT license_key, product_ref, lic_status FROM $table_name WHERE email = %s",
+ $email
+ );
+
+ return $wpdb->get_results($query, ARRAY_A);
+ }
+
/**
* Saves a backup of the plugin's database tables in a secure folder.
*/
diff --git a/public/assets/css/slm-blocks.css b/public/assets/css/slm-blocks.css
new file mode 100644
index 0000000..bf7a5ef
--- /dev/null
+++ b/public/assets/css/slm-blocks.css
@@ -0,0 +1,84 @@
+.wp-block-slm-forgot-license {
+ background-color: #f9f9f9;
+ border: 1px dashed #ddd;
+ padding: 10px;
+ text-align: center;
+ font-size: 14px;
+ font-style: italic;
+}
+/* Style for the block preview */
+.slm-forgot-license-preview {
+ padding: 20px;
+ background-color: #f9f9f9;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ max-width: 400px;
+ font-family: Arial, sans-serif;
+}
+
+/* Vertically stacked form */
+.slm-forgot-license-form {
+ display: flex;
+ flex-direction: column; /* Stack items vertically */
+ gap: 10px; /* Adds spacing between form elements */
+ width: 100%; /* Ensure the form takes full width */
+}
+
+.slm-forgot-license-form label {
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.slm-forgot-license-form input {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-sizing: border-box; /* Ensures padding doesn't affect width */
+}
+
+.slm-forgot-license-form button {
+ padding: 10px;
+ background-color: #0073aa;
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ cursor: not-allowed; /* Indicate the button is disabled in preview */
+ text-align: center;
+ font-size: 16px;
+}
+
+/* Add hover styling for better preview (if enabled) */
+.slm-forgot-license-form button:hover {
+ background-color: #005f8d;
+}
+
+/* Style for the List Licenses block preview */
+.slm-list-licenses-preview {
+ padding: 20px;
+ background-color: #f9f9f9;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ max-width: 400px;
+ font-family: Arial, sans-serif;
+}
+
+.slm-list-licenses-preview-message {
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.slm-list-licenses-placeholder {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+}
+
+.slm-list-licenses-placeholder li {
+ padding: 5px 0;
+ border-bottom: 1px solid #ddd;
+}
+
+.slm-list-licenses-placeholder li:last-child {
+ border-bottom: none;
+}
diff --git a/public/assets/css/slm-front-end.css b/public/assets/css/slm-front-end.css
index 0e26ff1..ce8a4b3 100644
--- a/public/assets/css/slm-front-end.css
+++ b/public/assets/css/slm-front-end.css
@@ -24,3 +24,30 @@
.slm-status-badge.status-expired {
background-color: #6c757d; /* Gray */
}
+
+.slm-licenses-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 20px 0;
+ font-size: 14px;
+ font-family: Arial, sans-serif;
+ text-align: left;
+}
+
+.slm-licenses-table th, .slm-licenses-table td {
+ border: 1px solid #ddd;
+ padding: 10px;
+}
+
+.slm-licenses-table th {
+ background-color: #f4f4f4;
+ font-weight: bold;
+}
+
+.slm-licenses-table tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+
+.slm-licenses-table tr:hover {
+ background-color: #f1f1f1;
+}
diff --git a/public/assets/js/slm-blocks.js b/public/assets/js/slm-blocks.js
new file mode 100644
index 0000000..c27c138
--- /dev/null
+++ b/public/assets/js/slm-blocks.js
@@ -0,0 +1,97 @@
+const { registerBlockType } = wp.blocks;
+const { createElement } = wp.element;
+const { __ } = wp.i18n;
+
+// Register a custom block category.
+wp.domReady(() => {
+ // Check if the category already exists.
+ const categories = wp.blocks.getCategories();
+
+ if (!categories.some((category) => category.slug === 'slm-plus')) {
+ // Add the custom category.
+ wp.blocks.setCategories([
+ ...categories,
+ {
+ slug: 'slm-plus',
+ title: __('SLM Plus', 'slm-plus'),
+ icon: 'admin-network', // Dashicon or a custom icon.
+ },
+ ]);
+ }
+});
+
+
+// Register the block in the "SLM Plus" category.
+registerBlockType('slm-plus/forgot-license', {
+ title: __('Forgot License', 'slm-plus'),
+ icon: 'lock', // Dashicon or custom icon for the block.
+ category: 'slm-plus', // Assign to the SLM Plus category.
+ attributes: {},
+
+ edit: () => {
+ // Create a more realistic preview for the editor.
+ return createElement(
+ 'div',
+ { className: 'slm-forgot-license-preview' },
+ createElement(
+ 'form',
+ { className: 'slm-forgot-license-form' },
+ createElement(
+ 'label',
+ { htmlFor: 'slm-email' },
+ __('Enter your email address:', 'slm-plus')
+ ),
+ createElement('input', {
+ type: 'email',
+ id: 'slm-email',
+ placeholder: __('example@domain.com', 'slm-plus'),
+ disabled: true, // Disable interaction in the editor.
+ }),
+ createElement(
+ 'button',
+ { type: 'button', disabled: true },
+ __('Retrieve License', 'slm-plus')
+ )
+ )
+ );
+ },
+
+ save: () => {
+ // The saved output will still be the shortcode for frontend rendering.
+ return createElement('p', {}, '[slm_forgot_license]');
+ },
+});
+
+
+// Register the "List Licenses" block.
+registerBlockType('slm-plus/list-licenses', {
+ title: __('List Licenses', 'slm-plus'),
+ icon: 'list-view', // Dashicon for the block.
+ category: 'slm-plus', // Assign to the SLM Plus category.
+ attributes: {},
+
+ edit: () => {
+ return createElement(
+ 'div',
+ { className: 'slm-list-licenses-preview' },
+ createElement(
+ 'p',
+ { className: 'slm-list-licenses-preview-message' },
+ __('This block will display a list of licenses associated with the logged-in user.', 'slm-plus')
+ ),
+ createElement(
+ 'ul',
+ { className: 'slm-list-licenses-placeholder' },
+ createElement('li', {}, __('License Key: ************', 'slm-plus')),
+ createElement('li', {}, __('Product: Example Product', 'slm-plus')),
+ createElement('li', {}, __('Status: Active', 'slm-plus')),
+ createElement('li', {}, __('Expiry Date: 2024-12-31', 'slm-plus'))
+ )
+ );
+ },
+
+ save: () => {
+ // Outputs the shortcode for rendering on the frontend.
+ return createElement('p', {}, '[slm_list_licenses]');
+ },
+});
diff --git a/slm-plus.php b/slm-plus.php
index dde9daf..7707ddc 100644
--- a/slm-plus.php
+++ b/slm-plus.php
@@ -1,7 +1,7 @@