Initial
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
node_modules
|
||||||
132
api.php
Normal file
132
api.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
function get_all_user_roles() {
|
||||||
|
global $wp_roles;
|
||||||
|
|
||||||
|
if (!isset($wp_roles)) {
|
||||||
|
$wp_roles = new WP_Roles();
|
||||||
|
}
|
||||||
|
|
||||||
|
$roles = $wp_roles->roles;
|
||||||
|
$role_names = [];
|
||||||
|
|
||||||
|
foreach ($roles as $role_key => $role_data) {
|
||||||
|
$role_names[] = [
|
||||||
|
'key' => $role_key,
|
||||||
|
'name' => $role_data['name'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response($role_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_admin_user() {
|
||||||
|
return true;
|
||||||
|
/* return current_user_can('manage_options'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
function register_custom_user_roles_endpoint() {
|
||||||
|
register_rest_route('lcp-paywall/v1', '/user-roles', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => 'get_all_user_roles',
|
||||||
|
'permission_callback' => 'is_admin_user',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('rest_api_init', 'register_custom_user_roles_endpoint');
|
||||||
|
|
||||||
|
|
||||||
|
/* GET TAXONOMIES */
|
||||||
|
function get_taxonomies_by_post_type( $request ) {
|
||||||
|
$post_type = $request->get_param('post_type');
|
||||||
|
|
||||||
|
if (empty($post_type)) {
|
||||||
|
return new WP_Error('missing_post_type', 'Post type parameter is required', ['status' => 400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get taxonomies associated with the post type
|
||||||
|
$taxonomies = get_object_taxonomies($post_type, 'names'); // Use 'names' instead of 'objects' for simple list
|
||||||
|
|
||||||
|
// If no taxonomies found, return an error message
|
||||||
|
if (empty($taxonomies)) {
|
||||||
|
return new WP_Error('no_taxonomies', 'No taxonomies found for the given post type', ['status' => 404]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch taxonomy objects for the specific taxonomies
|
||||||
|
$taxonomy_objects = [];
|
||||||
|
foreach ($taxonomies as $taxonomy) {
|
||||||
|
$taxonomy_objects[] = get_taxonomy($taxonomy); // Retrieve taxonomy object for each taxonomy
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response($taxonomy_objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function register_taxonomies_endpoint() {
|
||||||
|
register_rest_route('lcp-paywall/v1', '/taxonomies', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => 'get_taxonomies_by_post_type',
|
||||||
|
'args' => [
|
||||||
|
'post_type' => [
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('rest_api_init', 'register_taxonomies_endpoint');
|
||||||
|
|
||||||
|
function get_terms_by_taxonomy_slug( $request ) {
|
||||||
|
// Get the taxonomy slug from the request parameters
|
||||||
|
$taxonomy_slug = $request->get_param( 'taxonomy' );
|
||||||
|
|
||||||
|
// Check if the taxonomy slug is provided
|
||||||
|
if ( empty( $taxonomy_slug ) ) {
|
||||||
|
return new WP_Error( 'missing_taxonomy', 'Taxonomy slug parameter is required', array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the taxonomy exists
|
||||||
|
if ( ! taxonomy_exists( $taxonomy_slug ) ) {
|
||||||
|
return new WP_Error( 'invalid_taxonomy', 'The provided taxonomy does not exist', array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the terms for the taxonomy
|
||||||
|
$terms = get_terms( array(
|
||||||
|
'taxonomy' => $taxonomy_slug,
|
||||||
|
'hide_empty' => false, // Set to true to only get terms that have posts assigned
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Check if terms were found
|
||||||
|
if ( is_wp_error( $terms ) ) {
|
||||||
|
return $terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $terms ) ) {
|
||||||
|
return new WP_Error( 'no_terms', 'No terms found for the given taxonomy', array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the terms as a response
|
||||||
|
return rest_ensure_response( $terms );
|
||||||
|
}
|
||||||
|
|
||||||
|
function register_custom_taxonomy_terms_endpoint() {
|
||||||
|
// Register the custom REST route
|
||||||
|
register_rest_route( 'lcp-paywall/v1','/terms', array(
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => 'get_terms_by_taxonomy_slug',
|
||||||
|
'args' => array(
|
||||||
|
'taxonomy' => array(
|
||||||
|
'required' => true,
|
||||||
|
'validate_callback' => function( $param, $request, $key ) {
|
||||||
|
// Validate that the taxonomy exists
|
||||||
|
return taxonomy_exists( $param );
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'rest_api_init', 'register_custom_taxonomy_terms_endpoint' );
|
||||||
1
build/index.asset.php
Normal file
1
build/index.asset.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php return array('dependencies' => array('react', 'wp-components', 'wp-element'), 'version' => '33ab959b55f1e87d13f0');
|
||||||
1
build/index.js
Normal file
1
build/index.js
Normal file
File diff suppressed because one or more lines are too long
149
lcp-paywall.php
Normal file
149
lcp-paywall.php
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
Plugin Name: LCP Paywall
|
||||||
|
Plugin URI: https://localcontentpro.com/paywall
|
||||||
|
Description: Flexible and customizable paywall solution for WordPress.
|
||||||
|
Author: Local Content Pro
|
||||||
|
Version: 0.0.1
|
||||||
|
Author URI: https://localcontentpro.com/
|
||||||
|
Tags: paywall, subscriptions, metered, membership, pay wall, content monetization, metered access, metered pay wall, paid content
|
||||||
|
Text Domain: lcp
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// If this file is called directly, abort.
|
||||||
|
if (!defined('WPINC')) {
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once plugin_dir_path(__FILE__) . 'api.php';
|
||||||
|
// Add menu page to the admin dashboard
|
||||||
|
add_action('admin_menu', 'lcp_paywall_add_admin_menu');
|
||||||
|
add_action('admin_enqueue_scripts', 'lcp_paywall_enqueue_admin_scripts');
|
||||||
|
|
||||||
|
function lcp_paywall_add_admin_menu() {
|
||||||
|
add_menu_page(
|
||||||
|
'LCP Paywall', // Page title
|
||||||
|
'LCP Paywall', // Menu title
|
||||||
|
'manage_options', // Capability required
|
||||||
|
'lcp-paywall', // Menu slug
|
||||||
|
'lcp_paywall_admin_page', // Function to output content
|
||||||
|
'dashicons-lock', // Icon (lock icon)
|
||||||
|
30 // Position in menu
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lcp_paywall_admin_page() {
|
||||||
|
// Check user capabilities
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
<div id="lcp-paywall-app-container">
|
||||||
|
<!-- React app will render here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function lcp_paywall_enqueue_admin_scripts($hook) {
|
||||||
|
// Only load on our admin page
|
||||||
|
if ('toplevel_page_lcp-paywall' !== $hook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue the built js file
|
||||||
|
wp_enqueue_script(
|
||||||
|
'lcp-paywall-admin',
|
||||||
|
plugins_url('build/index.js', __FILE__),
|
||||||
|
['wp-element', 'wp-components'], // Added wp-components dependency
|
||||||
|
filemtime(plugin_dir_path(__FILE__) . 'build/index.js'),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enqueue WordPress components styles
|
||||||
|
wp_enqueue_style('wp-components');
|
||||||
|
|
||||||
|
// Get all registered post types
|
||||||
|
$post_types = get_post_types(['public' => true], 'objects');
|
||||||
|
$post_types_data = [];
|
||||||
|
|
||||||
|
foreach ($post_types as $post_type) {
|
||||||
|
$post_types_data[] = [
|
||||||
|
'name' => $post_type->name,
|
||||||
|
'label' => $post_type->label,
|
||||||
|
'singular_label' => $post_type->labels->singular_name
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass data to JavaScript
|
||||||
|
wp_localize_script(
|
||||||
|
'lcp-paywall-admin',
|
||||||
|
'lcpPaywallData',
|
||||||
|
[
|
||||||
|
'postTypes' => $post_types_data,
|
||||||
|
'nonce' => wp_create_nonce('lcp_paywall_nonce'),
|
||||||
|
'ajaxUrl' => admin_url('admin-ajax.php')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add some basic styles
|
||||||
|
wp_add_inline_style(
|
||||||
|
'wp-admin',
|
||||||
|
'
|
||||||
|
.lcp-paywall-post-type {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccd0d4;
|
||||||
|
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||||
|
}
|
||||||
|
.lcp-paywall-post-type.is-over {
|
||||||
|
background: #f0f0f1;
|
||||||
|
}
|
||||||
|
.lcp-paywall-draggable-item {
|
||||||
|
padding: 15px;
|
||||||
|
margin: 5px 0;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px dashed #ccd0d4;
|
||||||
|
cursor: move;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
.lcp-paywall-draggable-item:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
.lcp-paywall-draggable-item.is-dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
box-shadow: 0 5px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.lcp-paywall-rule-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.lcp-paywall-rule-controls .components-select-control {
|
||||||
|
margin-bottom: 0;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
.lcp-paywall-rule-controls .components-select-control__input {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.lcp-paywall-add-rule {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-top: 15px;
|
||||||
|
border-top: 1px solid #f0f0f1;
|
||||||
|
}
|
||||||
|
.lcp-paywall-add-rule-button.is-secondary {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.lcp-paywall-rules-container {
|
||||||
|
min-height: 50px;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
19903
package-lock.json
generated
Normal file
19903
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "lcp-paywall",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "LCP Paywall WordPress Plugin",
|
||||||
|
"scripts": {
|
||||||
|
"build": "wp-scripts build",
|
||||||
|
"start": "wp-scripts start",
|
||||||
|
"check-engines": "wp-scripts check-engines"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@wordpress/scripts": "^26.0.0",
|
||||||
|
"@wordpress/components": "^25.0.0",
|
||||||
|
"@wordpress/element": "^5.0.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-dnd": "^16.0.1",
|
||||||
|
"react-dnd-html5-backend": "^16.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
237
src/components/DraggableItem.js
Normal file
237
src/components/DraggableItem.js
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
import { useDrag, useDrop } from 'react-dnd';
|
||||||
|
import { DateTimePicker,SelectControl, TextControl, __experimentalNumberControl as NumberControl } from '@wordpress/components';
|
||||||
|
import { useState, useEffect, useCallback } from '@wordpress/element';
|
||||||
|
|
||||||
|
const DraggableItem = ({ id, index, moveItem, postType, children }) => {
|
||||||
|
// Drag-and-drop hooks with memoization
|
||||||
|
const [{ isDragging }, drag] = useDrag(
|
||||||
|
useCallback(() => ({
|
||||||
|
type: 'rule',
|
||||||
|
item: { id, index },
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: !!monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
}), [id, index])
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, drop] = useDrop(
|
||||||
|
useCallback(() => ({
|
||||||
|
accept: 'rule',
|
||||||
|
hover: (item) => {
|
||||||
|
if (item.index !== index) {
|
||||||
|
moveItem(item.index, index);
|
||||||
|
item.index = index; // Keep track of the moved index
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}), [index, moveItem])
|
||||||
|
);
|
||||||
|
|
||||||
|
// State management
|
||||||
|
const [ruleType, setRuleType] = useState('post');
|
||||||
|
const [lockType, setLockType] = useState('has_subscription');
|
||||||
|
const [operator, setOperator] = useState('=');
|
||||||
|
const [metaKey, setMetaKey] = useState('');
|
||||||
|
const [roles, setRoles] = useState([]); // State to store user roles
|
||||||
|
const [taxonomies, setTaxonomies] = useState([]); // State to store taxonomies
|
||||||
|
const [terms, setTerms] = useState([]); // State to store terms of a selected taxonomy
|
||||||
|
const [postContentLengthAmount, setPostContentLengthAmount] = useState(0);
|
||||||
|
const [postContentLengthUnit, setPostContentLengthUnit] = useState('characters');
|
||||||
|
const [postContentLengthOperator, setPostContentLengthOperator] = useState('=');
|
||||||
|
const [postDate,setPostDate] = useState('')
|
||||||
|
const [postDateOperator, setPostDateOperator] = useState('=');
|
||||||
|
const [metaKeyOperator, setMetaKeyOperator] = useState('=');
|
||||||
|
const [metaValue, setMetaValue] = useState('');
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch user roles from the WordPress REST API
|
||||||
|
useEffect(() => {
|
||||||
|
if (lockType === 'has_role') {
|
||||||
|
fetch('/localcontentpro/wp-json/lcp-paywall/v1/user-roles') // Your custom REST API endpoint
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
setRoles(data.map(role => ({ label: role.name, value: role.key })));
|
||||||
|
})
|
||||||
|
.catch((error) => console.error('Error fetching roles:', error));
|
||||||
|
}
|
||||||
|
}, [lockType]); // Fetch only when lockType is 'has_role'
|
||||||
|
|
||||||
|
// Fetch taxonomies for the current post type
|
||||||
|
useEffect(() => {
|
||||||
|
if ((lockType === 'in_term' || lockType === 'not_in_term') && postType) {
|
||||||
|
fetch(`/localcontentpro/wp-json/lcp-paywall/v1/taxonomies?post_type=${postType}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const taxonomyOptions = Object.keys(data).map(taxonomy => ({
|
||||||
|
label: data[taxonomy].name,
|
||||||
|
value: taxonomy,
|
||||||
|
}));
|
||||||
|
setTaxonomies(taxonomyOptions);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching taxonomies:', error));
|
||||||
|
}
|
||||||
|
}, [lockType, postType]); // Fetch taxonomies when lockType is 'in_term' and postType changes
|
||||||
|
|
||||||
|
// Fetch terms when lockType is in_term or not_in_term and a taxonomy is selected
|
||||||
|
useEffect(() => {
|
||||||
|
if ((lockType === 'in_term' || lockType === 'not_in_term') && metaKey) {
|
||||||
|
fetch(`/localcontentpro/wp-json/lcp-paywall/v1/terms?taxonomy=${metaKey}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const termOptions = data.map(term => ({
|
||||||
|
label: term.name,
|
||||||
|
value: term.id,
|
||||||
|
}));
|
||||||
|
setTerms(termOptions);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching terms:', error));
|
||||||
|
}
|
||||||
|
}, [lockType, metaKey]); // Fetch terms when lockType is 'in_term' or 'not_in_term' and metaKey (taxonomy) is selected
|
||||||
|
|
||||||
|
const postLockTypeOptions = [
|
||||||
|
{ value: 'post_content_length', label: 'Post Content Length' },
|
||||||
|
{ value: 'post_in', label: 'Post In' },
|
||||||
|
{value: 'post_not_in', label: 'Post Not In' },
|
||||||
|
{ value: 'meta_value', label: 'Meta Value' },
|
||||||
|
{ value: 'in_term', label: 'In Term' },
|
||||||
|
{ value: 'not_in_term', label: 'Not In Term' },
|
||||||
|
{ value: 'older_than', label: 'Older Than' },
|
||||||
|
{ value: 'newer_than', label: 'Newer Than' },
|
||||||
|
{ value: 'author_in', label: 'Author In' },
|
||||||
|
{ value: 'author_not_in', label: 'Author Not In' },
|
||||||
|
{ value: 'post_date', label: 'Post Date' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const userLockTypeOptions = [
|
||||||
|
{ label: 'Has Subscription', value: 'has_subscription' },
|
||||||
|
{ label: 'Is Logged In', value: 'is_logged_in' },
|
||||||
|
{ label: 'Role In', value: 'role_in' },
|
||||||
|
{ label: 'Role Not In', value: 'role_not_in' },
|
||||||
|
{ label: 'Meta Value', value: 'meta_value' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const userDeviceLockTypeOptions = [
|
||||||
|
{ label: 'Browser In', value: 'browser_in' },
|
||||||
|
{ label: 'Browser Not In', value: 'browser_not_in' },
|
||||||
|
{ label: 'Operating System In', value: 'os_in' },
|
||||||
|
{ label: 'Operating System Not In', value: 'os_not_in' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const comparisonOperators = [
|
||||||
|
{ label: '=', value: '=' },
|
||||||
|
{ label: '!=', value: '!=' },
|
||||||
|
{ label: '>', value: '>' },
|
||||||
|
{ label: '<', value: '<' },
|
||||||
|
{ label: '>=', value: '>=' },
|
||||||
|
{ label: '<=', value: '<=' },
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={(node) => drag(drop(node))}
|
||||||
|
className={`lcp-paywall-draggable-item ${isDragging ? 'is-dragging' : ''}`}
|
||||||
|
style={{ opacity: isDragging ? 0.5 : 1 }}
|
||||||
|
>
|
||||||
|
<div className="lcp-paywall-rule-controls">
|
||||||
|
<SelectControl
|
||||||
|
value={ruleType}
|
||||||
|
options={[
|
||||||
|
{ label: 'Current Post', value: 'post' },
|
||||||
|
{ label: 'Current User', value: 'user' },
|
||||||
|
{ label: 'Current User Device', value: 'user_device' },
|
||||||
|
]}
|
||||||
|
onChange={setRuleType}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectControl
|
||||||
|
value={lockType}
|
||||||
|
options={
|
||||||
|
ruleType === 'post' ? postLockTypeOptions : ruleType === 'user' ? userLockTypeOptions : userDeviceLockTypeOptions
|
||||||
|
}
|
||||||
|
onChange={setLockType}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{lockType === 'meta_value' && (
|
||||||
|
<>
|
||||||
|
<TextControl
|
||||||
|
value={metaKey}
|
||||||
|
onChange={setMetaKey}
|
||||||
|
placeholder="Meta Key"
|
||||||
|
/>
|
||||||
|
<SelectControl
|
||||||
|
value={metaKeyOperator}
|
||||||
|
options={comparisonOperators}
|
||||||
|
onChange={setMetaKeyOperator}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<TextControl
|
||||||
|
value={metaValue}
|
||||||
|
onChange={setMetaValue}
|
||||||
|
placeholder="Meta Value"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{lockType === 'has_role' && (
|
||||||
|
<SelectControl
|
||||||
|
value={metaKey}
|
||||||
|
options={roles}
|
||||||
|
onChange={setMetaKey}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(lockType === 'in_term' || lockType === 'not_in_term') && (
|
||||||
|
<>
|
||||||
|
<SelectControl
|
||||||
|
value={metaKey}
|
||||||
|
options={taxonomies}
|
||||||
|
onChange={setMetaKey}
|
||||||
|
/>
|
||||||
|
<SelectControl
|
||||||
|
value={metaKey}
|
||||||
|
options={terms}
|
||||||
|
onChange={setMetaKey}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{lockType === 'post_content_length' && (
|
||||||
|
<>
|
||||||
|
<SelectControl
|
||||||
|
value={postContentLengthOperator}
|
||||||
|
options={comparisonOperators}
|
||||||
|
onChange={setPostContentLengthOperator}
|
||||||
|
/>
|
||||||
|
<NumberControl
|
||||||
|
value={postContentLengthAmount}
|
||||||
|
onChange={setPostContentLengthAmount}
|
||||||
|
/>
|
||||||
|
<SelectControl
|
||||||
|
value={postContentLengthUnit}
|
||||||
|
options={[
|
||||||
|
{ label: 'Characters', value: 'characters' },
|
||||||
|
{ label: 'Words', value: 'words' } ]}
|
||||||
|
onChange={setPostContentLengthUnit}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{lockType === 'post_date' && (
|
||||||
|
<>
|
||||||
|
<SelectControl
|
||||||
|
value={postDateOperator}
|
||||||
|
options={comparisonOperators}
|
||||||
|
onChange={setPostDateOperator}
|
||||||
|
/>
|
||||||
|
<DateTimePicker
|
||||||
|
value={postDate}
|
||||||
|
onChange={setPostDate}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DraggableItem;
|
||||||
120
src/components/PostTypeContainer.js
Normal file
120
src/components/PostTypeContainer.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { useDrop } from 'react-dnd';
|
||||||
|
import { Button, ToggleControl, __experimentalNumberControl as NumberControl, SelectControl } from '@wordpress/components';
|
||||||
|
import { useState, useEffect, useCallback } from '@wordpress/element';
|
||||||
|
import DraggableItem from './DraggableItem';
|
||||||
|
|
||||||
|
const PostTypeContainer = ({ postType }) => {
|
||||||
|
const [rules, setRules] = useState([{ id: Date.now() }]);
|
||||||
|
const [lockDefault, setLockDefault] = useState(false);
|
||||||
|
const [enableMeter, setEnableMeter] = useState(false);
|
||||||
|
const [meterAmount, setMeterAmount] = useState(0);
|
||||||
|
const [meterDuration, setMeterDuration] = useState(1);
|
||||||
|
const [meterCookieUnits, setMeterCookieUnits] = useState('hour');
|
||||||
|
const [totalSeconds, setTotalSeconds] = useState(3600);
|
||||||
|
|
||||||
|
const [{ isOver }, drop] = useDrop(() => ({
|
||||||
|
accept: 'rule',
|
||||||
|
drop: () => ({ postType: postType.name }),
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isOver: !!monitor.isOver(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const moveItem = useCallback((dragIndex, hoverIndex) => {
|
||||||
|
setRules((prevRules) => {
|
||||||
|
const newRules = [...prevRules];
|
||||||
|
const dragItem = newRules[dragIndex];
|
||||||
|
|
||||||
|
// Remove the dragged item
|
||||||
|
newRules.splice(dragIndex, 1);
|
||||||
|
// Insert it at the new position
|
||||||
|
newRules.splice(hoverIndex, 0, dragItem);
|
||||||
|
|
||||||
|
return newRules;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addRule = () => {
|
||||||
|
setRules([...rules, { id: Date.now() }]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateTotalSeconds = (duration, unit) => {
|
||||||
|
const unitToSeconds = {
|
||||||
|
second: 1,
|
||||||
|
minute: 60,
|
||||||
|
hour: 3600,
|
||||||
|
day: 86400,
|
||||||
|
week: 604800,
|
||||||
|
};
|
||||||
|
return duration * (unitToSeconds[unit] || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update total seconds whenever duration or unit changes
|
||||||
|
useEffect(() => {
|
||||||
|
setTotalSeconds(calculateTotalSeconds(meterDuration, meterCookieUnits));
|
||||||
|
}, [meterDuration, meterCookieUnits]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`lcp-paywall-post-type ${isOver ? 'is-over' : ''}`} ref={drop}>
|
||||||
|
<h3>{postType.label}</h3>
|
||||||
|
<ToggleControl
|
||||||
|
label="Lock Posts By Default"
|
||||||
|
checked={lockDefault}
|
||||||
|
onChange={setLockDefault}
|
||||||
|
/>
|
||||||
|
<ToggleControl
|
||||||
|
label="Enable Meter"
|
||||||
|
checked={enableMeter}
|
||||||
|
onChange={setEnableMeter}
|
||||||
|
/>
|
||||||
|
{enableMeter && (
|
||||||
|
<>
|
||||||
|
<NumberControl
|
||||||
|
label="Meter Amount"
|
||||||
|
value={meterAmount}
|
||||||
|
onChange={setMeterAmount}
|
||||||
|
/>
|
||||||
|
<NumberControl
|
||||||
|
label="Meter Cookie Duration"
|
||||||
|
value={meterDuration}
|
||||||
|
onChange={setMeterDuration}
|
||||||
|
/>
|
||||||
|
<SelectControl
|
||||||
|
label="Meter Cookie Units"
|
||||||
|
value={meterCookieUnits}
|
||||||
|
options={[
|
||||||
|
{ label: 'Minutes', value: 'minute' },
|
||||||
|
{ label: 'Hours', value: 'hour' },
|
||||||
|
{ label: 'Days', value: 'day' },
|
||||||
|
{ label: 'Weeks', value: 'week' },
|
||||||
|
]}
|
||||||
|
onChange={setMeterCookieUnits}
|
||||||
|
/>
|
||||||
|
<p><strong>Total Seconds:</strong> {totalSeconds}</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className="lcp-paywall-rules-container">
|
||||||
|
{rules.map((rule, index) => (
|
||||||
|
<DraggableItem
|
||||||
|
key={rule.id}
|
||||||
|
id={rule.id}
|
||||||
|
index={index}
|
||||||
|
moveItem={moveItem}
|
||||||
|
postType={postType.name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="lcp-paywall-add-rule">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={addRule}
|
||||||
|
className="lcp-paywall-add-rule-button"
|
||||||
|
>
|
||||||
|
Add Rule
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PostTypeContainer;
|
||||||
24
src/index.js
Normal file
24
src/index.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { render } from '@wordpress/element';
|
||||||
|
import { DndProvider } from 'react-dnd';
|
||||||
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
import PostTypeContainer from './components/PostTypeContainer';
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<DndProvider backend={HTML5Backend}>
|
||||||
|
<div id="lcp-paywall-app">
|
||||||
|
{window.lcpPaywallData.postTypes.map(postType => (
|
||||||
|
<PostTypeContainer key={postType.name} postType={postType} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</DndProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait for DOM to be ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const container = document.getElementById('lcp-paywall-app-container');
|
||||||
|
if (container) {
|
||||||
|
render(<App />, container);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user