Initial
This commit is contained in:
164
assets/css/admin.css
Normal file
164
assets/css/admin.css
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
.lcp-post-type-rules {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 1px solid #ccd0d4;
|
||||||
|
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-type-settings {
|
||||||
|
background: #f9f9f9;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-group label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-group input[type="number"] {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle Switch */
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 50px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #2271b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lcp-rules-container {
|
||||||
|
margin: 15px 0;
|
||||||
|
min-height: 50px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lcp-rule {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lcp-rule.ui-sortable-helper {
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-header .dashicons-menu {
|
||||||
|
cursor: move;
|
||||||
|
color: #666;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-header .delete-rule {
|
||||||
|
margin-left: auto;
|
||||||
|
color: #dc3232;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-header .delete-rule:hover {
|
||||||
|
color: #aa0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-body select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-rule {
|
||||||
|
margin-top: 10px !important;
|
||||||
|
margin-bottom: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#save-rules {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-type-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-top: 15px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-type-actions .button {
|
||||||
|
height: 35px;
|
||||||
|
line-height: 33px;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-post-type-rules {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
171
assets/js/admin.js
Normal file
171
assets/js/admin.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Initialize Sortable for rule containers
|
||||||
|
document.querySelectorAll('.lcp-rules-container').forEach(container => {
|
||||||
|
new Sortable(container, {
|
||||||
|
handle: '.dashicons-menu',
|
||||||
|
animation: 150
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add new rule
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (e.target.matches('.add-rule')) {
|
||||||
|
const postType = e.target.dataset.postType;
|
||||||
|
const template = document.querySelector('.lcp-rule-template').innerHTML;
|
||||||
|
const container = e.target.closest('.lcp-post-type-rules').querySelector('.lcp-rules-container');
|
||||||
|
container.insertAdjacentHTML('beforeend', template);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete rule
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
// Check if the click was on the delete button or its child icon
|
||||||
|
if (e.target.matches('.delete-rule') || e.target.closest('.delete-rule')) {
|
||||||
|
const ruleElement = e.target.closest('.lcp-rule');
|
||||||
|
if (ruleElement) {
|
||||||
|
ruleElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update taxonomy and terms fields visibility
|
||||||
|
document.addEventListener('change', function(e) {
|
||||||
|
if (e.target.matches('.rule-value-type')) {
|
||||||
|
const ruleBody = e.target.closest('.rule-body');
|
||||||
|
if (!ruleBody) return; // Exit if rule-body not found
|
||||||
|
|
||||||
|
const taxonomySelect = ruleBody.querySelector('.rule-taxonomy');
|
||||||
|
const termSelect = ruleBody.querySelector('.rule-term');
|
||||||
|
|
||||||
|
if (!taxonomySelect || !termSelect) return; // Exit if selects not found
|
||||||
|
|
||||||
|
if (e.target.value === 'taxonomy') {
|
||||||
|
taxonomySelect.style.display = 'inline-block';
|
||||||
|
termSelect.style.display = 'inline-block';
|
||||||
|
} else {
|
||||||
|
taxonomySelect.style.display = 'none';
|
||||||
|
termSelect.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update terms when taxonomy changes
|
||||||
|
document.addEventListener('change', function(e) {
|
||||||
|
if (e.target.matches('.rule-taxonomy')) {
|
||||||
|
const termSelect = e.target.nextElementSibling;
|
||||||
|
if (!termSelect) return; // Exit if term select not found
|
||||||
|
|
||||||
|
const taxonomy = e.target.value;
|
||||||
|
|
||||||
|
fetch(lcpPaywall.ajaxurl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
action: 'lcp_get_taxonomy_terms',
|
||||||
|
nonce: lcpPaywall.nonce,
|
||||||
|
taxonomy: taxonomy
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
termSelect.innerHTML = '';
|
||||||
|
data.data.forEach(term => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = term.term_id;
|
||||||
|
option.textContent = term.name;
|
||||||
|
termSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save individual post type rules
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (e.target.matches('.save-post-type-rules')) {
|
||||||
|
const postTypeSection = e.target.closest('.lcp-post-type-rules');
|
||||||
|
const postType = postTypeSection.dataset.postType;
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
[postType]: {
|
||||||
|
settings: {
|
||||||
|
default_lock_status: postTypeSection.querySelector('.default-lock-status').value,
|
||||||
|
is_metered: postTypeSection.querySelector('.is-metered').checked,
|
||||||
|
metered_free_posts: parseInt(postTypeSection.querySelector('.metered-free-posts').value),
|
||||||
|
metered_interval: parseInt(postTypeSection.querySelector('.metered-interval').value)
|
||||||
|
},
|
||||||
|
rules: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
postTypeSection.querySelectorAll('.lcp-rule').forEach(ruleElement => {
|
||||||
|
const rule = {
|
||||||
|
action: ruleElement.querySelector('.rule-action').value,
|
||||||
|
condition: ruleElement.querySelector('.rule-condition').value,
|
||||||
|
operator: ruleElement.querySelector('.rule-operator').value,
|
||||||
|
value_type: ruleElement.querySelector('.rule-value-type').value,
|
||||||
|
taxonomy: ruleElement.querySelector('.rule-taxonomy')?.value || '',
|
||||||
|
term: ruleElement.querySelector('.rule-term')?.value || ''
|
||||||
|
};
|
||||||
|
rules[postType].rules.push(rule);
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch(lcpPaywall.ajaxurl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
action: 'lcp_save_rules',
|
||||||
|
nonce: lcpPaywall.nonce,
|
||||||
|
rules: JSON.stringify(rules)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert(`Rules for ${postType} saved successfully!`);
|
||||||
|
} else {
|
||||||
|
alert(`Failed to save rules for ${postType}. Please try again.`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert(`Failed to save rules for ${postType}. Please try again.`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save settings
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (e.target.matches('#save-settings')) {
|
||||||
|
// Get the content from the WordPress editor
|
||||||
|
const content = wp.editor.getContent('lcp_nag_message');
|
||||||
|
|
||||||
|
fetch(lcpPaywall.ajaxurl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
action: 'lcp_save_settings',
|
||||||
|
nonce: lcpPaywall.nonce,
|
||||||
|
nag_message: content
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert('Settings saved successfully!');
|
||||||
|
} else {
|
||||||
|
alert('Failed to save settings. Please try again.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert('Failed to save settings. Please try again.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
314
includes/class-lcp-paywall-admin.php
Normal file
314
includes/class-lcp-paywall-admin.php
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class LCP_Paywall_Admin {
|
||||||
|
private $rules;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->rules = new LCP_Paywall_Rules();
|
||||||
|
|
||||||
|
add_action('admin_menu', array($this, 'add_menu_pages'));
|
||||||
|
add_action('admin_init', array($this, 'register_settings'));
|
||||||
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
|
||||||
|
add_action('wp_ajax_lcp_save_rules', array($this, 'ajax_save_rules'));
|
||||||
|
add_action('wp_ajax_lcp_get_taxonomy_terms', array($this, 'ajax_get_taxonomy_terms'));
|
||||||
|
add_action('wp_ajax_lcp_save_settings', array($this, 'ajax_save_settings'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_menu_pages() {
|
||||||
|
add_menu_page(
|
||||||
|
'LCP Paywall',
|
||||||
|
'LCP Paywall',
|
||||||
|
'manage_options',
|
||||||
|
'lcp-paywall',
|
||||||
|
array($this, 'render_rules_page'),
|
||||||
|
'dashicons-lock',
|
||||||
|
30
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'lcp-paywall',
|
||||||
|
'Paywall Settings',
|
||||||
|
'Settings',
|
||||||
|
'manage_options',
|
||||||
|
'lcp-paywall-settings',
|
||||||
|
array($this, 'render_settings_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_settings() {
|
||||||
|
register_setting('lcp_paywall_options', 'lcp_paywall_rules');
|
||||||
|
register_setting('lcp_paywall_settings', 'lcp_paywall_settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue_admin_assets($hook) {
|
||||||
|
if (!in_array($hook, array('toplevel_page_lcp-paywall', 'lcp-paywall_page_lcp-paywall-settings'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_enqueue_style('lcp-paywall-admin');
|
||||||
|
|
||||||
|
// Enqueue WordPress editor assets
|
||||||
|
if ($hook === 'lcp-paywall_page_lcp-paywall-settings') {
|
||||||
|
wp_enqueue_editor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue Sortable.js
|
||||||
|
wp_enqueue_script(
|
||||||
|
'sortablejs',
|
||||||
|
'https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js',
|
||||||
|
array(),
|
||||||
|
'1.15.0',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_enqueue_script(
|
||||||
|
'lcp-paywall-admin',
|
||||||
|
LCP_PAYWALL_URL . 'assets/js/admin.js',
|
||||||
|
array('sortablejs'),
|
||||||
|
LCP_PAYWALL_VERSION,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_localize_script('lcp-paywall-admin', 'lcpPaywall', array(
|
||||||
|
'ajaxurl' => admin_url('admin-ajax.php'),
|
||||||
|
'nonce' => wp_create_nonce('lcp_paywall_nonce'),
|
||||||
|
'operators' => $this->rules->get_operators(),
|
||||||
|
'conditions' => $this->rules->get_conditions(),
|
||||||
|
'valueTypes' => $this->rules->get_value_types(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_rules_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_types = get_post_types(array('public' => true), 'objects');
|
||||||
|
$current_rules = $this->rules->get_rules();
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
|
||||||
|
<div id="lcp-paywall-rules-manager">
|
||||||
|
<?php foreach ($post_types as $post_type):
|
||||||
|
$settings = $this->rules->get_default_post_type_settings($post_type->name);
|
||||||
|
?>
|
||||||
|
<div class="lcp-post-type-rules" data-post-type="<?php echo esc_attr($post_type->name); ?>">
|
||||||
|
<h2><?php echo esc_html($post_type->labels->name); ?></h2>
|
||||||
|
|
||||||
|
<div class="post-type-settings">
|
||||||
|
<div class="setting-group">
|
||||||
|
<label>Default Lock Status:</label>
|
||||||
|
<select class="default-lock-status" name="default_lock_status">
|
||||||
|
<option value="locked" <?php selected($settings['default_lock_status'], 'locked'); ?>>Locked</option>
|
||||||
|
<option value="unlocked" <?php selected($settings['default_lock_status'], 'unlocked'); ?>>Unlocked</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-group">
|
||||||
|
<label>Enable Metered Access:</label>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" class="is-metered" name="is_metered" <?php checked($settings['is_metered']); ?>>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-group">
|
||||||
|
<label>Free Posts Count:</label>
|
||||||
|
<input type="number" class="metered-free-posts" name="metered_free_posts"
|
||||||
|
min="0" step="1" value="<?php echo esc_attr($settings['metered_free_posts']); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-group">
|
||||||
|
<label>Metered Interval (days):</label>
|
||||||
|
<input type="number" class="metered-interval" name="metered_interval"
|
||||||
|
min="0" step="1" value="<?php echo esc_attr($settings['metered_interval']); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lcp-rules-container">
|
||||||
|
<?php
|
||||||
|
if (isset($current_rules[$post_type->name]['rules'])) {
|
||||||
|
foreach ($current_rules[$post_type->name]['rules'] as $rule) {
|
||||||
|
$this->render_rule_template($post_type, $rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-type-actions">
|
||||||
|
<button type="button" class="button add-rule" data-post-type="<?php echo esc_attr($post_type->name); ?>">
|
||||||
|
Add Rule
|
||||||
|
</button>
|
||||||
|
<button type="button" class="button button-primary save-post-type-rules" data-post-type="<?php echo esc_attr($post_type->name); ?>">
|
||||||
|
Save <?php echo esc_html($post_type->labels->name); ?> Rules
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<div class="lcp-rule-template" style="display: none;">
|
||||||
|
<?php $this->render_rule_template(null); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_settings_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = get_option('lcp_paywall_settings', array());
|
||||||
|
$nag_message = isset($settings['nag_message']) ? $settings['nag_message'] : '';
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
|
||||||
|
<div id="lcp-paywall-settings">
|
||||||
|
<h2>Paywall Nag Message</h2>
|
||||||
|
<p>Configure the message that will be shown to users when content is locked.</p>
|
||||||
|
|
||||||
|
<div class="nag-message-editor">
|
||||||
|
<?php
|
||||||
|
wp_editor(
|
||||||
|
$nag_message,
|
||||||
|
'lcp_nag_message',
|
||||||
|
array(
|
||||||
|
'textarea_name' => 'lcp_nag_message',
|
||||||
|
'media_buttons' => true,
|
||||||
|
'textarea_rows' => 10,
|
||||||
|
'teeny' => false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="submit">
|
||||||
|
<button type="button" class="button button-primary" id="save-settings">Save Settings</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_rule_template($post_type, $rule = null) {
|
||||||
|
?>
|
||||||
|
<div class="lcp-rule">
|
||||||
|
<div class="rule-header">
|
||||||
|
<span class="dashicons dashicons-menu"></span>
|
||||||
|
<select class="rule-action">
|
||||||
|
<option value="lock" <?php selected(isset($rule['action']) && $rule['action'] === 'lock'); ?>>Lock</option>
|
||||||
|
<option value="unlock" <?php selected(isset($rule['action']) && $rule['action'] === 'unlock'); ?>>Unlock</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button type="button" class="button delete-rule">
|
||||||
|
<span class="dashicons dashicons-trash"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-body">
|
||||||
|
<select class="rule-condition">
|
||||||
|
<option value="user" <?php selected(isset($rule['condition']) && $rule['condition'] === 'user'); ?>>User</option>
|
||||||
|
<option value="post" <?php selected(isset($rule['condition']) && $rule['condition'] === 'post'); ?>>Post</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select class="rule-operator">
|
||||||
|
<option value="=" <?php selected(isset($rule['operator']) && $rule['operator'] === '='); ?>>=</option>
|
||||||
|
<option value="!=" <?php selected(isset($rule['operator']) && $rule['operator'] === '!='); ?>>!=</option>
|
||||||
|
<option value="in" <?php selected(isset($rule['operator']) && $rule['operator'] === 'in'); ?>>in</option>
|
||||||
|
<option value="notin" <?php selected(isset($rule['operator']) && $rule['operator'] === 'notin'); ?>>not in</option>
|
||||||
|
<option value="is" <?php selected(isset($rule['operator']) && $rule['operator'] === 'is'); ?>>is</option>
|
||||||
|
<option value="isnot" <?php selected(isset($rule['operator']) && $rule['operator'] === 'isnot'); ?>>is not</option>
|
||||||
|
<option value="olderthan" <?php selected(isset($rule['operator']) && $rule['operator'] === 'olderthan'); ?>>older than</option>
|
||||||
|
<option value="newerthan" <?php selected(isset($rule['operator']) && $rule['operator'] === 'newerthan'); ?>>newer than</option>
|
||||||
|
<option value="hassub" <?php selected(isset($rule['operator']) && $rule['operator'] === 'hassub'); ?>>has subscription</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select class="rule-value-type">
|
||||||
|
<option value="logged_in" <?php selected(isset($rule['value_type']) && $rule['value_type'] === 'logged_in'); ?>>Logged In</option>
|
||||||
|
<option value="taxonomy" <?php selected(isset($rule['value_type']) && $rule['value_type'] === 'taxonomy'); ?>>Taxonomy</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<?php if ($post_type): ?>
|
||||||
|
<select class="rule-taxonomy" style="display: <?php echo (isset($rule['value_type']) && $rule['value_type'] === 'taxonomy') ? 'inline-block' : 'none'; ?>">
|
||||||
|
<?php foreach ($this->rules->get_taxonomies_for_post_type($post_type->name) as $taxonomy): ?>
|
||||||
|
<option value="<?php echo esc_attr($taxonomy->name); ?>" <?php selected(isset($rule['taxonomy']) && $rule['taxonomy'] === $taxonomy->name); ?>>
|
||||||
|
<?php echo esc_html($taxonomy->label); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select class="rule-term" style="display: <?php echo (isset($rule['value_type']) && $rule['value_type'] === 'taxonomy') ? 'inline-block' : 'none'; ?>">
|
||||||
|
<?php
|
||||||
|
if (isset($rule['taxonomy'])) {
|
||||||
|
foreach ($this->rules->get_terms_for_taxonomy($rule['taxonomy']) as $term): ?>
|
||||||
|
<option value="<?php echo esc_attr($term->term_id); ?>" <?php selected(isset($rule['term']) && $rule['term'] === $term->term_id); ?>>
|
||||||
|
<?php echo esc_html($term->name); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ajax_save_rules() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ajax_referer('lcp_paywall_nonce', 'nonce');
|
||||||
|
|
||||||
|
$rules = isset($_POST['rules']) ? json_decode(stripslashes($_POST['rules']), true) : array();
|
||||||
|
|
||||||
|
if ($this->rules->save_rules($rules)) {
|
||||||
|
wp_send_json_success('Rules saved successfully');
|
||||||
|
} else {
|
||||||
|
wp_send_json_error('Failed to save rules');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ajax_get_taxonomy_terms() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ajax_referer('lcp_paywall_nonce', 'nonce');
|
||||||
|
|
||||||
|
$taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : '';
|
||||||
|
|
||||||
|
if (!$taxonomy) {
|
||||||
|
wp_send_json_error('Invalid taxonomy');
|
||||||
|
}
|
||||||
|
|
||||||
|
$terms = $this->rules->get_terms_for_taxonomy($taxonomy);
|
||||||
|
wp_send_json_success($terms);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ajax_save_settings() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ajax_referer('lcp_paywall_nonce', 'nonce');
|
||||||
|
|
||||||
|
$nag_message = isset($_POST['nag_message']) ? wp_kses_post(stripslashes($_POST['nag_message'])) : '';
|
||||||
|
|
||||||
|
$settings = array(
|
||||||
|
'nag_message' => $nag_message
|
||||||
|
);
|
||||||
|
|
||||||
|
if (update_option('lcp_paywall_settings', $settings)) {
|
||||||
|
wp_send_json_success('Settings saved successfully');
|
||||||
|
} else {
|
||||||
|
wp_send_json_error('Failed to save settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
includes/class-lcp-paywall-rules.php
Normal file
77
includes/class-lcp-paywall-rules.php
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class LCP_Paywall_Rules {
|
||||||
|
private $option_name = 'lcp_paywall_rules';
|
||||||
|
|
||||||
|
public function get_rules() {
|
||||||
|
return get_option($this->option_name, array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_rules_for_post_type($post_type) {
|
||||||
|
$rules = $this->get_rules();
|
||||||
|
return isset($rules[$post_type]) ? $rules[$post_type] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save_rules($rules) {
|
||||||
|
// Ensure settings exist for each post type
|
||||||
|
foreach ($rules as $post_type => $data) {
|
||||||
|
if (!isset($data['settings'])) {
|
||||||
|
$rules[$post_type]['settings'] = $this->get_default_post_type_settings($post_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return update_option($this->option_name, $rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_operators() {
|
||||||
|
return array(
|
||||||
|
'=' => 'Equals',
|
||||||
|
'!=' => 'Not Equals',
|
||||||
|
'in' => 'In',
|
||||||
|
'notin' => 'Not In',
|
||||||
|
'is' => 'Is',
|
||||||
|
'isnot' => 'Is Not',
|
||||||
|
'olderthan' => 'Older Than',
|
||||||
|
'newerthan' => 'Newer Than',
|
||||||
|
'hassub' => 'Has Subscription'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_conditions() {
|
||||||
|
return array(
|
||||||
|
'user' => 'User',
|
||||||
|
'post' => 'Post'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_value_types() {
|
||||||
|
return array(
|
||||||
|
'logged_in' => 'Logged In',
|
||||||
|
'taxonomy' => 'Taxonomy'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_taxonomies_for_post_type($post_type) {
|
||||||
|
return get_object_taxonomies($post_type, 'objects');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_terms_for_taxonomy($taxonomy) {
|
||||||
|
$terms = get_terms(array(
|
||||||
|
'taxonomy' => $taxonomy,
|
||||||
|
'hide_empty' => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
return is_wp_error($terms) ? array() : $terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_default_post_type_settings($post_type) {
|
||||||
|
$rules = $this->get_rules();
|
||||||
|
return isset($rules[$post_type]['settings']) ? $rules[$post_type]['settings'] : array(
|
||||||
|
'default_lock_status' => 'locked',
|
||||||
|
'is_metered' => false,
|
||||||
|
'metered_free_posts' => 0,
|
||||||
|
'metered_interval' => 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
141
includes/class-lcp-paywall.php
Normal file
141
includes/class-lcp-paywall.php
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class LCP_Paywall {
|
||||||
|
private $rules;
|
||||||
|
private $admin;
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
$this->rules = new LCP_Paywall_Rules();
|
||||||
|
|
||||||
|
if (is_admin()) {
|
||||||
|
$this->admin = new LCP_Paywall_Admin();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('init', array($this, 'init_hooks'));
|
||||||
|
add_filter('the_content', array($this, 'maybe_restrict_content'), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init_hooks() {
|
||||||
|
// Register scripts and styles
|
||||||
|
add_action('admin_enqueue_scripts', array($this, 'register_admin_assets'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_admin_assets() {
|
||||||
|
wp_register_style(
|
||||||
|
'lcp-paywall-admin',
|
||||||
|
LCP_PAYWALL_URL . 'assets/css/admin.css',
|
||||||
|
array(),
|
||||||
|
LCP_PAYWALL_VERSION
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_register_script(
|
||||||
|
'lcp-paywall-admin',
|
||||||
|
LCP_PAYWALL_URL . 'assets/js/admin.js',
|
||||||
|
array('jquery', 'jquery-ui-sortable'),
|
||||||
|
LCP_PAYWALL_VERSION,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybe_restrict_content($content) {
|
||||||
|
if (!is_singular()) return $content;
|
||||||
|
|
||||||
|
$post_type = get_post_type();
|
||||||
|
$post_id = get_the_ID();
|
||||||
|
|
||||||
|
if ($this->should_restrict_access($post_type, $post_id)) {
|
||||||
|
return $this->get_nag_message();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function should_restrict_access($post_type, $post_id) {
|
||||||
|
$post_type_rules = $this->rules->get_rules_for_post_type($post_type);
|
||||||
|
|
||||||
|
// Get default lock status
|
||||||
|
$default_lock_status = isset($post_type_rules['settings']['default_lock_status']) ?
|
||||||
|
$post_type_rules['settings']['default_lock_status'] === 'locked' : true;
|
||||||
|
|
||||||
|
// If no rules exist, use default lock status
|
||||||
|
if (empty($post_type_rules['rules'])) {
|
||||||
|
return $default_lock_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each rule in order until one evaluates to true
|
||||||
|
foreach ($post_type_rules['rules'] as $rule) {
|
||||||
|
if ($this->evaluate_rule($rule, $post_id)) {
|
||||||
|
// First matching rule determines the lock status
|
||||||
|
return $rule['action'] === 'lock';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no rules match, use default lock status
|
||||||
|
return $default_lock_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluate_rule($rule, $post_id) {
|
||||||
|
if ($rule['condition'] === 'user') {
|
||||||
|
return $this->evaluate_user_rule($rule);
|
||||||
|
} else if ($rule['condition'] === 'post') {
|
||||||
|
return $this->evaluate_post_rule($rule, $post_id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluate_user_rule($rule) {
|
||||||
|
if ($rule['value_type'] === 'logged_in') {
|
||||||
|
$is_logged_in = is_user_logged_in();
|
||||||
|
|
||||||
|
switch ($rule['operator']) {
|
||||||
|
case '=':
|
||||||
|
case 'is':
|
||||||
|
return $is_logged_in;
|
||||||
|
case '!=':
|
||||||
|
case 'isnot':
|
||||||
|
return !$is_logged_in;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluate_post_rule($rule, $post_id) {
|
||||||
|
if ($rule['value_type'] === 'taxonomy' && !empty($rule['taxonomy']) && !empty($rule['term'])) {
|
||||||
|
$terms = wp_get_post_terms($post_id, $rule['taxonomy'], array('fields' => 'ids'));
|
||||||
|
|
||||||
|
if (is_wp_error($terms)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($rule['operator']) {
|
||||||
|
case '=':
|
||||||
|
case 'in':
|
||||||
|
return in_array((int)$rule['term'], $terms);
|
||||||
|
case '!=':
|
||||||
|
case 'notin':
|
||||||
|
return !in_array((int)$rule['term'], $terms);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_nag_message() {
|
||||||
|
$settings = get_option('lcp_paywall_settings', array());
|
||||||
|
$message = isset($settings['nag_message']) ? $settings['nag_message'] : '';
|
||||||
|
|
||||||
|
if (empty($message)) {
|
||||||
|
$message = '<div class="lcp-paywall-nag">
|
||||||
|
<h3>This content is locked</h3>
|
||||||
|
<p>Please upgrade your subscription to access this content.</p>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply_filters('lcp_paywall_nag_message', $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
lcp-paywall.php
Normal file
28
lcp-paywall.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: LCP Paywall
|
||||||
|
* Plugin URI:
|
||||||
|
* Description: A flexible paywall plugin with firewall-like rule management for WordPress content
|
||||||
|
* Version: 1.0.0
|
||||||
|
* Author:
|
||||||
|
* Text Domain: lcp-paywall
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
define('LCP_PAYWALL_PATH', plugin_dir_path(__FILE__));
|
||||||
|
define('LCP_PAYWALL_URL', plugin_dir_url(__FILE__));
|
||||||
|
define('LCP_PAYWALL_VERSION', '1.0.0');
|
||||||
|
|
||||||
|
// Include core files
|
||||||
|
require_once(LCP_PAYWALL_PATH . 'includes/class-lcp-paywall.php');
|
||||||
|
require_once(LCP_PAYWALL_PATH . 'includes/class-lcp-paywall-rules.php');
|
||||||
|
require_once(LCP_PAYWALL_PATH . 'includes/class-lcp-paywall-admin.php');
|
||||||
|
|
||||||
|
// Initialize the plugin
|
||||||
|
function lcp_paywall_init() {
|
||||||
|
global $lcp_paywall;
|
||||||
|
$lcp_paywall = new LCP_Paywall();
|
||||||
|
$lcp_paywall->init();
|
||||||
|
}
|
||||||
|
add_action('plugins_loaded', 'lcp_paywall_init');
|
||||||
Reference in New Issue
Block a user