mirror of
https://ghproxy.net/https://github.com/michelve/software-license-manager.git
synced 2025-10-04 02:06:37 +08:00
Add custom block category "SLM Plus" and improve shortcode blocks
feat: Add custom block category "SLM Plus" and improve shortcode blocks - Registered a new block category "SLM Plus" for better organization. - Updated "Forgot License" and "List Licenses" blocks to assign them to the custom category. - Improved block editor previews for both blocks: - "Forgot License" block displays a preview with a form placeholder. - "List Licenses" block displays a preview with a sample list of licenses. - Fixed issues with blocks appearing under "Uncategorized" by ensuring proper category registration. - Ensured unique naming in block scripts to avoid conflicts. - Added clear styles for block previews and frontend rendering in `slm-blocks.css`. - Verified shortcode functionality for rendering licenses dynamically.
This commit is contained in:
parent
3d6a5b3d10
commit
f1918ebf47
8 changed files with 543 additions and 2 deletions
25
includes/slm-blocks.php
Normal file
25
includes/slm-blocks.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if (!defined('WPINC')) {
|
||||
die();
|
||||
}
|
||||
|
||||
// Register block editor assets.
|
||||
add_action('enqueue_block_editor_assets', function () {
|
||||
wp_enqueue_script(
|
||||
'slm-blocks-js',
|
||||
SLM_ASSETS_URL . 'js/slm-blocks.js',
|
||||
['wp-blocks', 'wp-editor', 'wp-element', 'wp-i18n'], // Dependencies.
|
||||
SLM_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'slm-blocks-style',
|
||||
SLM_ASSETS_URL . 'css/slm-blocks.css',
|
||||
[],
|
||||
SLM_VERSION
|
||||
);
|
||||
});
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
<?php
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if (!defined('WPINC')) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @package SLM Plus
|
||||
|
@ -20,6 +25,8 @@ require_once(SLM_LIB . 'slm-error-codes.php');
|
|||
require_once(SLM_LIB . 'slm-init-time-tasks.php');
|
||||
require_once(SLM_LIB . 'slm-api-listener.php');
|
||||
require_once(SLM_LIB . 'slm-scripts.php');
|
||||
require_once(SLM_LIB . 'slm-shortcodes.php');
|
||||
require_once(SLM_LIB . 'slm-blocks.php');
|
||||
|
||||
// Admin-only includes
|
||||
if (is_admin()) {
|
||||
|
|
287
includes/slm-shortcodes.php
Normal file
287
includes/slm-shortcodes.php
Normal file
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if (!defined('WPINC')) {
|
||||
die();
|
||||
}
|
||||
|
||||
// If this file is called directly, abort.
|
||||
if (!defined('WPINC')) {
|
||||
die();
|
||||
}
|
||||
|
||||
if (!defined('HOUR_IN_SECONDS')) {
|
||||
define('HOUR_IN_SECONDS', 3600);
|
||||
}
|
||||
|
||||
|
||||
add_action('admin_init', function () {
|
||||
if (is_user_logged_in() && current_user_can('manage_options') && isset($_GET['slm_clear_transients'])) {
|
||||
global $wpdb;
|
||||
$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);
|
||||
|
||||
add_action('admin_notices', function () {
|
||||
echo '<div class="notice notice-success"><p>All rate-limiting transients have been cleared.</p></div>';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 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 '<p>Invalid request. Please try again.</p>';
|
||||
}
|
||||
|
||||
// Sanitize and validate email.
|
||||
$email = sanitize_email($_POST['slm_forgot_license_email']);
|
||||
if (!is_email($email)) {
|
||||
return apply_filters('slm_invalid_email_message', '<p>Invalid email address.</p>');
|
||||
}
|
||||
|
||||
// Rate limiting: prevent repeated submissions from the same IP.
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
if ($this->is_rate_limited($ip)) {
|
||||
return '<p>You are submitting requests too quickly. Please try again later.</p>';
|
||||
}
|
||||
|
||||
// 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', '<p>Your licenses have been sent to your email.</p>');
|
||||
}
|
||||
|
||||
return apply_filters('slm_no_license_message', '<p>No licenses found for the provided email.</p>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
?>
|
||||
<form method="POST" class="slm-forgot-license-form">
|
||||
<label for="slm_email"><?php echo esc_html(apply_filters('slm_form_label', 'Enter your email address:')); ?></label>
|
||||
<input type="email" id="slm_email" name="slm_forgot_license_email" required>
|
||||
<?php wp_nonce_field('slm_forgot_license_action', 'slm_forgot_license_nonce'); ?>
|
||||
<button type="submit"><?php echo esc_html(apply_filters('slm_form_button_text', 'Retrieve License')); ?></button>
|
||||
</form>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SLM_List_Licenses_FE {
|
||||
/**
|
||||
* Initialize the class and hooks.
|
||||
*/
|
||||
public function __construct() {
|
||||
// Register shortcode.
|
||||
add_shortcode('slm_list_licenses', [$this, 'render_shortcode']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the shortcode.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render_shortcode() {
|
||||
// Check if the user is logged in.
|
||||
if (!is_user_logged_in()) {
|
||||
return '<p>' . __('You must be logged in to view your licenses.', 'slm-plus') . '</p>';
|
||||
}
|
||||
|
||||
// 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', '<p>' . __('No licenses found for your account.', 'slm-plus') . '</p>');
|
||||
}
|
||||
|
||||
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();
|
||||
?>
|
||||
<table class="slm-licenses-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo esc_html(__('License Key', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Product', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Status', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Activated On', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Expiry Date', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Max Domains', 'slm-plus')); ?></th>
|
||||
<th><?php echo esc_html(__('Max Devices', 'slm-plus')); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($licenses as $license): ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($license['license_key']); ?></td>
|
||||
<td><?php echo esc_html($license['product_ref']); ?></td>
|
||||
<td><?php echo esc_html($license['lic_status']); ?></td>
|
||||
<td><?php echo esc_html($license['date_activated'] ?: __('N/A', 'slm-plus')); ?></td>
|
||||
<td><?php echo esc_html($license['date_expiry'] ?: __('N/A', 'slm-plus')); ?></td>
|
||||
<td><?php echo esc_html($license['max_allowed_domains']); ?></td>
|
||||
<td><?php echo esc_html($license['max_allowed_devices']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
new SLM_Forgot_License();
|
||||
new SLM_List_Licenses_FE();
|
||||
|
|
@ -73,6 +73,7 @@ $slm_helper = new SLM_Helper_Class();
|
|||
|
||||
class SLM_API_Utility
|
||||
{
|
||||
|
||||
/*
|
||||
* The args array can contain the following:
|
||||
* result (success or error)
|
||||
|
@ -189,6 +190,19 @@ class SLM_API_Utility
|
|||
|
||||
class SLM_Utility
|
||||
{
|
||||
public static function get_licenses_by_email($email) {
|
||||
global $wpdb;
|
||||
|
||||
// Query the licenses table for entries matching the email.
|
||||
$table_name = SLM_TBL_LICENSE_KEYS; // Adjust table name if needed.
|
||||
$query = $wpdb->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.
|
||||
*/
|
||||
|
|
84
public/assets/css/slm-blocks.css
Normal file
84
public/assets/css/slm-blocks.css
Normal file
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
97
public/assets/js/slm-blocks.js
Normal file
97
public/assets/js/slm-blocks.js
Normal file
|
@ -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]');
|
||||
},
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/*
|
||||
Plugin Name: SLM Plus
|
||||
Version: 6.3.2
|
||||
Version: 6.3.3
|
||||
Plugin URI: https://github.com/michelve/software-license-manager/
|
||||
Author: Michel Velis
|
||||
Author URI: https://github.com/michelve/
|
||||
|
@ -33,7 +33,7 @@ add_action('plugins_loaded', 'slmplus_load_textdomain');
|
|||
global $wpdb, $slm_debug_logger;
|
||||
|
||||
// Define constants for plugin paths, URLs, and database tables
|
||||
define('SLM_VERSION', '6.3.2');
|
||||
define('SLM_VERSION', '6.3.3');
|
||||
define('SLM_DB_VERSION', '5.8.7');
|
||||
define('SLM_REWRITE_VERSION', '3.1.2');
|
||||
define('SLM_FOLDER', dirname(plugin_basename(__FILE__)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue