Changeset 3633440
- Timestamp:
- 08/03/2026 08:36:32 PM (3 months ago)
- Location:
- subscription-system
- Files:
-
- 18 edited
- 20 copied
-
tags/1.0.20 (copied) (copied from subscription-system/trunk )
-
tags/1.0.20/README.txt (copied) (copied from subscription-system/trunk/README.txt ) (3 diffs)
-
tags/1.0.20/admin/class-subs-sys-admin.php (copied) (copied from subscription-system/trunk/admin/class-subs-sys-admin.php ) (5 diffs)
-
tags/1.0.20/admin/partials/content-plans-display.php (copied) (copied from subscription-system/trunk/admin/partials/content-plans-display.php )
-
tags/1.0.20/admin/partials/dashboard-display.php (modified) (1 diff)
-
tags/1.0.20/admin/partials/help-support-display.php (copied) (copied from subscription-system/trunk/admin/partials/help-support-display.php )
-
tags/1.0.20/admin/partials/interest-subscribers-display.php (copied) (copied from subscription-system/trunk/admin/partials/interest-subscribers-display.php )
-
tags/1.0.20/admin/partials/plan-categories-display.php (modified) (3 diffs)
-
tags/1.0.20/admin/partials/plans-display.php (copied) (copied from subscription-system/trunk/admin/partials/plans-display.php ) (2 diffs)
-
tags/1.0.20/admin/partials/settings-display.php (copied) (copied from subscription-system/trunk/admin/partials/settings-display.php )
-
tags/1.0.20/admin/partials/subscribe-modal-settings-display.php (copied) (copied from subscription-system/trunk/admin/partials/subscribe-modal-settings-display.php ) (1 diff)
-
tags/1.0.20/admin/partials/subscription-system-admin-add.php (modified) (1 diff)
-
tags/1.0.20/admin/partials/work-items-display.php (modified) (1 diff)
-
tags/1.0.20/admin/partials/work-logs-display.php (modified) (1 diff)
-
tags/1.0.20/includes/class-subs-sys-ajax.php (copied) (copied from subscription-system/trunk/includes/class-subs-sys-ajax.php )
-
tags/1.0.20/includes/class-subs-sys-db-updater.php (copied) (copied from subscription-system/trunk/includes/class-subs-sys-db-updater.php )
-
tags/1.0.20/includes/class-subs-sys-interest-subscription.php (copied) (copied from subscription-system/trunk/includes/class-subs-sys-interest-subscription.php )
-
tags/1.0.20/includes/class-subs-sys-shortcodes.php (copied) (copied from subscription-system/trunk/includes/class-subs-sys-shortcodes.php )
-
tags/1.0.20/includes/class-subs-sys.php (copied) (copied from subscription-system/trunk/includes/class-subs-sys.php )
-
tags/1.0.20/public/class-subs-sys-public.php (copied) (copied from subscription-system/trunk/public/class-subs-sys-public.php )
-
tags/1.0.20/public/css/interest-subscribe.css (copied) (copied from subscription-system/trunk/public/css/interest-subscribe.css ) (8 diffs)
-
tags/1.0.20/public/js/interest-subscribe.js (copied) (copied from subscription-system/trunk/public/js/interest-subscribe.js ) (4 diffs)
-
tags/1.0.20/public/partials/interest-subscribe-modal.php (copied) (copied from subscription-system/trunk/public/partials/interest-subscribe-modal.php ) (6 diffs)
-
tags/1.0.20/subscription-system.php (copied) (copied from subscription-system/trunk/subscription-system.php ) (3 diffs)
-
tags/1.0.20/vendor/composer/autoload_psr4.php (copied) (copied from subscription-system/trunk/vendor/composer/autoload_psr4.php )
-
trunk/README.txt (modified) (3 diffs)
-
trunk/admin/class-subs-sys-admin.php (modified) (5 diffs)
-
trunk/admin/partials/dashboard-display.php (modified) (1 diff)
-
trunk/admin/partials/plan-categories-display.php (modified) (3 diffs)
-
trunk/admin/partials/plans-display.php (modified) (2 diffs)
-
trunk/admin/partials/subscribe-modal-settings-display.php (modified) (1 diff)
-
trunk/admin/partials/subscription-system-admin-add.php (modified) (1 diff)
-
trunk/admin/partials/work-items-display.php (modified) (1 diff)
-
trunk/admin/partials/work-logs-display.php (modified) (1 diff)
-
trunk/public/css/interest-subscribe.css (modified) (8 diffs)
-
trunk/public/js/interest-subscribe.js (modified) (4 diffs)
-
trunk/public/partials/interest-subscribe-modal.php (modified) (6 diffs)
-
trunk/subscription-system.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
subscription-system/tags/1.0.20/README.txt
r3598150 r3633440 6 6 Tested up to: 7.0 7 7 Requires PHP: 8.0 8 Stable tag: 1.0. 188 Stable tag: 1.0.20 9 9 License: GPLv2 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 85 85 86 86 == Changelog == 87 88 = 1.0.20 = 89 * Subscribe modal: added configurable re-show delay after close (minutes, days, weeks, months) 90 * Subscribe modal: fixed responsive layout with scrollable content and always-visible footer buttons 91 * Subscribe modal: removed post-close cooldown banners for a less intrusive experience 87 92 88 93 = 1.0.18 = … … 257 262 == Upgrade Notice == 258 263 264 = 1.0.20 = 265 Improves the subscribe modal with a configurable re-show delay after close, responsive scrollable layout, and removes intrusive post-close notifications. 266 259 267 = 1.0.18 = 260 268 Major feature update. Introduces the Subscribe Button & Modal system with floating buttons, auto-open, and context-aware category/tag subscriptions. Content Plans now power the modal with strict per-post plan filtering, Stripe sync, and a full Stripe Checkout payment flow. Guest users see a "Log in / Not now" prompt. Includes plan conflict prevention, improved plan cards, and a polished post-payment success page. -
subscription-system/tags/1.0.20/admin/class-subs-sys-admin.php
r3598150 r3633440 102 102 103 103 // 7. Channels 104 //$this->add_channels_submenu();104 $this->add_channels_submenu(); 105 105 106 106 // 8. Work Items … … 468 468 */ 469 469 public function render_dashboard_page() { 470 // Get subscription statistics 470 if (!current_user_can('manage_options')) { 471 wp_die( 472 esc_html__( 473 'You do not have permission to manage subscriptions.', 474 'subscription-system' 475 ) 476 ); 477 } 478 479 $action = isset($_GET['action']) 480 ? sanitize_key(wp_unslash($_GET['action'])) 481 : ''; 482 483 /* 484 * Open the manual subscription form when the URL contains: 485 * admin.php?page=subscription-dashboard&action=add 486 */ 487 if ('add' === $action) { 488 $plans = $this->subscription_manager->get_subscription_plans(); 489 490 $users = get_users( 491 array( 492 'orderby' => 'display_name', 493 'order' => 'ASC', 494 ) 495 ); 496 497 include plugin_dir_path(dirname(__FILE__)) 498 . 'admin/partials/subscription-system-admin-add.php'; 499 500 return; 501 } 502 503 /* 504 * Otherwise, show the normal dashboard. 505 */ 471 506 $active_subscriptions = $this->get_subscription_count('active'); 472 507 $paused_subscriptions = $this->get_subscription_count('paused'); 473 508 $total_revenue = $this->get_total_revenue(); 474 475 // Include the dashboard template476 include plugin_dir_path(dirname(__FILE__)). 'admin/partials/dashboard-display.php';509 510 include plugin_dir_path(dirname(__FILE__)) 511 . 'admin/partials/dashboard-display.php'; 477 512 } 478 513 … … 1115 1150 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open', array( 'sanitize_callback' => 'absint', 'default' => 0 ) ); 1116 1151 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_delay', array( 'sanitize_callback' => 'absint', 'default' => 3 ) ); 1152 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_cooldown', array( 'sanitize_callback' => 'absint', 'default' => 60 ) ); 1153 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_cooldown_unit', array( 1154 'sanitize_callback' => function( $val ) { 1155 $val = sanitize_key( $val ); 1156 return in_array( $val, subs_sys_get_modal_cooldown_units(), true ) ? $val : 'minutes'; 1157 }, 1158 'default' => 'minutes', 1159 ) ); 1117 1160 register_setting( 'subscription_modal_settings', 'subs_modal_floating_btn', array( 'sanitize_callback' => 'absint', 'default' => 0 ) ); 1118 1161 register_setting( 'subscription_modal_settings', 'subs_modal_floating_btn_text', array( 'sanitize_callback' => 'sanitize_text_field', 'default' => 'Subscribe' ) ); … … 1429 1472 */ 1430 1473 public function process_forms() { 1431 // Check if we're on the right page 1432 if (!isset($_GET['post_type']) || $_GET['post_type'] !== 'subscription') { 1433 return; 1434 } 1435 1436 // Process add plan form 1437 if (isset($_POST['add_plan_submit']) && isset($_POST['add_plan_nonce'])) { 1438 // Verify nonce 1439 if (!wp_verify_nonce($_POST['add_plan_nonce'], 'add_plan_nonce')) { 1440 wp_die(__('Security check failed', 'subscription-system')); 1441 } 1442 1443 // Get form data 1444 $raw_features = isset( $_POST['plan_features_json'] ) ? sanitize_text_field( wp_unslash( $_POST['plan_features_json'] ) ) : ''; 1445 $features_arr = json_decode( $raw_features, true ); 1446 if ( ! is_array( $features_arr ) ) { 1447 $features_arr = array( 'max_categories' => 0, 'max_tags' => 0 ); 1448 } 1449 $features_arr['max_categories'] = max( 0, (int) ( $features_arr['max_categories'] ?? 0 ) ); 1450 $features_arr['max_tags'] = max( 0, (int) ( $features_arr['max_tags'] ?? 0 ) ); 1451 1452 $plan_data = array( 1453 'plan_name' => isset($_POST['plan_name']) ? sanitize_text_field($_POST['plan_name']) : '', 1454 'description' => isset($_POST['description']) ? wp_kses_post($_POST['description']) : '', 1455 'price' => isset($_POST['price']) ? floatval($_POST['price']) : 0, 1456 'duration' => isset($_POST['duration']) ? intval($_POST['duration']) : 30, 1457 'category_id' => isset($_POST['category_id']) ? intval($_POST['category_id']) : 0, 1458 'status' => isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'active', 1459 'features' => wp_json_encode( $features_arr ), 1474 $page = isset($_GET['page']) 1475 ? sanitize_key(wp_unslash($_GET['page'])) 1476 : ''; 1477 1478 $action = isset($_GET['action']) 1479 ? sanitize_key(wp_unslash($_GET['action'])) 1480 : ''; 1481 1482 /* 1483 * Process the manual Add Subscription form. 1484 */ 1485 if ( 1486 'subscription-dashboard' === $page && 1487 'add' === $action && 1488 isset($_POST['ss_add_subscription']) 1489 ) { 1490 if (!current_user_can('manage_options')) { 1491 wp_die( 1492 esc_html__( 1493 'You do not have permission to create subscriptions.', 1494 'subscription-system' 1495 ) 1496 ); 1497 } 1498 1499 check_admin_referer('ss_add_subscription', 'ss_nonce'); 1500 1501 $user_id = isset($_POST['user_id']) 1502 ? absint($_POST['user_id']) 1503 : 0; 1504 1505 $plan_id = isset($_POST['plan_id']) 1506 ? absint($_POST['plan_id']) 1507 : 0; 1508 1509 $status = isset($_POST['status']) 1510 ? sanitize_key(wp_unslash($_POST['status'])) 1511 : 'active'; 1512 1513 $start_date_input = isset($_POST['start_date']) 1514 ? sanitize_text_field(wp_unslash($_POST['start_date'])) 1515 : ''; 1516 1517 /* 1518 * Store an error temporarily and return to the form. 1519 */ 1520 $error_redirect = static function ($message) { 1521 set_transient( 1522 'subs_sys_manual_subscription_error_' . get_current_user_id(), 1523 sanitize_text_field($message), 1524 60 1525 ); 1526 1527 wp_safe_redirect( 1528 add_query_arg( 1529 array( 1530 'page' => 'subscription-dashboard', 1531 'action' => 'add', 1532 'subscription_error' => '1', 1533 ), 1534 admin_url('admin.php') 1535 ) 1536 ); 1537 1538 exit; 1539 }; 1540 1541 /* 1542 * Validate the selected WordPress user. 1543 */ 1544 $user = get_userdata($user_id); 1545 1546 if (!$user) { 1547 $error_redirect( 1548 __('Please select a valid WordPress user.', 'subscription-system') 1549 ); 1550 } 1551 1552 /* 1553 * Validate the selected subscription plan. 1554 */ 1555 $plan = $this->subscription_manager->get_plan($plan_id); 1556 1557 if (!$plan) { 1558 $error_redirect( 1559 __('Please select a valid subscription plan.', 'subscription-system') 1560 ); 1561 } 1562 1563 /* 1564 * Only permit known subscription statuses. 1565 */ 1566 $allowed_statuses = array( 1567 'active', 1568 'paused', 1569 'cancelled', 1570 'expired', 1460 1571 ); 1461 1462 // Add the plan 1463 $result = $this->add_plan($plan_data); 1464 1572 1573 if (!in_array($status, $allowed_statuses, true)) { 1574 $status = 'active'; 1575 } 1576 1577 /* 1578 * Convert the datetime-local form value to MySQL format. 1579 */ 1580 $start_date_object = DateTimeImmutable::createFromFormat( 1581 'Y-m-d\TH:i', 1582 $start_date_input, 1583 wp_timezone() 1584 ); 1585 1586 if (false === $start_date_object) { 1587 $error_redirect( 1588 __('Please enter a valid start date.', 'subscription-system') 1589 ); 1590 } 1591 1592 $start_date = $start_date_object->format('Y-m-d H:i:s'); 1593 1594 /* 1595 * Prevent duplicate active or paused subscriptions for the 1596 * same user and plan. 1597 */ 1598 global $wpdb; 1599 1600 $subscriptions_table = $wpdb->prefix . 'subscriptions'; 1601 1602 $existing_subscription = $wpdb->get_var( 1603 $wpdb->prepare( 1604 "SELECT id 1605 FROM {$subscriptions_table} 1606 WHERE user_id = %d 1607 AND plan_id = %d 1608 AND status IN ('active', 'paused') 1609 LIMIT 1", 1610 $user_id, 1611 $plan_id 1612 ) 1613 ); 1614 1615 if ($existing_subscription) { 1616 $error_redirect( 1617 __( 1618 'This user already has an active or paused subscription for this plan.', 1619 'subscription-system' 1620 ) 1621 ); 1622 } 1623 1624 /* 1625 * Create the local manual subscription. 1626 * This does not create or charge a Stripe subscription. 1627 */ 1628 $result = $this->subscription_manager->create_subscription( 1629 $user_id, 1630 $plan_id, 1631 array( 1632 'status' => $status, 1633 'start_date' => $start_date, 1634 'customer_email' => $user->user_email, 1635 'customer_name' => $user->display_name, 1636 'payment_id' => 'manual_admin_' . wp_generate_uuid4(), 1637 ) 1638 ); 1639 1465 1640 if (is_wp_error($result)) { 1466 // Store error message 1467 add_settings_error( 1468 'subscription_system', 1469 'add_plan_error', 1470 $result->get_error_message(), 1471 'error' 1472 ); 1473 } else { 1474 // Store success message 1475 add_settings_error( 1476 'subscription_system', 1477 'add_plan_success', 1478 __('Plan added successfully', 'subscription-system'), 1479 'success' 1480 ); 1481 1482 // Use wp_safe_redirect instead of wp_redirect 1483 wp_safe_redirect(admin_url('edit.php?post_type=subscription&page=subscription-plans')); 1484 exit; 1485 } 1486 } 1487 1488 // Process edit plan form 1489 if (isset($_POST['edit_plan_submit']) && isset($_POST['edit_plan_nonce'])) { 1490 // Verify nonce 1491 if (!wp_verify_nonce($_POST['edit_plan_nonce'], 'edit_plan_nonce')) { 1492 wp_die(__('Security check failed', 'subscription-system')); 1493 } 1494 1495 // Get form data 1496 $plan_id = isset($_POST['plan_id']) ? intval($_POST['plan_id']) : 0; 1497 1498 if ($plan_id <= 0) { 1499 wp_die(__('Invalid plan ID', 'subscription-system')); 1500 } 1501 1502 $raw_features_edit = isset( $_POST['plan_features_json'] ) ? sanitize_text_field( wp_unslash( $_POST['plan_features_json'] ) ) : ''; 1503 $features_arr_edit = json_decode( $raw_features_edit, true ); 1504 if ( ! is_array( $features_arr_edit ) ) { 1505 $features_arr_edit = array( 'max_categories' => 0, 'max_tags' => 0 ); 1506 } 1507 $features_arr_edit['max_categories'] = max( 0, (int) ( $features_arr_edit['max_categories'] ?? 0 ) ); 1508 $features_arr_edit['max_tags'] = max( 0, (int) ( $features_arr_edit['max_tags'] ?? 0 ) ); 1509 1510 $plan_data = array( 1511 'plan_name' => isset($_POST['plan_name']) ? sanitize_text_field($_POST['plan_name']) : '', 1512 'description' => isset($_POST['description']) ? wp_kses_post($_POST['description']) : '', 1513 'price' => isset($_POST['price']) ? floatval($_POST['price']) : 0, 1514 'duration' => isset($_POST['duration']) ? intval($_POST['duration']) : 30, 1515 'category_id' => isset($_POST['category_id']) ? intval($_POST['category_id']) : 0, 1516 'status' => isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'active', 1517 'features' => wp_json_encode( $features_arr_edit ), 1518 'updated_at' => current_time('mysql'), 1641 $error_redirect($result->get_error_message()); 1642 } 1643 1644 wp_safe_redirect( 1645 add_query_arg( 1646 array( 1647 'page' => 'subscription-dashboard', 1648 'action' => 'add', 1649 'subscription_added' => absint($result), 1650 ), 1651 admin_url('admin.php') 1652 ) 1519 1653 ); 1520 1521 // Update the plan 1522 $result = $this->subscription_manager->update_plan($plan_id, $plan_data); 1523 1524 if (is_wp_error($result)) { 1525 // Store error message 1526 add_settings_error( 1527 'subscription_system', 1528 'edit_plan_error', 1529 $result->get_error_message(), 1530 'error' 1531 ); 1532 } else { 1533 // Store success message 1534 add_settings_error( 1535 'subscription_system', 1536 'edit_plan_success', 1537 __('Plan updated successfully', 'subscription-system'), 1538 'success' 1539 ); 1540 1541 // Use wp_safe_redirect instead of wp_redirect 1542 wp_safe_redirect(admin_url('edit.php?post_type=subscription&page=subscription-plans')); 1543 exit; 1544 } 1545 } 1546 1547 // Process developer mode forms 1654 1655 exit; 1656 } 1657 1658 /* 1659 * Keep the plugin's existing Developer Mode processing working. 1660 */ 1548 1661 $this->process_developer_mode_forms(); 1549 1662 } … … 3593 3706 */ 3594 3707 public function render_work_logs_page() { 3595 // Get user ID from URL 3596 $user_id = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0; 3597 3598 // Get current user ID 3708 $is_admin = current_user_can('manage_options'); 3599 3709 $current_user_id = get_current_user_id(); 3600 3601 // Check permissions 3602 // Admin can view any user's logs, non-admin can only view their own logs 3603 if (!current_user_can('manage_options')) { 3604 // If not an admin, ensure user can only view their own logs 3605 if ($user_id != $current_user_id) { 3710 3711 $user_id = isset($_GET['user_id']) 3712 ? absint(wp_unslash($_GET['user_id'])) 3713 : 0; 3714 3715 /* 3716 * These variables are always defined so the template can safely 3717 * display either the subscriber selector or an individual user's logs. 3718 */ 3719 $subscriber_choices = array(); 3720 $show_user_selector = false; 3721 $selection_error = ''; 3722 $user = false; 3723 $work_logs = array(); 3724 $channels = array(); 3725 $work_items = array(); 3726 3727 /* 3728 * Administrators may select any subscriber. 3729 * Only users who actually have at least one subscription are shown. 3730 */ 3731 if ($is_admin) { 3732 $all_subscribers = $this->get_all_subscribers(); 3733 3734 foreach ($all_subscribers as $subscriber) { 3735 if (!empty($subscriber['subscriptions'])) { 3736 $subscriber_choices[] = $subscriber; 3737 } 3738 } 3739 3740 /* 3741 * Opening the Work Logs menu without user_id should show 3742 * the subscriber selector instead of an invalid-user error. 3743 */ 3744 if (0 === $user_id) { 3745 $show_user_selector = true; 3746 } 3747 } else { 3748 /* 3749 * Non-admin users can only view their own work logs. 3750 */ 3751 if ($user_id > 0 && $user_id !== $current_user_id) { 3606 3752 wp_die( 3607 esc_html__('Sorry, you are not allowed to view this user\'s work logs.', 'subscription-system'), 3753 esc_html__( 3754 'Sorry, you are not allowed to view this user\'s work logs.', 3755 'subscription-system' 3756 ), 3608 3757 esc_html__('Permission Denied', 'subscription-system'), 3609 3758 array('response' => 403) 3610 3759 ); 3611 3760 } 3612 } 3613 3614 // If no user_id specified and user is not admin, default to current user 3615 if ($user_id == 0 && !current_user_can('manage_options')) { 3761 3616 3762 $user_id = $current_user_id; 3617 3763 } 3618 3619 // Process work log form submissions (add, edit, delete) 3620 $this->process_work_log_form(); 3621 3622 // Get user information 3623 $user = get_userdata($user_id); 3624 3625 // Get all work logs for this user 3626 $work_logs = $this->get_user_work_logs($user_id); 3627 3628 // Get all channels for dropdown 3629 $channels = $this->get_all_channels(); 3630 3631 // Get all work items (will be filtered by JavaScript based on selected channel) 3632 $work_items = $this->get_all_work_items(); 3633 3634 // Include the work logs template 3635 include plugin_dir_path(dirname(__FILE__)) . 'admin/partials/work-logs-display.php'; 3764 3765 /* 3766 * When a user has been selected, verify that the WordPress user exists. 3767 */ 3768 if (!$show_user_selector) { 3769 $user = get_userdata($user_id); 3770 3771 if (!$user) { 3772 if ($is_admin) { 3773 $show_user_selector = true; 3774 $selection_error = __( 3775 'The selected subscriber could not be found. Please select another subscriber.', 3776 'subscription-system' 3777 ); 3778 } else { 3779 wp_die( 3780 esc_html__( 3781 'Your WordPress user account could not be found.', 3782 'subscription-system' 3783 ) 3784 ); 3785 } 3786 } 3787 } 3788 3789 /* 3790 * Only process and load work logs after a valid user is selected. 3791 */ 3792 if (!$show_user_selector && $user) { 3793 $this->process_work_log_form(); 3794 3795 $work_logs = $this->get_user_work_logs($user_id); 3796 $channels = $this->get_all_channels(); 3797 $work_items = $this->get_all_work_items(); 3798 } 3799 3800 include plugin_dir_path(dirname(__FILE__)) 3801 . 'admin/partials/work-logs-display.php'; 3636 3802 } 3637 3803 -
subscription-system/tags/1.0.20/admin/partials/dashboard-display.php
r3311076 r3633440 60 60 61 61 <div class="subscription-actions"> 62 <a href="<?php echo esc_url(admin_url(' post-new.php?post_type=subscription')); ?>" class="button button-primary">62 <a href="<?php echo esc_url(admin_url('admin.php?page=subscription-dashboard&action=add')); ?>" class="button button-primary"> 63 63 <?php echo esc_html__('Add New Subscription', 'subscription-system'); ?> 64 64 </a> -
subscription-system/tags/1.0.20/admin/partials/plan-categories-display.php
r3311076 r3633440 1 1 <?php 2 2 3 /** 3 4 * Provide a admin area view for the plugin … … 204 205 205 206 // Delete category 206 $('.delete-category').on('click', function() {207 /*$('.delete-category').on('click', function() { 207 208 var id = $(this).data('id'); 208 209 var nonce = $(this).data('nonce'); … … 211 212 window.location.href = '<?php echo esc_url(admin_url('edit.php?post_type=subscription&page=subscription-plan-categories&action=delete&category_id=')); ?>' + id + '&_wpnonce=' + nonce; 212 213 } 213 }); 214 });*/ 215 216 // Delete category 217 $('.delete-category').on('click', function() { 218 var id = $(this).data('id'); 219 var nonce = $(this).data('nonce'); 220 221 if (!confirm('<?php echo esc_js(__('Are you sure you want to delete this category?', 'subscription-system')); ?>')) { 222 return; 223 } 224 225 var deleteUrl = new URL( 226 <?php echo wp_json_encode(admin_url('admin.php')); ?> 227 ); 228 229 deleteUrl.searchParams.set('page', 'subscription-plan-categories'); 230 deleteUrl.searchParams.set('action', 'delete'); 231 deleteUrl.searchParams.set('category_id', id); 232 deleteUrl.searchParams.set('_wpnonce', nonce); 233 234 window.location.assign(deleteUrl.toString()); 235 }); 214 236 215 237 // Cancel edit -
subscription-system/tags/1.0.20/admin/partials/plans-display.php
r3598150 r3633440 500 500 501 501 // Delete plan 502 $('.delete-plan').on('click', function() {502 /* $('.delete-plan').on('click', function() { 503 503 var id = $(this).data('id'); 504 504 var nonce = $(this).data('nonce'); … … 507 507 window.location.href = '<?php echo admin_url('edit.php?post_type=subscription&page=subscription-plans&action=delete&plan_id='); ?>' + id + '&_wpnonce=' + nonce; 508 508 } 509 }); 509 }); */ 510 511 // Delete plan 512 $('.delete-plan').on('click', function(event) { 513 event.preventDefault(); 514 515 var id = $(this).data('id'); 516 var nonce = $(this).data('nonce'); 517 518 if (!confirm('<?php echo esc_js(__('Are you sure you want to delete this plan?', 'subscription-system')); ?>')) { 519 return; 520 } 521 522 /* 523 * Plans are displayed through a custom admin page. 524 * Therefore, the request must use admin.php rather than edit.php. 525 */ 526 var deleteUrl = new URL( 527 <?php echo wp_json_encode(admin_url('admin.php')); ?> 528 ); 529 530 deleteUrl.searchParams.set('page', 'subscription-plans'); 531 deleteUrl.searchParams.set('action', 'delete'); 532 deleteUrl.searchParams.set('plan_id', id); 533 deleteUrl.searchParams.set('_wpnonce', nonce); 534 535 window.location.assign(deleteUrl.toString()); 536 }); 537 510 538 511 539 // Cancel edit -
subscription-system/tags/1.0.20/admin/partials/subscribe-modal-settings-display.php
r3598150 r3633440 46 46 class="small-text"> 47 47 <p class="description"><?php esc_html_e( 'Seconds to wait before automatically opening the modal (0 = immediate).', 'subscription-system' ); ?></p> 48 </td> 49 </tr> 50 51 <tr> 52 <th scope="row"><?php esc_html_e( 'Re-show After Close', 'subscription-system' ); ?></th> 53 <td> 54 <?php 55 $cooldown_value = absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 56 $cooldown_unit = sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 57 $cooldown_units = array( 58 'minutes' => __( 'Minutes', 'subscription-system' ), 59 'days' => __( 'Days', 'subscription-system' ), 60 'weeks' => __( 'Weeks', 'subscription-system' ), 61 'months' => __( 'Months', 'subscription-system' ), 62 ); 63 ?> 64 <input type="number" name="subs_modal_auto_open_cooldown" min="0" max="999" step="1" 65 value="<?php echo esc_attr( $cooldown_value ); ?>" 66 class="small-text"> 67 <select name="subs_modal_auto_open_cooldown_unit"> 68 <?php foreach ( $cooldown_units as $unit_key => $unit_label ) : ?> 69 <option value="<?php echo esc_attr( $unit_key ); ?>" <?php selected( $cooldown_unit, $unit_key ); ?>> 70 <?php echo esc_html( $unit_label ); ?> 71 </option> 72 <?php endforeach; ?> 73 </select> 74 <p class="description"> 75 <?php esc_html_e( 'After a visitor closes the modal, wait this long before auto-opening it again. Use 0 to auto-open on every page load (no cooldown). Visitors will see this timing in the modal and after closing it.', 'subscription-system' ); ?> 76 </p> 77 <?php if ( $cooldown_value > 0 && function_exists( 'subs_sys_get_modal_cooldown_label' ) ) : ?> 78 <p class="description"> 79 <?php 80 printf( 81 /* translators: %s: human-readable cooldown period */ 82 esc_html__( 'Preview: visitors will see “We won\'t show this again for %s.”', 'subscription-system' ), 83 esc_html( subs_sys_get_modal_cooldown_label( $cooldown_value, $cooldown_unit ) ) 84 ); 85 ?> 86 </p> 87 <?php endif; ?> 48 88 </td> 49 89 </tr> -
subscription-system/tags/1.0.20/admin/partials/subscription-system-admin-add.php
r3311076 r3633440 1 1 <?php 2 2 /** 3 * Admin add subscription view.3 * Manual Add Subscription screen. 4 4 * 5 * @since 1.0.0 6 * @package Subscription_System 5 * @package Subscription_System 7 6 */ 8 7 9 // If this file is called directly, abort.10 8 if (!defined('ABSPATH')) { 11 9 exit; 12 10 } 11 12 $error_message = ''; 13 14 if (isset($_GET['subscription_error'])) { 15 $error_key = 'subs_sys_manual_subscription_error_' 16 . get_current_user_id(); 17 18 $error_message = get_transient($error_key); 19 20 delete_transient($error_key); 21 } 13 22 ?> 14 23 15 24 <div class="wrap ss-admin-container"> 16 <h1 class="ss-admin-title"><?php esc_html_e('Add Subscription', 'subscription-system'); ?></h1> 17 18 <?php if (isset($success_message)) : ?> 19 <div class="ss-admin-message success"> 20 <?php echo esc_html($success_message); ?> 25 <h1 class="wp-heading-inline"> 26 <?php esc_html_e('Add New Subscription', 'subscription-system'); ?> 27 </h1> 28 29 <a 30 href="<?php echo esc_url(admin_url('admin.php?page=subscription-dashboard')); ?>" 31 class="page-title-action" 32 > 33 <?php esc_html_e('Back to Dashboard', 'subscription-system'); ?> 34 </a> 35 36 <hr class="wp-header-end"> 37 38 <?php if (isset($_GET['subscription_added'])) : ?> 39 <div class="notice notice-success is-dismissible"> 40 <p> 41 <?php esc_html_e( 42 'Subscription created successfully.', 43 'subscription-system' 44 ); ?> 45 </p> 21 46 </div> 22 47 <?php endif; ?> 23 24 <?php if ( isset($error_message)) : ?>25 <div class=" ss-admin-message error">26 < ?php echo esc_html($error_message); ?>48 49 <?php if (!empty($error_message)) : ?> 50 <div class="notice notice-error is-dismissible"> 51 <p><?php echo esc_html($error_message); ?></p> 27 52 </div> 28 53 <?php endif; ?> 29 30 <form method="post" class="ss-admin-form"> 31 <?php wp_nonce_field('ss_add_subscription', 'ss_nonce'); ?> 32 33 <div class="ss-admin-form-row"> 34 <label for="user_id"><?php esc_html_e('User ID', 'subscription-system'); ?></label> 35 <input type="number" id="user_id" name="user_id" required> 36 <p class="description"><?php esc_html_e('Enter the WordPress user ID.', 'subscription-system'); ?></p> 37 </div> 38 39 <div class="ss-admin-form-row"> 40 <label for="email"><?php esc_html_e('Email', 'subscription-system'); ?></label> 41 <input type="email" id="email" name="email" required> 42 </div> 43 44 <div class="ss-admin-form-row"> 45 <label for="plan_id"><?php esc_html_e('Subscription Plan', 'subscription-system'); ?></label> 46 <select id="plan_id" name="plan_id" required> 47 <option value=""><?php esc_html_e('Select a plan', 'subscription-system'); ?></option> 48 <?php foreach ($plans as $plan_id => $plan) : ?> 49 <option value="<?php echo esc_attr($plan_id); ?>"><?php echo esc_html($plan['name']); ?></option> 50 <?php endforeach; ?> 51 </select> 52 </div> 53 54 <div class="ss-admin-form-row"> 55 <label for="status"><?php esc_html_e('Status', 'subscription-system'); ?></label> 56 <select id="status" name="status"> 57 <option value="active"><?php esc_html_e('Active', 'subscription-system'); ?></option> 58 <option value="cancelled"><?php esc_html_e('Cancelled', 'subscription-system'); ?></option> 59 <option value="expired"><?php esc_html_e('Expired', 'subscription-system'); ?></option> 60 </select> 61 </div> 62 63 <div class="ss-admin-form-row"> 64 <label for="start_date"><?php esc_html_e('Start Date', 'subscription-system'); ?></label> 65 <input type="text" id="start_date" name="start_date" value="<?php echo esc_attr(current_time('mysql')); ?>" required> 66 <p class="description"><?php esc_html_e('Format: YYYY-MM-DD HH:MM:SS', 'subscription-system'); ?></p> 67 </div> 68 69 <div class="ss-admin-form-row"> 70 <button type="submit" name="ss_add_subscription" class="button button-primary"><?php esc_html_e('Add Subscription', 'subscription-system'); ?></button> 71 </div> 72 </form> 73 </div> 54 55 <?php if (empty($users)) : ?> 56 <div class="notice notice-warning"> 57 <p> 58 <?php esc_html_e( 59 'No WordPress users are available. Create a user before adding a subscription.', 60 'subscription-system' 61 ); ?> 62 </p> 63 </div> 64 <?php elseif (empty($plans)) : ?> 65 <div class="notice notice-warning"> 66 <p> 67 <?php esc_html_e( 68 'No active subscription plans are available. Create a plan first.', 69 'subscription-system' 70 ); ?> 71 </p> 72 </div> 73 <?php else : ?> 74 75 <div class="card" style="max-width: 700px; margin-top: 20px;"> 76 <form method="post"> 77 <?php wp_nonce_field('ss_add_subscription', 'ss_nonce'); ?> 78 79 <table class="form-table" role="presentation"> 80 <tbody> 81 <tr> 82 <th scope="row"> 83 <label for="user_id"> 84 <?php esc_html_e( 85 'WordPress User', 86 'subscription-system' 87 ); ?> 88 </label> 89 </th> 90 91 <td> 92 <select 93 id="user_id" 94 name="user_id" 95 class="regular-text" 96 required 97 > 98 <option value=""> 99 <?php esc_html_e( 100 'Select a user', 101 'subscription-system' 102 ); ?> 103 </option> 104 105 <?php foreach ($users as $user) : ?> 106 <option value="<?php echo esc_attr($user->ID); ?>"> 107 <?php 108 echo esc_html( 109 $user->display_name 110 . ' — ' 111 . $user->user_email 112 . ' (ID: ' 113 . $user->ID 114 . ')' 115 ); 116 ?> 117 </option> 118 <?php endforeach; ?> 119 </select> 120 </td> 121 </tr> 122 123 <tr> 124 <th scope="row"> 125 <label for="plan_id"> 126 <?php esc_html_e( 127 'Subscription Plan', 128 'subscription-system' 129 ); ?> 130 </label> 131 </th> 132 133 <td> 134 <select 135 id="plan_id" 136 name="plan_id" 137 class="regular-text" 138 required 139 > 140 <option value=""> 141 <?php esc_html_e( 142 'Select a plan', 143 'subscription-system' 144 ); ?> 145 </option> 146 147 <?php foreach ($plans as $plan) : ?> 148 <option value="<?php echo esc_attr($plan->id); ?>"> 149 <?php 150 echo esc_html( 151 $plan->plan_name 152 . ' — ' 153 . number_format((float) $plan->price, 2) 154 ); 155 ?> 156 </option> 157 <?php endforeach; ?> 158 </select> 159 </td> 160 </tr> 161 162 <tr> 163 <th scope="row"> 164 <label for="status"> 165 <?php esc_html_e( 166 'Status', 167 'subscription-system' 168 ); ?> 169 </label> 170 </th> 171 172 <td> 173 <select id="status" name="status"> 174 <option value="active"> 175 <?php esc_html_e('Active', 'subscription-system'); ?> 176 </option> 177 178 <option value="paused"> 179 <?php esc_html_e('Paused', 'subscription-system'); ?> 180 </option> 181 182 <option value="cancelled"> 183 <?php esc_html_e('Cancelled', 'subscription-system'); ?> 184 </option> 185 186 <option value="expired"> 187 <?php esc_html_e('Expired', 'subscription-system'); ?> 188 </option> 189 </select> 190 </td> 191 </tr> 192 193 <tr> 194 <th scope="row"> 195 <label for="start_date"> 196 <?php esc_html_e( 197 'Start Date', 198 'subscription-system' 199 ); ?> 200 </label> 201 </th> 202 203 <td> 204 <input 205 type="datetime-local" 206 id="start_date" 207 name="start_date" 208 value="<?php echo esc_attr(current_time('Y-m-d\TH:i')); ?>" 209 required 210 > 211 </td> 212 </tr> 213 </tbody> 214 </table> 215 216 <?php 217 submit_button( 218 __('Add Subscription', 'subscription-system'), 219 'primary', 220 'ss_add_subscription' 221 ); 222 ?> 223 </form> 224 </div> 225 226 <?php endif; ?> 227 </div> -
subscription-system/tags/1.0.20/admin/partials/work-items-display.php
r3311076 r3633440 60 60 <?php echo esc_html__('Edit', 'subscription-system'); ?> 61 61 </a> | 62 <a href="<?php echo esc_url(wp_nonce_url(admin_url('edit.php?post_type=subscription&page=subscription-work-items&action=delete&work_item_id=' . $work_item->id), 'delete_work_item_' . $work_item->id)); ?>" class="delete-work-item" onclick="return confirm('<?php echo esc_js(esc_html__('Are you sure you want to delete this work item?', 'subscription-system')); ?>')"> 63 <?php echo esc_html__('Delete', 'subscription-system'); ?> 62 <a 63 href="<?php 64 echo esc_url( 65 wp_nonce_url( 66 admin_url( 67 'admin.php?page=subscription-work-items' 68 . '&action=delete' 69 . '&work_item_id=' 70 . absint($work_item->id) 71 ), 72 'delete_work_item_' . absint($work_item->id) 73 ) 74 ); 75 ?>" 76 class="delete-work-item" 77 onclick="return confirm('<?php echo esc_js(__('Are you sure you want to delete this work item?', 'subscription-system')); ?>');" 78 > 79 <?php echo esc_html__('Delete', 'subscription-system'); ?> 80 </a> 64 81 </a> 65 82 </td> -
subscription-system/tags/1.0.20/admin/partials/work-logs-display.php
r3463757 r3633440 26 26 27 27 <div class="wrap"> 28 <?php if ($user): ?> 29 <?php 30 /* translators: %s: user's display name */ 31 ?><h1><?php echo esc_html(sprintf(__('Work Logs for %s', 'subscription-system'), $user->display_name)); ?></h1> 32 <?php else: ?> 33 <h1><?php echo esc_html__('Work Logs', 'subscription-system'); ?></h1> 28 29 <?php if (!empty($show_user_selector)) : ?> 30 31 <h1> 32 <?php echo esc_html__('Work Logs', 'subscription-system'); ?> 33 </h1> 34 35 <?php if (!empty($selection_error)) : ?> 36 <div class="notice notice-error"> 37 <p><?php echo esc_html($selection_error); ?></p> 38 </div> 39 <?php endif; ?> 40 41 <div 42 class="subscription-admin-card" 43 style="max-width: 650px; margin-top: 20px;" 44 > 45 <h2> 46 <?php echo esc_html__( 47 'Select a Subscriber', 48 'subscription-system' 49 ); ?> 50 </h2> 51 52 <p> 53 <?php echo esc_html__( 54 'Choose a subscriber to view, add, edit, or delete their work logs.', 55 'subscription-system' 56 ); ?> 57 </p> 58 59 <?php if (empty($subscriber_choices)) : ?> 60 61 <div class="notice notice-warning inline"> 62 <p> 63 <?php echo esc_html__( 64 'No subscribers with subscription plans were found.', 65 'subscription-system' 66 ); ?> 67 </p> 68 </div> 69 70 <p> 71 <a 72 href="<?php echo esc_url( 73 admin_url( 74 'admin.php?page=subscription-subscribers' 75 ) 76 ); ?>" 77 class="button" 78 > 79 <?php echo esc_html__( 80 'View Subscribers', 81 'subscription-system' 82 ); ?> 83 </a> 84 </p> 85 86 <?php else : ?> 87 88 <form 89 method="get" 90 action="<?php echo esc_url(admin_url('admin.php')); ?>" 91 > 92 <input 93 type="hidden" 94 name="page" 95 value="subscription-work-logs" 96 > 97 98 <table class="form-table" role="presentation"> 99 <tbody> 100 <tr> 101 <th scope="row"> 102 <label for="work_logs_user_id"> 103 <?php echo esc_html__( 104 'Subscriber', 105 'subscription-system' 106 ); ?> 107 </label> 108 </th> 109 110 <td> 111 <select 112 id="work_logs_user_id" 113 name="user_id" 114 class="regular-text" 115 required 116 > 117 <option value=""> 118 <?php echo esc_html__( 119 'Select a subscriber', 120 'subscription-system' 121 ); ?> 122 </option> 123 124 <?php 125 foreach ( 126 $subscriber_choices as $subscriber 127 ) : 128 ?> 129 <option 130 value="<?php echo esc_attr( 131 $subscriber['user_id'] 132 ); ?>" 133 > 134 <?php 135 echo esc_html( 136 $subscriber['display_name'] 137 . ' — ' 138 . $subscriber['email'] 139 . ' (ID: ' 140 . $subscriber['user_id'] 141 . ')' 142 ); 143 ?> 144 </option> 145 <?php endforeach; ?> 146 </select> 147 </td> 148 </tr> 149 </tbody> 150 </table> 151 152 <?php 153 submit_button( 154 __('View Work Logs', 'subscription-system'), 155 'primary', 156 'submit', 157 false 158 ); 159 ?> 160 </form> 161 162 <?php endif; ?> 163 </div> 164 165 <?php return; ?> 166 167 <?php endif; ?> 168 169 <?php if (!$user) : ?> 170 <h1> 171 <?php echo esc_html__('Work Logs', 'subscription-system'); ?> 172 </h1> 173 34 174 <div class="notice notice-error"> 35 <p><?php echo esc_html__('Invalid user ID. Please select a valid user.', 'subscription-system'); ?></p> 175 <p> 176 <?php echo esc_html__( 177 'The selected user could not be found.', 178 'subscription-system' 179 ); ?> 180 </p> 36 181 </div> 37 <?php if (current_user_can('manage_options')): ?> 38 <p><a href="<?php echo esc_url(admin_url('admin.php?page=subscription-subscribers')); ?>" class="button"><?php echo esc_html__('Back to Subscribers', 'subscription-system'); ?></a></p> 39 <?php endif; ?> 182 40 183 <?php return; ?> 41 184 <?php endif; ?> 42 43 <?php if (current_user_can('manage_options')): ?> 44 <p><a href="<?php echo esc_url(admin_url('admin.php?page=subscription-subscribers')); ?>" class="button"><?php echo esc_html__('Back to Subscribers', 'subscription-system'); ?></a></p> 185 186 <?php 187 /* translators: %s: user's display name */ 188 ?> 189 <h1> 190 <?php 191 echo esc_html( 192 sprintf( 193 __('Work Logs for %s', 'subscription-system'), 194 $user->display_name 195 ) 196 ); 197 ?> 198 </h1> 199 200 <?php if (current_user_can('manage_options')) : ?> 201 <p> 202 <a 203 href="<?php echo esc_url( 204 admin_url('admin.php?page=subscription-work-logs') 205 ); ?>" 206 class="button" 207 > 208 <?php echo esc_html__( 209 'Select Another Subscriber', 210 'subscription-system' 211 ); ?> 212 </a> 213 214 <a 215 href="<?php echo esc_url( 216 admin_url('admin.php?page=subscription-subscribers') 217 ); ?>" 218 class="button" 219 > 220 <?php echo esc_html__( 221 'Back to Subscribers', 222 'subscription-system' 223 ); ?> 224 </a> 225 </p> 45 226 <?php endif; ?> 46 227 47 228 <?php settings_errors('subscription_messages'); ?> 48 49 <div class="subscription-admin-container"> 50 <div class="subscription-admin-main"> 51 <div class="subscription-admin-card"> 52 <h2><?php echo esc_html__('All Work Logs', 'subscription-system'); ?></h2> 53 54 <?php if (empty($work_logs)): ?> 55 <p><?php echo esc_html__('No work logs found for this user.', 'subscription-system'); ?></p> 56 <?php else: ?> 57 <table class="wp-list-table widefat fixed striped"> 58 <thead> 229 230 <div class="subscription-admin-container"> 231 232 <div class="subscription-admin-main"> 233 <div class="subscription-admin-card"> 234 235 <h2> 236 <?php echo esc_html__( 237 'All Work Logs', 238 'subscription-system' 239 ); ?> 240 </h2> 241 242 <?php if (empty($work_logs)) : ?> 243 244 <p> 245 <?php echo esc_html__( 246 'No work logs found for this subscriber.', 247 'subscription-system' 248 ); ?> 249 </p> 250 251 <?php else : ?> 252 253 <table class="wp-list-table widefat fixed striped"> 254 <thead> 255 <tr> 256 <th> 257 <?php echo esc_html__( 258 'ID', 259 'subscription-system' 260 ); ?> 261 </th> 262 263 <th> 264 <?php echo esc_html__( 265 'Channel', 266 'subscription-system' 267 ); ?> 268 </th> 269 270 <th> 271 <?php echo esc_html__( 272 'Work Item', 273 'subscription-system' 274 ); ?> 275 </th> 276 277 <th> 278 <?php echo esc_html__( 279 'Importance', 280 'subscription-system' 281 ); ?> 282 </th> 283 284 <th> 285 <?php echo esc_html__( 286 'Notes', 287 'subscription-system' 288 ); ?> 289 </th> 290 291 <th> 292 <?php echo esc_html__( 293 'Performed At', 294 'subscription-system' 295 ); ?> 296 </th> 297 298 <th> 299 <?php echo esc_html__( 300 'Created At', 301 'subscription-system' 302 ); ?> 303 </th> 304 305 <th> 306 <?php echo esc_html__( 307 'Actions', 308 'subscription-system' 309 ); ?> 310 </th> 311 </tr> 312 </thead> 313 314 <tbody> 315 <?php foreach ($work_logs as $work_log) : ?> 59 316 <tr> 60 <th><?php echo esc_html__('ID', 'subscription-system'); ?></th> 61 <th><?php echo esc_html__('Channel', 'subscription-system'); ?></th> 62 <th><?php echo esc_html__('Work Item', 'subscription-system'); ?></th> 63 <th><?php echo esc_html__('Importance', 'subscription-system'); ?></th> 64 <th><?php echo esc_html__('Notes', 'subscription-system'); ?></th> 65 <th><?php echo esc_html__('Performed At', 'subscription-system'); ?></th> 66 <th><?php echo esc_html__('Created At', 'subscription-system'); ?></th> 67 <th><?php echo esc_html__('Actions', 'subscription-system'); ?></th> 317 <td> 318 <?php echo esc_html($work_log->id); ?> 319 </td> 320 321 <td> 322 <?php 323 echo esc_html( 324 $work_log->channel_name 325 ? $work_log->channel_name 326 : __('None', 'subscription-system') 327 ); 328 ?> 329 </td> 330 331 <td> 332 <?php 333 echo esc_html( 334 $work_log->work_item_name 335 ? $work_log->work_item_name 336 : __('None', 'subscription-system') 337 ); 338 ?> 339 </td> 340 341 <td> 342 <?php echo esc_html( 343 $work_log->importance 344 ); ?> 345 </td> 346 347 <td> 348 <div class="work-log-notes-preview"> 349 <?php 350 $plain_notes = wp_strip_all_tags( 351 $work_log->notes 352 ); 353 354 echo esc_html( 355 mb_substr($plain_notes, 0, 100) 356 ); 357 358 if (mb_strlen($plain_notes) > 100) { 359 echo '…'; 360 } 361 ?> 362 363 <?php if ( 364 mb_strlen($plain_notes) > 100 365 ) : ?> 366 <a 367 href="#" 368 class="view-full-notes" 369 data-notes="<?php echo esc_attr( 370 $work_log->notes 371 ); ?>" 372 > 373 <?php echo esc_html__( 374 'View Full Notes', 375 'subscription-system' 376 ); ?> 377 </a> 378 <?php endif; ?> 379 </div> 380 </td> 381 382 <td> 383 <?php 384 echo esc_html( 385 date_i18n( 386 get_option('date_format') 387 . ' ' 388 . get_option('time_format'), 389 strtotime( 390 $work_log->performed_at 391 ) 392 ) 393 ); 394 ?> 395 </td> 396 397 <td> 398 <?php 399 echo esc_html( 400 date_i18n( 401 get_option('date_format') 402 . ' ' 403 . get_option('time_format'), 404 strtotime( 405 $work_log->created_at 406 ) 407 ) 408 ); 409 ?> 410 </td> 411 412 <td> 413 <a 414 href="#" 415 class="edit-work-log" 416 data-id="<?php echo esc_attr( 417 $work_log->id 418 ); ?>" 419 data-channel-id="<?php echo esc_attr( 420 $work_log->channel_id 421 ); ?>" 422 data-work-item-id="<?php echo esc_attr( 423 $work_log->work_item_id 424 ); ?>" 425 data-importance="<?php echo esc_attr( 426 $work_log->importance 427 ); ?>" 428 data-notes="<?php echo esc_attr( 429 $work_log->notes 430 ); ?>" 431 data-performed-at="<?php echo esc_attr( 432 date( 433 'Y-m-d H:i', 434 strtotime( 435 $work_log->performed_at 436 ) 437 ) 438 ); ?>" 439 > 440 <?php echo esc_html__( 441 'Edit', 442 'subscription-system' 443 ); ?> 444 </a> 445 446 | 447 448 <a 449 href="<?php 450 echo esc_url( 451 wp_nonce_url( 452 add_query_arg( 453 array( 454 'page' => 'subscription-work-logs', 455 'action' => 'delete', 456 'work_log_id' => absint( 457 $work_log->id 458 ), 459 'user_id' => absint( 460 $user_id 461 ), 462 ), 463 admin_url( 464 'admin.php' 465 ) 466 ), 467 'delete_work_log_' 468 . absint( 469 $work_log->id 470 ) 471 ) 472 ); 473 ?>" 474 class="delete-work-log" 475 onclick="return confirm('<?php 476 echo esc_js( 477 __( 478 'Are you sure you want to delete this work log?', 479 'subscription-system' 480 ) 481 ); 482 ?>');" 483 > 484 <?php echo esc_html__( 485 'Delete', 486 'subscription-system' 487 ); ?> 488 </a> 489 </td> 68 490 </tr> 69 </thead> 70 <tbody> 71 <?php foreach ($work_logs as $work_log): ?> 72 <tr> 73 <td><?php echo esc_html($work_log->id); ?></td> 74 <td><?php echo esc_html($work_log->channel_name ? $work_log->channel_name : __('None', 'subscription-system')); ?></td> 75 <td><?php echo esc_html($work_log->work_item_name ? $work_log->work_item_name : __('None', 'subscription-system')); ?></td> 76 <td><?php echo esc_html($work_log->importance); ?></td> 77 <td> 78 <div class="work-log-notes-preview"> 79 <?php echo wp_kses_post(substr(strip_tags($work_log->notes), 0, 100) . (strlen(strip_tags($work_log->notes)) > 100 ? '...' : '')); ?> 80 <?php if (strlen(strip_tags($work_log->notes)) > 100): ?> 81 <a href="#" class="view-full-notes" data-notes="<?php echo esc_attr($work_log->notes); ?>"><?php echo esc_html__('View Full Notes', 'subscription-system'); ?></a> 82 <?php endif; ?> 83 </div> 84 </td> 85 <td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($work_log->performed_at))); ?></td> 86 <td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($work_log->created_at))); ?></td> 87 <td> 88 <a href="#" class="edit-work-log" 89 data-id="<?php echo esc_attr($work_log->id); ?>" 90 data-channel-id="<?php echo esc_attr($work_log->channel_id); ?>" 91 data-work-item-id="<?php echo esc_attr($work_log->work_item_id); ?>" 92 data-importance="<?php echo esc_attr($work_log->importance); ?>" 93 data-notes="<?php echo esc_attr($work_log->notes); ?>" 94 data-performed-at="<?php echo esc_attr(date('Y-m-d H:i', strtotime($work_log->performed_at))); ?>"> 95 <?php echo esc_html__('Edit', 'subscription-system'); ?> 96 </a> | 97 <a href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=subscription-work-logs&action=delete&work_log_id=' . $work_log->id . '&user_id=' . $user_id), 'delete_work_log_' . $work_log->id)); ?>" class="delete-work-log" onclick="return confirm('<?php echo esc_js(__('Are you sure you want to delete this work log?', 'subscription-system')); ?>')"> 98 <?php echo esc_html__('Delete', 'subscription-system'); ?> 99 </a> 100 </td> 101 </tr> 102 <?php endforeach; ?> 103 </tbody> 104 </table> 105 <?php endif; ?> 106 </div> 491 <?php endforeach; ?> 492 </tbody> 493 </table> 494 495 <?php endif; ?> 496 107 497 </div> 108 109 <div class="subscription-admin-sidebar"> 110 <div class="subscription-admin-card"> 111 <h2><?php echo esc_html__('Add New Work Log', 'subscription-system'); ?></h2> 112 498 </div> 499 500 <div class="subscription-admin-sidebar"> 501 502 <div class="subscription-admin-card"> 503 504 <h2> 505 <?php echo esc_html__( 506 'Add New Work Log', 507 'subscription-system' 508 ); ?> 509 </h2> 510 511 <?php if (empty($channels)) : ?> 512 513 <div class="notice notice-warning inline"> 514 <p> 515 <?php echo esc_html__( 516 'No channels are available. Create an active channel before adding a work log.', 517 'subscription-system' 518 ); ?> 519 </p> 520 </div> 521 522 <?php else : ?> 523 113 524 <form method="post" action=""> 114 <?php wp_nonce_field('subscription_add_work_log', 'subscription_nonce'); ?> 115 <input type="hidden" name="user_id" value="<?php echo esc_attr($user_id); ?>"> 116 525 526 <?php 527 wp_nonce_field( 528 'subscription_add_work_log', 529 'subscription_nonce' 530 ); 531 ?> 532 533 <input 534 type="hidden" 535 name="user_id" 536 value="<?php echo esc_attr($user_id); ?>" 537 > 538 117 539 <div class="form-field"> 118 <label for="channel_id"><?php echo esc_html__('Channel', 'subscription-system'); ?> <span class="required">*</span></label> 119 <select id="channel_id" name="channel_id" required> 120 <option value=""><?php echo esc_html__('-- Select Channel --', 'subscription-system'); ?></option> 121 <?php foreach ($channels as $channel): ?> 122 <?php if ($channel->status === 'active'): ?> 123 <option value="<?php echo esc_attr($channel->id); ?>"><?php echo esc_html($channel->name); ?></option> 540 <label for="channel_id"> 541 <?php echo esc_html__( 542 'Channel', 543 'subscription-system' 544 ); ?> 545 <span class="required">*</span> 546 </label> 547 548 <select 549 id="channel_id" 550 name="channel_id" 551 required 552 > 553 <option value=""> 554 <?php echo esc_html__( 555 '-- Select Channel --', 556 'subscription-system' 557 ); ?> 558 </option> 559 560 <?php foreach ($channels as $channel) : ?> 561 <?php if ( 562 'active' === $channel->status 563 ) : ?> 564 <option 565 value="<?php echo esc_attr( 566 $channel->id 567 ); ?>" 568 > 569 <?php echo esc_html( 570 $channel->name 571 ); ?> 572 </option> 124 573 <?php endif; ?> 125 574 <?php endforeach; ?> 126 575 </select> 127 576 </div> 128 577 129 578 <div class="form-field"> 130 <label for="work_item_id"><?php echo esc_html__('Work Item', 'subscription-system'); ?> <span class="required">*</span></label> 131 <select id="work_item_id" name="work_item_id" required disabled> 132 <option value=""><?php echo esc_html__('-- Select Channel First --', 'subscription-system'); ?></option> 579 <label for="work_item_id"> 580 <?php echo esc_html__( 581 'Work Item', 582 'subscription-system' 583 ); ?> 584 <span class="required">*</span> 585 </label> 586 587 <select 588 id="work_item_id" 589 name="work_item_id" 590 required 591 disabled 592 > 593 <option value=""> 594 <?php echo esc_html__( 595 '-- Select Channel First --', 596 'subscription-system' 597 ); ?> 598 </option> 133 599 </select> 134 600 </div> 135 601 136 602 <div class="form-field"> 137 <label for="importance"><?php echo esc_html__('Importance', 'subscription-system'); ?></label> 138 <textarea id="importance" name="importance" rows="3"></textarea> 603 <label for="importance"> 604 <?php echo esc_html__( 605 'Importance', 606 'subscription-system' 607 ); ?> 608 </label> 609 610 <textarea 611 id="importance" 612 name="importance" 613 rows="3" 614 ></textarea> 139 615 </div> 140 616 141 617 <div class="form-field"> 142 <label for="notes"><?php echo esc_html__('Notes', 'subscription-system'); ?></label> 143 <?php 144 $editor_settings = array( 145 'textarea_name' => 'notes', 146 'textarea_rows' => 10, 147 'media_buttons' => true, 148 'teeny' => true, 149 'quicktags' => true, 618 <label for="notes"> 619 <?php echo esc_html__( 620 'Notes', 621 'subscription-system' 622 ); ?> 623 </label> 624 625 <?php 626 wp_editor( 627 '', 628 'notes', 629 array( 630 'textarea_name' => 'notes', 631 'textarea_rows' => 8, 632 'media_buttons' => true, 633 'teeny' => true, 634 'quicktags' => true, 635 ) 150 636 ); 151 wp_editor('', 'notes', $editor_settings);152 637 ?> 153 638 </div> 154 639 155 640 <div class="form-field"> 156 <label for="performed_at"><?php echo esc_html__('Performed At', 'subscription-system'); ?></label> 157 <input type="text" id="performed_at" name="performed_at" class="datetimepicker" value="<?php echo esc_attr(date_i18n('Y-m-d H:i')); ?>" autocomplete="off"> 641 <label for="performed_at"> 642 <?php echo esc_html__( 643 'Performed At', 644 'subscription-system' 645 ); ?> 646 </label> 647 648 <input 649 type="text" 650 id="performed_at" 651 name="performed_at" 652 class="datetimepicker" 653 value="<?php echo esc_attr( 654 current_time('Y-m-d H:i') 655 ); ?>" 656 autocomplete="off" 657 > 158 658 </div> 159 659 160 660 <p class="submit"> 161 <input type="submit" name="add_work_log" class="button button-primary" value="<?php echo esc_attr__('Add Work Log', 'subscription-system'); ?>"> 661 <input 662 type="submit" 663 name="add_work_log" 664 class="button button-primary" 665 value="<?php echo esc_attr__( 666 'Add Work Log', 667 'subscription-system' 668 ); ?>" 669 > 162 670 </p> 671 163 672 </form> 164 </div> 165 166 <div class="subscription-admin-card" id="edit-work-log-form" style="display: none;"> 167 <h2><?php echo esc_html__('Edit Work Log', 'subscription-system'); ?></h2> 168 169 <form method="post" action=""> 170 <?php wp_nonce_field('subscription_edit_work_log', 'subscription_nonce'); ?> 171 <input type="hidden" id="edit_work_log_id" name="work_log_id"> 172 173 <div class="form-field"> 174 <label for="edit_channel_id"><?php echo esc_html__('Channel', 'subscription-system'); ?> <span class="required">*</span></label> 175 <select id="edit_channel_id" name="channel_id" required> 176 <option value=""><?php echo esc_html__('-- Select Channel --', 'subscription-system'); ?></option> 177 <?php foreach ($channels as $channel): ?> 178 <?php if ($channel->status === 'active'): ?> 179 <option value="<?php echo esc_attr($channel->id); ?>"><?php echo esc_html($channel->name); ?></option> 180 <?php endif; ?> 181 <?php endforeach; ?> 182 </select> 183 </div> 184 185 <div class="form-field"> 186 <label for="edit_work_item_id"><?php echo esc_html__('Work Item', 'subscription-system'); ?> <span class="required">*</span></label> 187 <select id="edit_work_item_id" name="work_item_id" required> 188 <option value=""><?php echo esc_html__('-- Select Channel First --', 'subscription-system'); ?></option> 189 </select> 190 </div> 191 192 <div class="form-field"> 193 <label for="edit_importance"><?php echo esc_html__('Importance', 'subscription-system'); ?></label> 194 <textarea id="edit_importance" name="importance" rows="3"></textarea> 195 </div> 196 197 <div class="form-field"> 198 <label for="edit_notes"><?php echo esc_html__('Notes', 'subscription-system'); ?></label> 199 <?php 200 $editor_settings = array( 673 674 <?php endif; ?> 675 676 </div> 677 678 <div 679 class="subscription-admin-card" 680 id="edit-work-log-form" 681 style="display: none;" 682 > 683 <h2> 684 <?php echo esc_html__( 685 'Edit Work Log', 686 'subscription-system' 687 ); ?> 688 </h2> 689 690 <form method="post" action=""> 691 692 <?php 693 wp_nonce_field( 694 'subscription_edit_work_log', 695 'subscription_nonce' 696 ); 697 ?> 698 699 <input 700 type="hidden" 701 id="edit_work_log_id" 702 name="work_log_id" 703 > 704 705 <div class="form-field"> 706 <label for="edit_channel_id"> 707 <?php echo esc_html__( 708 'Channel', 709 'subscription-system' 710 ); ?> 711 <span class="required">*</span> 712 </label> 713 714 <select 715 id="edit_channel_id" 716 name="channel_id" 717 required 718 > 719 <option value=""> 720 <?php echo esc_html__( 721 '-- Select Channel --', 722 'subscription-system' 723 ); ?> 724 </option> 725 726 <?php foreach ($channels as $channel) : ?> 727 <?php if ( 728 'active' === $channel->status 729 ) : ?> 730 <option 731 value="<?php echo esc_attr( 732 $channel->id 733 ); ?>" 734 > 735 <?php echo esc_html( 736 $channel->name 737 ); ?> 738 </option> 739 <?php endif; ?> 740 <?php endforeach; ?> 741 </select> 742 </div> 743 744 <div class="form-field"> 745 <label for="edit_work_item_id"> 746 <?php echo esc_html__( 747 'Work Item', 748 'subscription-system' 749 ); ?> 750 <span class="required">*</span> 751 </label> 752 753 <select 754 id="edit_work_item_id" 755 name="work_item_id" 756 required 757 > 758 <option value=""> 759 <?php echo esc_html__( 760 '-- Select Channel First --', 761 'subscription-system' 762 ); ?> 763 </option> 764 </select> 765 </div> 766 767 <div class="form-field"> 768 <label for="edit_importance"> 769 <?php echo esc_html__( 770 'Importance', 771 'subscription-system' 772 ); ?> 773 </label> 774 775 <textarea 776 id="edit_importance" 777 name="importance" 778 rows="3" 779 ></textarea> 780 </div> 781 782 <div class="form-field"> 783 <label for="edit_notes"> 784 <?php echo esc_html__( 785 'Notes', 786 'subscription-system' 787 ); ?> 788 </label> 789 790 <?php 791 wp_editor( 792 '', 793 'edit_notes', 794 array( 201 795 'textarea_name' => 'notes', 202 'textarea_rows' => 10,796 'textarea_rows' => 8, 203 797 'media_buttons' => true, 204 'teeny' => true, 205 'quicktags' => true, 206 ); 207 wp_editor('', 'edit_notes', $editor_settings); 208 ?> 209 </div> 210 211 <div class="form-field"> 212 <label for="edit_performed_at"><?php echo esc_html__('Performed At', 'subscription-system'); ?></label> 213 <input type="text" id="edit_performed_at" name="performed_at" class="datetimepicker" autocomplete="off"> 214 </div> 215 216 <p class="submit"> 217 <input type="submit" name="edit_work_log" class="button button-primary" value="<?php echo esc_attr__('Update Work Log', 'subscription-system'); ?>"> 218 <button type="button" class="button cancel-edit"><?php echo esc_html__('Cancel', 'subscription-system'); ?></button> 219 </p> 220 </form> 221 </div> 222 223 <div id="notes-modal" class="modal" style="display: none;"> 224 <div class="modal-content"> 225 <span class="close">×</span> 226 <h3><?php echo esc_html__('Full Notes', 'subscription-system'); ?></h3> 227 <div id="full-notes-content"></div> 798 'teeny' => true, 799 'quicktags' => true, 800 ) 801 ); 802 ?> 228 803 </div> 804 805 <div class="form-field"> 806 <label for="edit_performed_at"> 807 <?php echo esc_html__( 808 'Performed At', 809 'subscription-system' 810 ); ?> 811 </label> 812 813 <input 814 type="text" 815 id="edit_performed_at" 816 name="performed_at" 817 class="datetimepicker" 818 autocomplete="off" 819 > 820 </div> 821 822 <p class="submit"> 823 <input 824 type="submit" 825 name="edit_work_log" 826 class="button button-primary" 827 value="<?php echo esc_attr__( 828 'Update Work Log', 829 'subscription-system' 830 ); ?>" 831 > 832 833 <button 834 type="button" 835 class="button cancel-edit" 836 > 837 <?php echo esc_html__( 838 'Cancel', 839 'subscription-system' 840 ); ?> 841 </button> 842 </p> 843 844 </form> 845 </div> 846 847 <div 848 id="notes-modal" 849 class="modal" 850 style="display: none;" 851 > 852 <div class="modal-content"> 853 <button 854 type="button" 855 class="close" 856 aria-label="<?php echo esc_attr__( 857 'Close', 858 'subscription-system' 859 ); ?>" 860 > 861 × 862 </button> 863 864 <h3> 865 <?php echo esc_html__( 866 'Full Notes', 867 'subscription-system' 868 ); ?> 869 </h3> 870 871 <div id="full-notes-content"></div> 229 872 </div> 230 873 </div> 874 231 875 </div> 876 </div> 232 877 </div> 233 878 -
subscription-system/tags/1.0.20/public/css/interest-subscribe.css
r3598150 r3633440 33 33 justify-content: center; 34 34 padding: 20px; 35 box-sizing: border-box; 35 36 } 36 37 … … 45 46 } 46 47 48 .ss-interest-modal__dialog, 49 .ss-interest-modal__dialog *, 50 .ss-interest-modal__dialog *::before, 51 .ss-interest-modal__dialog *::after { 52 box-sizing: border-box; 53 } 54 47 55 .ss-interest-modal__dialog { 48 56 position: relative; 57 display: flex; 58 flex-direction: column; 49 59 width: 100%; 50 60 max-width: 560px; 51 max-height: 90vh;61 max-height: calc(100dvh - 40px); 52 62 overflow: hidden; 53 63 background: #fff; 54 64 border-radius: 16px; 55 65 box-shadow: 0 24px 60px rgba(15, 23, 42, 0.25); 56 display: flex;57 flex-direction: column;58 66 } 59 67 … … 64 72 padding: 20px 24px; 65 73 border-bottom: 1px solid #e5e7eb; 74 flex: 0 0 auto; 66 75 } 67 76 … … 82 91 83 92 .ss-interest-modal__body { 93 flex: 1 1 auto; 84 94 padding: 20px 24px; 95 overflow-x: hidden; 85 96 overflow-y: auto; 97 min-height: 0; 98 -webkit-overflow-scrolling: touch; 99 overscroll-behavior: contain; 100 } 101 102 .ss-interest-form { 103 display: flex; 104 flex-direction: column; 105 flex: 1 1 auto; 106 min-height: 0; 107 overflow: hidden; 86 108 } 87 109 … … 232 254 .ss-interest-modal__footer { 233 255 display: flex; 256 flex-direction: column; 257 gap: 12px; 258 padding: 16px 24px 20px; 259 border-top: 1px solid #e5e7eb; 260 flex: 0 0 auto; 261 background: #fff; 262 } 263 264 .ss-interest-modal__footer-actions { 265 display: flex; 266 flex-direction: row; 234 267 justify-content: flex-end; 268 align-items: stretch; 235 269 gap: 10px; 236 padding: 16px 24px 24px; 237 border-top: 1px solid #e5e7eb; 270 width: 100%; 271 } 272 273 .ss-interest-modal__footer-actions .ss-interest-btn { 274 display: inline-flex; 275 align-items: center; 276 justify-content: center; 277 width: auto; 278 max-width: none; 279 min-width: 110px; 280 flex: 0 0 auto; 281 margin: 0; 282 white-space: nowrap; 283 float: none; 284 clear: none; 285 } 286 287 .ss-interest-modal__footer-actions button.ss-interest-btn, 288 .ss-interest-modal__footer-actions a.ss-interest-btn { 289 display: inline-flex; 290 width: auto; 238 291 } 239 292 … … 266 319 } 267 320 321 .ss-interest-modal__cooldown-hint { 322 margin: 0; 323 padding: 10px 12px; 324 border-radius: 8px; 325 background: #f8fafc; 326 border: 1px solid #e2e8f0; 327 color: #475569; 328 font-size: 13px; 329 line-height: 1.5; 330 } 331 268 332 @media (max-width: 600px) { 333 .ss-interest-modal { 334 padding: 0; 335 align-items: flex-end; 336 } 337 269 338 .ss-interest-modal__dialog { 270 max-height: 95vh; 339 max-width: none; 340 max-height: 92dvh; 341 border-radius: 16px 16px 0 0; 342 } 343 344 .ss-interest-modal__header { 345 padding: 16px 18px; 346 } 347 348 .ss-interest-modal__header h2 { 349 font-size: 18px; 350 } 351 352 .ss-interest-modal__body { 353 padding: 16px 18px; 354 } 355 356 .ss-interest-modal__footer { 357 padding: 14px 18px calc(14px + env(safe-area-inset-bottom, 0px)); 358 } 359 360 .ss-interest-modal__footer-actions { 361 flex-direction: row; 362 } 363 364 .ss-interest-modal__footer-actions .ss-interest-btn { 365 flex: 1 1 0; 366 min-width: 0; 271 367 } 272 368 273 369 .ss-interest-checkboxes { 274 370 grid-template-columns: 1fr; 371 max-height: 140px; 372 } 373 374 .ss-interest-guest { 375 padding: 28px 20px 20px; 275 376 } 276 377 } … … 346 447 padding: 40px 32px 28px; 347 448 gap: 12px; 449 flex: 1 1 auto; 450 min-height: 0; 451 overflow-x: hidden; 452 overflow-y: auto; 453 -webkit-overflow-scrolling: touch; 348 454 } 349 455 … … 370 476 .ss-interest-guest__footer { 371 477 justify-content: center; 372 gap: 10px; 478 } 479 480 .ss-interest-guest__footer .ss-interest-modal__footer-actions { 481 justify-content: center; 373 482 } 374 483 -
subscription-system/tags/1.0.20/public/js/interest-subscribe.js
r3598150 r3633440 3 3 4 4 var vars = (typeof ssInterestVars !== 'undefined') ? ssInterestVars : {}; 5 var DISMISS_KEY = 'ss_interest_modal_dismissed_at'; 5 6 6 7 function getSelectedCount($container) { … … 56 57 } 57 58 59 function getCooldownMs() { 60 return parseInt(vars.auto_open_cooldown_ms, 10) || 0; 61 } 62 63 function getDismissedAt() { 64 return parseInt(localStorage.getItem(DISMISS_KEY), 10) || 0; 65 } 66 67 function getRemainingCooldownMs() { 68 var cooldownMs = getCooldownMs(); 69 var dismissedAt = getDismissedAt(); 70 if (cooldownMs <= 0 || !dismissedAt) { 71 return 0; 72 } 73 return Math.max(0, cooldownMs - (Date.now() - dismissedAt)); 74 } 75 76 function wasRecentlyDismissed() { 77 var cooldownMs = getCooldownMs(); 78 if (cooldownMs <= 0) { 79 return false; 80 } 81 82 var remainingMs = getRemainingCooldownMs(); 83 if (remainingMs > 0) { 84 return true; 85 } 86 87 if (getDismissedAt()) { 88 localStorage.removeItem(DISMISS_KEY); 89 } 90 return false; 91 } 92 93 function recordDismissal() { 94 if (getCooldownMs() <= 0) { 95 return; 96 } 97 localStorage.setItem(DISMISS_KEY, String(Date.now())); 98 } 99 100 function dismissModal($modal) { 101 recordDismissal(); 102 closeModal($modal); 103 } 104 58 105 // ── Helper: remove a query param from the current URL without reload ── 59 106 function removeQueryParam(param) { … … 78 125 openModal($targetModal); 79 126 } 80 } else if (vars.auto_open ) {127 } else if (vars.auto_open && !wasRecentlyDismissed()) { 81 128 var delay = parseInt(vars.auto_open_delay, 10) || 0; 82 129 setTimeout(function () { … … 96 143 97 144 $(document).on('click', '[data-ss-close-modal]', function () { 98 closeModal($(this).closest('.ss-interest-modal'));145 dismissModal($(this).closest('.ss-interest-modal')); 99 146 }); 100 147 101 148 $(document).on('keydown', function (e) { 102 149 if (e.key === 'Escape') { 103 closeModal($('.ss-interest-modal.is-open')); 150 var $open = $('.ss-interest-modal.is-open'); 151 if ($open.length) { 152 dismissModal($open); 153 } 104 154 } 105 155 }); -
subscription-system/tags/1.0.20/public/partials/interest-subscribe-modal.php
r3598150 r3633440 22 22 $prefill_email = $is_logged_in ? $current_user->user_email : ''; 23 23 $hide_fields = $is_logged_in && (bool) get_option( 'subs_modal_hide_fields_logged_in', 1 ); 24 $cooldown_value = absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 25 $cooldown_label = ( $cooldown_value > 0 && function_exists( 'subs_sys_get_modal_cooldown_label' ) ) 26 ? subs_sys_get_modal_cooldown_label() 27 : ''; 28 $cooldown_hint_text = $cooldown_label 29 ? sprintf( 30 /* translators: %s: human-readable time period such as "2 weeks" */ 31 __( 'If you close this, we won\'t show it again for %s.', 'subscription-system' ), 32 $cooldown_label 33 ) 34 : ''; 24 35 25 36 // Build the login-redirect URL: after login, return here with ?ss_subscribe=1 so the modal auto-reopens. … … 31 42 <div id="<?php echo $modal_id; ?>" class="ss-interest-modal" aria-hidden="true" role="dialog" aria-labelledby="<?php echo $modal_id; ?>-title"> 32 43 <div class="ss-interest-modal__overlay" data-ss-close-modal></div> 33 <div class="ss-interest-modal__dialog ">44 <div class="ss-interest-modal__dialog<?php echo $is_logged_in ? ' ss-interest-modal__dialog--logged-in' : ' ss-interest-modal__dialog--guest'; ?>"> 34 45 <div class="ss-interest-modal__header"> 35 46 <h2 id="<?php echo $modal_id; ?>-title"><?php esc_html_e( 'Subscribe to Updates', 'subscription-system' ); ?></h2> … … 54 65 </div> 55 66 <div class="ss-interest-modal__footer ss-interest-guest__footer"> 67 <?php if ( $cooldown_hint_text ) : ?> 68 <p class="ss-interest-modal__cooldown-hint"><?php echo esc_html( $cooldown_hint_text ); ?></p> 69 <?php endif; ?> 70 <div class="ss-interest-modal__footer-actions"> 56 71 <button type="button" class="ss-interest-btn ss-interest-btn--ghost" data-ss-close-modal> 57 72 <?php esc_html_e( 'Not now', 'subscription-system' ); ?> … … 60 75 <?php esc_html_e( 'Log in to subscribe', 'subscription-system' ); ?> 61 76 </a> 77 </div> 62 78 </div> 63 79 … … 227 243 228 244 <div class="ss-interest-modal__footer"> 245 <?php if ( $cooldown_hint_text ) : ?> 246 <p class="ss-interest-modal__cooldown-hint"><?php echo esc_html( $cooldown_hint_text ); ?></p> 247 <?php endif; ?> 248 <div class="ss-interest-modal__footer-actions"> 229 249 <button type="button" class="ss-interest-btn ss-interest-btn--secondary" data-ss-close-modal> 230 250 <?php esc_html_e( 'Cancel', 'subscription-system' ); ?> … … 233 253 <?php esc_html_e( 'Subscribe', 'subscription-system' ); ?> 234 254 </button> 255 </div> 235 256 </div> 236 257 </form> -
subscription-system/tags/1.0.20/subscription-system.php
r3598150 r3633440 17 17 * Plugin URI: https://mediahubster.in/subscription-system 18 18 * Description: A comprehensive subscription management system for WordPress. 19 * Version: 1.0. 1819 * Version: 1.0.20 20 20 * Author: Joe Youngblood 21 21 * Author URI: https://mediahubster.in … … 97 97 98 98 // Define plugin constants 99 define('SUBS_SYS_VERSION', '1.0. 18');99 define('SUBS_SYS_VERSION', '1.0.20'); 100 100 define('SUBS_SYS_PLUGIN_DIR', plugin_dir_path(__FILE__)); 101 101 define('SUBS_SYS_PLUGIN_URL', plugin_dir_url(__FILE__)); 102 102 define('SUBS_SYS_PLUGIN_BASENAME', plugin_basename(__FILE__)); 103 104 /** 105 * Valid cooldown units for the subscribe modal re-show delay. 106 * 107 * @return string[] 108 */ 109 function subs_sys_get_modal_cooldown_units() { 110 return array( 'minutes', 'days', 'weeks', 'months' ); 111 } 112 113 /** 114 * Convert modal cooldown settings to milliseconds for frontend use. 115 * 116 * @param int|null $value Cooldown amount. 117 * @param string|null $unit Cooldown unit. 118 * @return int 119 */ 120 function subs_sys_get_modal_cooldown_ms( $value = null, $unit = null ) { 121 $value = null !== $value ? absint( $value ) : absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 122 $unit = null !== $unit ? sanitize_key( $unit ) : sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 123 124 if ( $value <= 0 ) { 125 return 0; 126 } 127 128 $seconds_per_unit = array( 129 'minutes' => MINUTE_IN_SECONDS, 130 'days' => DAY_IN_SECONDS, 131 'weeks' => WEEK_IN_SECONDS, 132 'months' => 30 * DAY_IN_SECONDS, 133 ); 134 135 if ( ! isset( $seconds_per_unit[ $unit ] ) ) { 136 $unit = 'minutes'; 137 } 138 139 return $value * $seconds_per_unit[ $unit ] * 1000; 140 } 141 142 /** 143 * Human-readable label for the modal cooldown period. 144 * 145 * @param int|null $value Cooldown amount. 146 * @param string|null $unit Cooldown unit. 147 * @return string 148 */ 149 function subs_sys_get_modal_cooldown_label( $value = null, $unit = null ) { 150 $value = null !== $value ? absint( $value ) : absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 151 $unit = null !== $unit ? sanitize_key( $unit ) : sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 152 153 if ( $value <= 0 ) { 154 return ''; 155 } 156 157 switch ( $unit ) { 158 case 'days': 159 /* translators: %d: number of days */ 160 return sprintf( _n( '%d day', '%d days', $value, 'subscription-system' ), $value ); 161 case 'weeks': 162 /* translators: %d: number of weeks */ 163 return sprintf( _n( '%d week', '%d weeks', $value, 'subscription-system' ), $value ); 164 case 'months': 165 /* translators: %d: number of months */ 166 return sprintf( _n( '%d month', '%d months', $value, 'subscription-system' ), $value ); 167 default: 168 /* translators: %d: number of minutes */ 169 return sprintf( _n( '%d minute', '%d minutes', $value, 'subscription-system' ), $value ); 170 } 171 } 103 172 104 173 /** … … 141 210 'ajax_url' => admin_url( 'admin-ajax.php' ), 142 211 'nonce' => wp_create_nonce( 'interest_subscription_nonce' ), 143 'auto_open' => (int) get_option( 'subs_modal_auto_open', 0 ), 144 'auto_open_delay' => absint( get_option( 'subs_modal_auto_open_delay', 3 ) ), 145 'hide_fields_logged_in' => (int) get_option( 'subs_modal_hide_fields_logged_in', 1 ), 146 'is_logged_in' => $is_logged_in ? 1 : 0, 147 'i18n' => array( 212 'auto_open' => (int) get_option( 'subs_modal_auto_open', 0 ), 213 'auto_open_delay' => absint( get_option( 'subs_modal_auto_open_delay', 3 ) ), 214 'auto_open_cooldown' => absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ), 215 'auto_open_cooldown_unit' => sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ), 216 'auto_open_cooldown_ms' => subs_sys_get_modal_cooldown_ms(), 217 'auto_open_cooldown_label' => subs_sys_get_modal_cooldown_label(), 218 'hide_fields_logged_in' => (int) get_option( 'subs_modal_hide_fields_logged_in', 1 ), 219 'is_logged_in' => $is_logged_in ? 1 : 0, 220 'i18n' => array( 148 221 'processing' => __( 'Processing...', 'subscription-system' ), 149 222 'submit' => __( 'Subscribe', 'subscription-system' ), -
subscription-system/trunk/README.txt
r3598150 r3633440 6 6 Tested up to: 7.0 7 7 Requires PHP: 8.0 8 Stable tag: 1.0. 188 Stable tag: 1.0.20 9 9 License: GPLv2 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 85 85 86 86 == Changelog == 87 88 = 1.0.20 = 89 * Subscribe modal: added configurable re-show delay after close (minutes, days, weeks, months) 90 * Subscribe modal: fixed responsive layout with scrollable content and always-visible footer buttons 91 * Subscribe modal: removed post-close cooldown banners for a less intrusive experience 87 92 88 93 = 1.0.18 = … … 257 262 == Upgrade Notice == 258 263 264 = 1.0.20 = 265 Improves the subscribe modal with a configurable re-show delay after close, responsive scrollable layout, and removes intrusive post-close notifications. 266 259 267 = 1.0.18 = 260 268 Major feature update. Introduces the Subscribe Button & Modal system with floating buttons, auto-open, and context-aware category/tag subscriptions. Content Plans now power the modal with strict per-post plan filtering, Stripe sync, and a full Stripe Checkout payment flow. Guest users see a "Log in / Not now" prompt. Includes plan conflict prevention, improved plan cards, and a polished post-payment success page. -
subscription-system/trunk/admin/class-subs-sys-admin.php
r3598150 r3633440 102 102 103 103 // 7. Channels 104 //$this->add_channels_submenu();104 $this->add_channels_submenu(); 105 105 106 106 // 8. Work Items … … 468 468 */ 469 469 public function render_dashboard_page() { 470 // Get subscription statistics 470 if (!current_user_can('manage_options')) { 471 wp_die( 472 esc_html__( 473 'You do not have permission to manage subscriptions.', 474 'subscription-system' 475 ) 476 ); 477 } 478 479 $action = isset($_GET['action']) 480 ? sanitize_key(wp_unslash($_GET['action'])) 481 : ''; 482 483 /* 484 * Open the manual subscription form when the URL contains: 485 * admin.php?page=subscription-dashboard&action=add 486 */ 487 if ('add' === $action) { 488 $plans = $this->subscription_manager->get_subscription_plans(); 489 490 $users = get_users( 491 array( 492 'orderby' => 'display_name', 493 'order' => 'ASC', 494 ) 495 ); 496 497 include plugin_dir_path(dirname(__FILE__)) 498 . 'admin/partials/subscription-system-admin-add.php'; 499 500 return; 501 } 502 503 /* 504 * Otherwise, show the normal dashboard. 505 */ 471 506 $active_subscriptions = $this->get_subscription_count('active'); 472 507 $paused_subscriptions = $this->get_subscription_count('paused'); 473 508 $total_revenue = $this->get_total_revenue(); 474 475 // Include the dashboard template476 include plugin_dir_path(dirname(__FILE__)). 'admin/partials/dashboard-display.php';509 510 include plugin_dir_path(dirname(__FILE__)) 511 . 'admin/partials/dashboard-display.php'; 477 512 } 478 513 … … 1115 1150 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open', array( 'sanitize_callback' => 'absint', 'default' => 0 ) ); 1116 1151 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_delay', array( 'sanitize_callback' => 'absint', 'default' => 3 ) ); 1152 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_cooldown', array( 'sanitize_callback' => 'absint', 'default' => 60 ) ); 1153 register_setting( 'subscription_modal_settings', 'subs_modal_auto_open_cooldown_unit', array( 1154 'sanitize_callback' => function( $val ) { 1155 $val = sanitize_key( $val ); 1156 return in_array( $val, subs_sys_get_modal_cooldown_units(), true ) ? $val : 'minutes'; 1157 }, 1158 'default' => 'minutes', 1159 ) ); 1117 1160 register_setting( 'subscription_modal_settings', 'subs_modal_floating_btn', array( 'sanitize_callback' => 'absint', 'default' => 0 ) ); 1118 1161 register_setting( 'subscription_modal_settings', 'subs_modal_floating_btn_text', array( 'sanitize_callback' => 'sanitize_text_field', 'default' => 'Subscribe' ) ); … … 1429 1472 */ 1430 1473 public function process_forms() { 1431 // Check if we're on the right page 1432 if (!isset($_GET['post_type']) || $_GET['post_type'] !== 'subscription') { 1433 return; 1434 } 1435 1436 // Process add plan form 1437 if (isset($_POST['add_plan_submit']) && isset($_POST['add_plan_nonce'])) { 1438 // Verify nonce 1439 if (!wp_verify_nonce($_POST['add_plan_nonce'], 'add_plan_nonce')) { 1440 wp_die(__('Security check failed', 'subscription-system')); 1441 } 1442 1443 // Get form data 1444 $raw_features = isset( $_POST['plan_features_json'] ) ? sanitize_text_field( wp_unslash( $_POST['plan_features_json'] ) ) : ''; 1445 $features_arr = json_decode( $raw_features, true ); 1446 if ( ! is_array( $features_arr ) ) { 1447 $features_arr = array( 'max_categories' => 0, 'max_tags' => 0 ); 1448 } 1449 $features_arr['max_categories'] = max( 0, (int) ( $features_arr['max_categories'] ?? 0 ) ); 1450 $features_arr['max_tags'] = max( 0, (int) ( $features_arr['max_tags'] ?? 0 ) ); 1451 1452 $plan_data = array( 1453 'plan_name' => isset($_POST['plan_name']) ? sanitize_text_field($_POST['plan_name']) : '', 1454 'description' => isset($_POST['description']) ? wp_kses_post($_POST['description']) : '', 1455 'price' => isset($_POST['price']) ? floatval($_POST['price']) : 0, 1456 'duration' => isset($_POST['duration']) ? intval($_POST['duration']) : 30, 1457 'category_id' => isset($_POST['category_id']) ? intval($_POST['category_id']) : 0, 1458 'status' => isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'active', 1459 'features' => wp_json_encode( $features_arr ), 1474 $page = isset($_GET['page']) 1475 ? sanitize_key(wp_unslash($_GET['page'])) 1476 : ''; 1477 1478 $action = isset($_GET['action']) 1479 ? sanitize_key(wp_unslash($_GET['action'])) 1480 : ''; 1481 1482 /* 1483 * Process the manual Add Subscription form. 1484 */ 1485 if ( 1486 'subscription-dashboard' === $page && 1487 'add' === $action && 1488 isset($_POST['ss_add_subscription']) 1489 ) { 1490 if (!current_user_can('manage_options')) { 1491 wp_die( 1492 esc_html__( 1493 'You do not have permission to create subscriptions.', 1494 'subscription-system' 1495 ) 1496 ); 1497 } 1498 1499 check_admin_referer('ss_add_subscription', 'ss_nonce'); 1500 1501 $user_id = isset($_POST['user_id']) 1502 ? absint($_POST['user_id']) 1503 : 0; 1504 1505 $plan_id = isset($_POST['plan_id']) 1506 ? absint($_POST['plan_id']) 1507 : 0; 1508 1509 $status = isset($_POST['status']) 1510 ? sanitize_key(wp_unslash($_POST['status'])) 1511 : 'active'; 1512 1513 $start_date_input = isset($_POST['start_date']) 1514 ? sanitize_text_field(wp_unslash($_POST['start_date'])) 1515 : ''; 1516 1517 /* 1518 * Store an error temporarily and return to the form. 1519 */ 1520 $error_redirect = static function ($message) { 1521 set_transient( 1522 'subs_sys_manual_subscription_error_' . get_current_user_id(), 1523 sanitize_text_field($message), 1524 60 1525 ); 1526 1527 wp_safe_redirect( 1528 add_query_arg( 1529 array( 1530 'page' => 'subscription-dashboard', 1531 'action' => 'add', 1532 'subscription_error' => '1', 1533 ), 1534 admin_url('admin.php') 1535 ) 1536 ); 1537 1538 exit; 1539 }; 1540 1541 /* 1542 * Validate the selected WordPress user. 1543 */ 1544 $user = get_userdata($user_id); 1545 1546 if (!$user) { 1547 $error_redirect( 1548 __('Please select a valid WordPress user.', 'subscription-system') 1549 ); 1550 } 1551 1552 /* 1553 * Validate the selected subscription plan. 1554 */ 1555 $plan = $this->subscription_manager->get_plan($plan_id); 1556 1557 if (!$plan) { 1558 $error_redirect( 1559 __('Please select a valid subscription plan.', 'subscription-system') 1560 ); 1561 } 1562 1563 /* 1564 * Only permit known subscription statuses. 1565 */ 1566 $allowed_statuses = array( 1567 'active', 1568 'paused', 1569 'cancelled', 1570 'expired', 1460 1571 ); 1461 1462 // Add the plan 1463 $result = $this->add_plan($plan_data); 1464 1572 1573 if (!in_array($status, $allowed_statuses, true)) { 1574 $status = 'active'; 1575 } 1576 1577 /* 1578 * Convert the datetime-local form value to MySQL format. 1579 */ 1580 $start_date_object = DateTimeImmutable::createFromFormat( 1581 'Y-m-d\TH:i', 1582 $start_date_input, 1583 wp_timezone() 1584 ); 1585 1586 if (false === $start_date_object) { 1587 $error_redirect( 1588 __('Please enter a valid start date.', 'subscription-system') 1589 ); 1590 } 1591 1592 $start_date = $start_date_object->format('Y-m-d H:i:s'); 1593 1594 /* 1595 * Prevent duplicate active or paused subscriptions for the 1596 * same user and plan. 1597 */ 1598 global $wpdb; 1599 1600 $subscriptions_table = $wpdb->prefix . 'subscriptions'; 1601 1602 $existing_subscription = $wpdb->get_var( 1603 $wpdb->prepare( 1604 "SELECT id 1605 FROM {$subscriptions_table} 1606 WHERE user_id = %d 1607 AND plan_id = %d 1608 AND status IN ('active', 'paused') 1609 LIMIT 1", 1610 $user_id, 1611 $plan_id 1612 ) 1613 ); 1614 1615 if ($existing_subscription) { 1616 $error_redirect( 1617 __( 1618 'This user already has an active or paused subscription for this plan.', 1619 'subscription-system' 1620 ) 1621 ); 1622 } 1623 1624 /* 1625 * Create the local manual subscription. 1626 * This does not create or charge a Stripe subscription. 1627 */ 1628 $result = $this->subscription_manager->create_subscription( 1629 $user_id, 1630 $plan_id, 1631 array( 1632 'status' => $status, 1633 'start_date' => $start_date, 1634 'customer_email' => $user->user_email, 1635 'customer_name' => $user->display_name, 1636 'payment_id' => 'manual_admin_' . wp_generate_uuid4(), 1637 ) 1638 ); 1639 1465 1640 if (is_wp_error($result)) { 1466 // Store error message 1467 add_settings_error( 1468 'subscription_system', 1469 'add_plan_error', 1470 $result->get_error_message(), 1471 'error' 1472 ); 1473 } else { 1474 // Store success message 1475 add_settings_error( 1476 'subscription_system', 1477 'add_plan_success', 1478 __('Plan added successfully', 'subscription-system'), 1479 'success' 1480 ); 1481 1482 // Use wp_safe_redirect instead of wp_redirect 1483 wp_safe_redirect(admin_url('edit.php?post_type=subscription&page=subscription-plans')); 1484 exit; 1485 } 1486 } 1487 1488 // Process edit plan form 1489 if (isset($_POST['edit_plan_submit']) && isset($_POST['edit_plan_nonce'])) { 1490 // Verify nonce 1491 if (!wp_verify_nonce($_POST['edit_plan_nonce'], 'edit_plan_nonce')) { 1492 wp_die(__('Security check failed', 'subscription-system')); 1493 } 1494 1495 // Get form data 1496 $plan_id = isset($_POST['plan_id']) ? intval($_POST['plan_id']) : 0; 1497 1498 if ($plan_id <= 0) { 1499 wp_die(__('Invalid plan ID', 'subscription-system')); 1500 } 1501 1502 $raw_features_edit = isset( $_POST['plan_features_json'] ) ? sanitize_text_field( wp_unslash( $_POST['plan_features_json'] ) ) : ''; 1503 $features_arr_edit = json_decode( $raw_features_edit, true ); 1504 if ( ! is_array( $features_arr_edit ) ) { 1505 $features_arr_edit = array( 'max_categories' => 0, 'max_tags' => 0 ); 1506 } 1507 $features_arr_edit['max_categories'] = max( 0, (int) ( $features_arr_edit['max_categories'] ?? 0 ) ); 1508 $features_arr_edit['max_tags'] = max( 0, (int) ( $features_arr_edit['max_tags'] ?? 0 ) ); 1509 1510 $plan_data = array( 1511 'plan_name' => isset($_POST['plan_name']) ? sanitize_text_field($_POST['plan_name']) : '', 1512 'description' => isset($_POST['description']) ? wp_kses_post($_POST['description']) : '', 1513 'price' => isset($_POST['price']) ? floatval($_POST['price']) : 0, 1514 'duration' => isset($_POST['duration']) ? intval($_POST['duration']) : 30, 1515 'category_id' => isset($_POST['category_id']) ? intval($_POST['category_id']) : 0, 1516 'status' => isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'active', 1517 'features' => wp_json_encode( $features_arr_edit ), 1518 'updated_at' => current_time('mysql'), 1641 $error_redirect($result->get_error_message()); 1642 } 1643 1644 wp_safe_redirect( 1645 add_query_arg( 1646 array( 1647 'page' => 'subscription-dashboard', 1648 'action' => 'add', 1649 'subscription_added' => absint($result), 1650 ), 1651 admin_url('admin.php') 1652 ) 1519 1653 ); 1520 1521 // Update the plan 1522 $result = $this->subscription_manager->update_plan($plan_id, $plan_data); 1523 1524 if (is_wp_error($result)) { 1525 // Store error message 1526 add_settings_error( 1527 'subscription_system', 1528 'edit_plan_error', 1529 $result->get_error_message(), 1530 'error' 1531 ); 1532 } else { 1533 // Store success message 1534 add_settings_error( 1535 'subscription_system', 1536 'edit_plan_success', 1537 __('Plan updated successfully', 'subscription-system'), 1538 'success' 1539 ); 1540 1541 // Use wp_safe_redirect instead of wp_redirect 1542 wp_safe_redirect(admin_url('edit.php?post_type=subscription&page=subscription-plans')); 1543 exit; 1544 } 1545 } 1546 1547 // Process developer mode forms 1654 1655 exit; 1656 } 1657 1658 /* 1659 * Keep the plugin's existing Developer Mode processing working. 1660 */ 1548 1661 $this->process_developer_mode_forms(); 1549 1662 } … … 3593 3706 */ 3594 3707 public function render_work_logs_page() { 3595 // Get user ID from URL 3596 $user_id = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0; 3597 3598 // Get current user ID 3708 $is_admin = current_user_can('manage_options'); 3599 3709 $current_user_id = get_current_user_id(); 3600 3601 // Check permissions 3602 // Admin can view any user's logs, non-admin can only view their own logs 3603 if (!current_user_can('manage_options')) { 3604 // If not an admin, ensure user can only view their own logs 3605 if ($user_id != $current_user_id) { 3710 3711 $user_id = isset($_GET['user_id']) 3712 ? absint(wp_unslash($_GET['user_id'])) 3713 : 0; 3714 3715 /* 3716 * These variables are always defined so the template can safely 3717 * display either the subscriber selector or an individual user's logs. 3718 */ 3719 $subscriber_choices = array(); 3720 $show_user_selector = false; 3721 $selection_error = ''; 3722 $user = false; 3723 $work_logs = array(); 3724 $channels = array(); 3725 $work_items = array(); 3726 3727 /* 3728 * Administrators may select any subscriber. 3729 * Only users who actually have at least one subscription are shown. 3730 */ 3731 if ($is_admin) { 3732 $all_subscribers = $this->get_all_subscribers(); 3733 3734 foreach ($all_subscribers as $subscriber) { 3735 if (!empty($subscriber['subscriptions'])) { 3736 $subscriber_choices[] = $subscriber; 3737 } 3738 } 3739 3740 /* 3741 * Opening the Work Logs menu without user_id should show 3742 * the subscriber selector instead of an invalid-user error. 3743 */ 3744 if (0 === $user_id) { 3745 $show_user_selector = true; 3746 } 3747 } else { 3748 /* 3749 * Non-admin users can only view their own work logs. 3750 */ 3751 if ($user_id > 0 && $user_id !== $current_user_id) { 3606 3752 wp_die( 3607 esc_html__('Sorry, you are not allowed to view this user\'s work logs.', 'subscription-system'), 3753 esc_html__( 3754 'Sorry, you are not allowed to view this user\'s work logs.', 3755 'subscription-system' 3756 ), 3608 3757 esc_html__('Permission Denied', 'subscription-system'), 3609 3758 array('response' => 403) 3610 3759 ); 3611 3760 } 3612 } 3613 3614 // If no user_id specified and user is not admin, default to current user 3615 if ($user_id == 0 && !current_user_can('manage_options')) { 3761 3616 3762 $user_id = $current_user_id; 3617 3763 } 3618 3619 // Process work log form submissions (add, edit, delete) 3620 $this->process_work_log_form(); 3621 3622 // Get user information 3623 $user = get_userdata($user_id); 3624 3625 // Get all work logs for this user 3626 $work_logs = $this->get_user_work_logs($user_id); 3627 3628 // Get all channels for dropdown 3629 $channels = $this->get_all_channels(); 3630 3631 // Get all work items (will be filtered by JavaScript based on selected channel) 3632 $work_items = $this->get_all_work_items(); 3633 3634 // Include the work logs template 3635 include plugin_dir_path(dirname(__FILE__)) . 'admin/partials/work-logs-display.php'; 3764 3765 /* 3766 * When a user has been selected, verify that the WordPress user exists. 3767 */ 3768 if (!$show_user_selector) { 3769 $user = get_userdata($user_id); 3770 3771 if (!$user) { 3772 if ($is_admin) { 3773 $show_user_selector = true; 3774 $selection_error = __( 3775 'The selected subscriber could not be found. Please select another subscriber.', 3776 'subscription-system' 3777 ); 3778 } else { 3779 wp_die( 3780 esc_html__( 3781 'Your WordPress user account could not be found.', 3782 'subscription-system' 3783 ) 3784 ); 3785 } 3786 } 3787 } 3788 3789 /* 3790 * Only process and load work logs after a valid user is selected. 3791 */ 3792 if (!$show_user_selector && $user) { 3793 $this->process_work_log_form(); 3794 3795 $work_logs = $this->get_user_work_logs($user_id); 3796 $channels = $this->get_all_channels(); 3797 $work_items = $this->get_all_work_items(); 3798 } 3799 3800 include plugin_dir_path(dirname(__FILE__)) 3801 . 'admin/partials/work-logs-display.php'; 3636 3802 } 3637 3803 -
subscription-system/trunk/admin/partials/dashboard-display.php
r3311076 r3633440 60 60 61 61 <div class="subscription-actions"> 62 <a href="<?php echo esc_url(admin_url(' post-new.php?post_type=subscription')); ?>" class="button button-primary">62 <a href="<?php echo esc_url(admin_url('admin.php?page=subscription-dashboard&action=add')); ?>" class="button button-primary"> 63 63 <?php echo esc_html__('Add New Subscription', 'subscription-system'); ?> 64 64 </a> -
subscription-system/trunk/admin/partials/plan-categories-display.php
r3311076 r3633440 1 1 <?php 2 2 3 /** 3 4 * Provide a admin area view for the plugin … … 204 205 205 206 // Delete category 206 $('.delete-category').on('click', function() {207 /*$('.delete-category').on('click', function() { 207 208 var id = $(this).data('id'); 208 209 var nonce = $(this).data('nonce'); … … 211 212 window.location.href = '<?php echo esc_url(admin_url('edit.php?post_type=subscription&page=subscription-plan-categories&action=delete&category_id=')); ?>' + id + '&_wpnonce=' + nonce; 212 213 } 213 }); 214 });*/ 215 216 // Delete category 217 $('.delete-category').on('click', function() { 218 var id = $(this).data('id'); 219 var nonce = $(this).data('nonce'); 220 221 if (!confirm('<?php echo esc_js(__('Are you sure you want to delete this category?', 'subscription-system')); ?>')) { 222 return; 223 } 224 225 var deleteUrl = new URL( 226 <?php echo wp_json_encode(admin_url('admin.php')); ?> 227 ); 228 229 deleteUrl.searchParams.set('page', 'subscription-plan-categories'); 230 deleteUrl.searchParams.set('action', 'delete'); 231 deleteUrl.searchParams.set('category_id', id); 232 deleteUrl.searchParams.set('_wpnonce', nonce); 233 234 window.location.assign(deleteUrl.toString()); 235 }); 214 236 215 237 // Cancel edit -
subscription-system/trunk/admin/partials/plans-display.php
r3598150 r3633440 500 500 501 501 // Delete plan 502 $('.delete-plan').on('click', function() {502 /* $('.delete-plan').on('click', function() { 503 503 var id = $(this).data('id'); 504 504 var nonce = $(this).data('nonce'); … … 507 507 window.location.href = '<?php echo admin_url('edit.php?post_type=subscription&page=subscription-plans&action=delete&plan_id='); ?>' + id + '&_wpnonce=' + nonce; 508 508 } 509 }); 509 }); */ 510 511 // Delete plan 512 $('.delete-plan').on('click', function(event) { 513 event.preventDefault(); 514 515 var id = $(this).data('id'); 516 var nonce = $(this).data('nonce'); 517 518 if (!confirm('<?php echo esc_js(__('Are you sure you want to delete this plan?', 'subscription-system')); ?>')) { 519 return; 520 } 521 522 /* 523 * Plans are displayed through a custom admin page. 524 * Therefore, the request must use admin.php rather than edit.php. 525 */ 526 var deleteUrl = new URL( 527 <?php echo wp_json_encode(admin_url('admin.php')); ?> 528 ); 529 530 deleteUrl.searchParams.set('page', 'subscription-plans'); 531 deleteUrl.searchParams.set('action', 'delete'); 532 deleteUrl.searchParams.set('plan_id', id); 533 deleteUrl.searchParams.set('_wpnonce', nonce); 534 535 window.location.assign(deleteUrl.toString()); 536 }); 537 510 538 511 539 // Cancel edit -
subscription-system/trunk/admin/partials/subscribe-modal-settings-display.php
r3598150 r3633440 46 46 class="small-text"> 47 47 <p class="description"><?php esc_html_e( 'Seconds to wait before automatically opening the modal (0 = immediate).', 'subscription-system' ); ?></p> 48 </td> 49 </tr> 50 51 <tr> 52 <th scope="row"><?php esc_html_e( 'Re-show After Close', 'subscription-system' ); ?></th> 53 <td> 54 <?php 55 $cooldown_value = absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 56 $cooldown_unit = sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 57 $cooldown_units = array( 58 'minutes' => __( 'Minutes', 'subscription-system' ), 59 'days' => __( 'Days', 'subscription-system' ), 60 'weeks' => __( 'Weeks', 'subscription-system' ), 61 'months' => __( 'Months', 'subscription-system' ), 62 ); 63 ?> 64 <input type="number" name="subs_modal_auto_open_cooldown" min="0" max="999" step="1" 65 value="<?php echo esc_attr( $cooldown_value ); ?>" 66 class="small-text"> 67 <select name="subs_modal_auto_open_cooldown_unit"> 68 <?php foreach ( $cooldown_units as $unit_key => $unit_label ) : ?> 69 <option value="<?php echo esc_attr( $unit_key ); ?>" <?php selected( $cooldown_unit, $unit_key ); ?>> 70 <?php echo esc_html( $unit_label ); ?> 71 </option> 72 <?php endforeach; ?> 73 </select> 74 <p class="description"> 75 <?php esc_html_e( 'After a visitor closes the modal, wait this long before auto-opening it again. Use 0 to auto-open on every page load (no cooldown). Visitors will see this timing in the modal and after closing it.', 'subscription-system' ); ?> 76 </p> 77 <?php if ( $cooldown_value > 0 && function_exists( 'subs_sys_get_modal_cooldown_label' ) ) : ?> 78 <p class="description"> 79 <?php 80 printf( 81 /* translators: %s: human-readable cooldown period */ 82 esc_html__( 'Preview: visitors will see “We won\'t show this again for %s.”', 'subscription-system' ), 83 esc_html( subs_sys_get_modal_cooldown_label( $cooldown_value, $cooldown_unit ) ) 84 ); 85 ?> 86 </p> 87 <?php endif; ?> 48 88 </td> 49 89 </tr> -
subscription-system/trunk/admin/partials/subscription-system-admin-add.php
r3311076 r3633440 1 1 <?php 2 2 /** 3 * Admin add subscription view.3 * Manual Add Subscription screen. 4 4 * 5 * @since 1.0.0 6 * @package Subscription_System 5 * @package Subscription_System 7 6 */ 8 7 9 // If this file is called directly, abort.10 8 if (!defined('ABSPATH')) { 11 9 exit; 12 10 } 11 12 $error_message = ''; 13 14 if (isset($_GET['subscription_error'])) { 15 $error_key = 'subs_sys_manual_subscription_error_' 16 . get_current_user_id(); 17 18 $error_message = get_transient($error_key); 19 20 delete_transient($error_key); 21 } 13 22 ?> 14 23 15 24 <div class="wrap ss-admin-container"> 16 <h1 class="ss-admin-title"><?php esc_html_e('Add Subscription', 'subscription-system'); ?></h1> 17 18 <?php if (isset($success_message)) : ?> 19 <div class="ss-admin-message success"> 20 <?php echo esc_html($success_message); ?> 25 <h1 class="wp-heading-inline"> 26 <?php esc_html_e('Add New Subscription', 'subscription-system'); ?> 27 </h1> 28 29 <a 30 href="<?php echo esc_url(admin_url('admin.php?page=subscription-dashboard')); ?>" 31 class="page-title-action" 32 > 33 <?php esc_html_e('Back to Dashboard', 'subscription-system'); ?> 34 </a> 35 36 <hr class="wp-header-end"> 37 38 <?php if (isset($_GET['subscription_added'])) : ?> 39 <div class="notice notice-success is-dismissible"> 40 <p> 41 <?php esc_html_e( 42 'Subscription created successfully.', 43 'subscription-system' 44 ); ?> 45 </p> 21 46 </div> 22 47 <?php endif; ?> 23 24 <?php if ( isset($error_message)) : ?>25 <div class=" ss-admin-message error">26 < ?php echo esc_html($error_message); ?>48 49 <?php if (!empty($error_message)) : ?> 50 <div class="notice notice-error is-dismissible"> 51 <p><?php echo esc_html($error_message); ?></p> 27 52 </div> 28 53 <?php endif; ?> 29 30 <form method="post" class="ss-admin-form"> 31 <?php wp_nonce_field('ss_add_subscription', 'ss_nonce'); ?> 32 33 <div class="ss-admin-form-row"> 34 <label for="user_id"><?php esc_html_e('User ID', 'subscription-system'); ?></label> 35 <input type="number" id="user_id" name="user_id" required> 36 <p class="description"><?php esc_html_e('Enter the WordPress user ID.', 'subscription-system'); ?></p> 37 </div> 38 39 <div class="ss-admin-form-row"> 40 <label for="email"><?php esc_html_e('Email', 'subscription-system'); ?></label> 41 <input type="email" id="email" name="email" required> 42 </div> 43 44 <div class="ss-admin-form-row"> 45 <label for="plan_id"><?php esc_html_e('Subscription Plan', 'subscription-system'); ?></label> 46 <select id="plan_id" name="plan_id" required> 47 <option value=""><?php esc_html_e('Select a plan', 'subscription-system'); ?></option> 48 <?php foreach ($plans as $plan_id => $plan) : ?> 49 <option value="<?php echo esc_attr($plan_id); ?>"><?php echo esc_html($plan['name']); ?></option> 50 <?php endforeach; ?> 51 </select> 52 </div> 53 54 <div class="ss-admin-form-row"> 55 <label for="status"><?php esc_html_e('Status', 'subscription-system'); ?></label> 56 <select id="status" name="status"> 57 <option value="active"><?php esc_html_e('Active', 'subscription-system'); ?></option> 58 <option value="cancelled"><?php esc_html_e('Cancelled', 'subscription-system'); ?></option> 59 <option value="expired"><?php esc_html_e('Expired', 'subscription-system'); ?></option> 60 </select> 61 </div> 62 63 <div class="ss-admin-form-row"> 64 <label for="start_date"><?php esc_html_e('Start Date', 'subscription-system'); ?></label> 65 <input type="text" id="start_date" name="start_date" value="<?php echo esc_attr(current_time('mysql')); ?>" required> 66 <p class="description"><?php esc_html_e('Format: YYYY-MM-DD HH:MM:SS', 'subscription-system'); ?></p> 67 </div> 68 69 <div class="ss-admin-form-row"> 70 <button type="submit" name="ss_add_subscription" class="button button-primary"><?php esc_html_e('Add Subscription', 'subscription-system'); ?></button> 71 </div> 72 </form> 73 </div> 54 55 <?php if (empty($users)) : ?> 56 <div class="notice notice-warning"> 57 <p> 58 <?php esc_html_e( 59 'No WordPress users are available. Create a user before adding a subscription.', 60 'subscription-system' 61 ); ?> 62 </p> 63 </div> 64 <?php elseif (empty($plans)) : ?> 65 <div class="notice notice-warning"> 66 <p> 67 <?php esc_html_e( 68 'No active subscription plans are available. Create a plan first.', 69 'subscription-system' 70 ); ?> 71 </p> 72 </div> 73 <?php else : ?> 74 75 <div class="card" style="max-width: 700px; margin-top: 20px;"> 76 <form method="post"> 77 <?php wp_nonce_field('ss_add_subscription', 'ss_nonce'); ?> 78 79 <table class="form-table" role="presentation"> 80 <tbody> 81 <tr> 82 <th scope="row"> 83 <label for="user_id"> 84 <?php esc_html_e( 85 'WordPress User', 86 'subscription-system' 87 ); ?> 88 </label> 89 </th> 90 91 <td> 92 <select 93 id="user_id" 94 name="user_id" 95 class="regular-text" 96 required 97 > 98 <option value=""> 99 <?php esc_html_e( 100 'Select a user', 101 'subscription-system' 102 ); ?> 103 </option> 104 105 <?php foreach ($users as $user) : ?> 106 <option value="<?php echo esc_attr($user->ID); ?>"> 107 <?php 108 echo esc_html( 109 $user->display_name 110 . ' — ' 111 . $user->user_email 112 . ' (ID: ' 113 . $user->ID 114 . ')' 115 ); 116 ?> 117 </option> 118 <?php endforeach; ?> 119 </select> 120 </td> 121 </tr> 122 123 <tr> 124 <th scope="row"> 125 <label for="plan_id"> 126 <?php esc_html_e( 127 'Subscription Plan', 128 'subscription-system' 129 ); ?> 130 </label> 131 </th> 132 133 <td> 134 <select 135 id="plan_id" 136 name="plan_id" 137 class="regular-text" 138 required 139 > 140 <option value=""> 141 <?php esc_html_e( 142 'Select a plan', 143 'subscription-system' 144 ); ?> 145 </option> 146 147 <?php foreach ($plans as $plan) : ?> 148 <option value="<?php echo esc_attr($plan->id); ?>"> 149 <?php 150 echo esc_html( 151 $plan->plan_name 152 . ' — ' 153 . number_format((float) $plan->price, 2) 154 ); 155 ?> 156 </option> 157 <?php endforeach; ?> 158 </select> 159 </td> 160 </tr> 161 162 <tr> 163 <th scope="row"> 164 <label for="status"> 165 <?php esc_html_e( 166 'Status', 167 'subscription-system' 168 ); ?> 169 </label> 170 </th> 171 172 <td> 173 <select id="status" name="status"> 174 <option value="active"> 175 <?php esc_html_e('Active', 'subscription-system'); ?> 176 </option> 177 178 <option value="paused"> 179 <?php esc_html_e('Paused', 'subscription-system'); ?> 180 </option> 181 182 <option value="cancelled"> 183 <?php esc_html_e('Cancelled', 'subscription-system'); ?> 184 </option> 185 186 <option value="expired"> 187 <?php esc_html_e('Expired', 'subscription-system'); ?> 188 </option> 189 </select> 190 </td> 191 </tr> 192 193 <tr> 194 <th scope="row"> 195 <label for="start_date"> 196 <?php esc_html_e( 197 'Start Date', 198 'subscription-system' 199 ); ?> 200 </label> 201 </th> 202 203 <td> 204 <input 205 type="datetime-local" 206 id="start_date" 207 name="start_date" 208 value="<?php echo esc_attr(current_time('Y-m-d\TH:i')); ?>" 209 required 210 > 211 </td> 212 </tr> 213 </tbody> 214 </table> 215 216 <?php 217 submit_button( 218 __('Add Subscription', 'subscription-system'), 219 'primary', 220 'ss_add_subscription' 221 ); 222 ?> 223 </form> 224 </div> 225 226 <?php endif; ?> 227 </div> -
subscription-system/trunk/admin/partials/work-items-display.php
r3311076 r3633440 60 60 <?php echo esc_html__('Edit', 'subscription-system'); ?> 61 61 </a> | 62 <a href="<?php echo esc_url(wp_nonce_url(admin_url('edit.php?post_type=subscription&page=subscription-work-items&action=delete&work_item_id=' . $work_item->id), 'delete_work_item_' . $work_item->id)); ?>" class="delete-work-item" onclick="return confirm('<?php echo esc_js(esc_html__('Are you sure you want to delete this work item?', 'subscription-system')); ?>')"> 63 <?php echo esc_html__('Delete', 'subscription-system'); ?> 62 <a 63 href="<?php 64 echo esc_url( 65 wp_nonce_url( 66 admin_url( 67 'admin.php?page=subscription-work-items' 68 . '&action=delete' 69 . '&work_item_id=' 70 . absint($work_item->id) 71 ), 72 'delete_work_item_' . absint($work_item->id) 73 ) 74 ); 75 ?>" 76 class="delete-work-item" 77 onclick="return confirm('<?php echo esc_js(__('Are you sure you want to delete this work item?', 'subscription-system')); ?>');" 78 > 79 <?php echo esc_html__('Delete', 'subscription-system'); ?> 80 </a> 64 81 </a> 65 82 </td> -
subscription-system/trunk/admin/partials/work-logs-display.php
r3463757 r3633440 26 26 27 27 <div class="wrap"> 28 <?php if ($user): ?> 29 <?php 30 /* translators: %s: user's display name */ 31 ?><h1><?php echo esc_html(sprintf(__('Work Logs for %s', 'subscription-system'), $user->display_name)); ?></h1> 32 <?php else: ?> 33 <h1><?php echo esc_html__('Work Logs', 'subscription-system'); ?></h1> 28 29 <?php if (!empty($show_user_selector)) : ?> 30 31 <h1> 32 <?php echo esc_html__('Work Logs', 'subscription-system'); ?> 33 </h1> 34 35 <?php if (!empty($selection_error)) : ?> 36 <div class="notice notice-error"> 37 <p><?php echo esc_html($selection_error); ?></p> 38 </div> 39 <?php endif; ?> 40 41 <div 42 class="subscription-admin-card" 43 style="max-width: 650px; margin-top: 20px;" 44 > 45 <h2> 46 <?php echo esc_html__( 47 'Select a Subscriber', 48 'subscription-system' 49 ); ?> 50 </h2> 51 52 <p> 53 <?php echo esc_html__( 54 'Choose a subscriber to view, add, edit, or delete their work logs.', 55 'subscription-system' 56 ); ?> 57 </p> 58 59 <?php if (empty($subscriber_choices)) : ?> 60 61 <div class="notice notice-warning inline"> 62 <p> 63 <?php echo esc_html__( 64 'No subscribers with subscription plans were found.', 65 'subscription-system' 66 ); ?> 67 </p> 68 </div> 69 70 <p> 71 <a 72 href="<?php echo esc_url( 73 admin_url( 74 'admin.php?page=subscription-subscribers' 75 ) 76 ); ?>" 77 class="button" 78 > 79 <?php echo esc_html__( 80 'View Subscribers', 81 'subscription-system' 82 ); ?> 83 </a> 84 </p> 85 86 <?php else : ?> 87 88 <form 89 method="get" 90 action="<?php echo esc_url(admin_url('admin.php')); ?>" 91 > 92 <input 93 type="hidden" 94 name="page" 95 value="subscription-work-logs" 96 > 97 98 <table class="form-table" role="presentation"> 99 <tbody> 100 <tr> 101 <th scope="row"> 102 <label for="work_logs_user_id"> 103 <?php echo esc_html__( 104 'Subscriber', 105 'subscription-system' 106 ); ?> 107 </label> 108 </th> 109 110 <td> 111 <select 112 id="work_logs_user_id" 113 name="user_id" 114 class="regular-text" 115 required 116 > 117 <option value=""> 118 <?php echo esc_html__( 119 'Select a subscriber', 120 'subscription-system' 121 ); ?> 122 </option> 123 124 <?php 125 foreach ( 126 $subscriber_choices as $subscriber 127 ) : 128 ?> 129 <option 130 value="<?php echo esc_attr( 131 $subscriber['user_id'] 132 ); ?>" 133 > 134 <?php 135 echo esc_html( 136 $subscriber['display_name'] 137 . ' — ' 138 . $subscriber['email'] 139 . ' (ID: ' 140 . $subscriber['user_id'] 141 . ')' 142 ); 143 ?> 144 </option> 145 <?php endforeach; ?> 146 </select> 147 </td> 148 </tr> 149 </tbody> 150 </table> 151 152 <?php 153 submit_button( 154 __('View Work Logs', 'subscription-system'), 155 'primary', 156 'submit', 157 false 158 ); 159 ?> 160 </form> 161 162 <?php endif; ?> 163 </div> 164 165 <?php return; ?> 166 167 <?php endif; ?> 168 169 <?php if (!$user) : ?> 170 <h1> 171 <?php echo esc_html__('Work Logs', 'subscription-system'); ?> 172 </h1> 173 34 174 <div class="notice notice-error"> 35 <p><?php echo esc_html__('Invalid user ID. Please select a valid user.', 'subscription-system'); ?></p> 175 <p> 176 <?php echo esc_html__( 177 'The selected user could not be found.', 178 'subscription-system' 179 ); ?> 180 </p> 36 181 </div> 37 <?php if (current_user_can('manage_options')): ?> 38 <p><a href="<?php echo esc_url(admin_url('admin.php?page=subscription-subscribers')); ?>" class="button"><?php echo esc_html__('Back to Subscribers', 'subscription-system'); ?></a></p> 39 <?php endif; ?> 182 40 183 <?php return; ?> 41 184 <?php endif; ?> 42 43 <?php if (current_user_can('manage_options')): ?> 44 <p><a href="<?php echo esc_url(admin_url('admin.php?page=subscription-subscribers')); ?>" class="button"><?php echo esc_html__('Back to Subscribers', 'subscription-system'); ?></a></p> 185 186 <?php 187 /* translators: %s: user's display name */ 188 ?> 189 <h1> 190 <?php 191 echo esc_html( 192 sprintf( 193 __('Work Logs for %s', 'subscription-system'), 194 $user->display_name 195 ) 196 ); 197 ?> 198 </h1> 199 200 <?php if (current_user_can('manage_options')) : ?> 201 <p> 202 <a 203 href="<?php echo esc_url( 204 admin_url('admin.php?page=subscription-work-logs') 205 ); ?>" 206 class="button" 207 > 208 <?php echo esc_html__( 209 'Select Another Subscriber', 210 'subscription-system' 211 ); ?> 212 </a> 213 214 <a 215 href="<?php echo esc_url( 216 admin_url('admin.php?page=subscription-subscribers') 217 ); ?>" 218 class="button" 219 > 220 <?php echo esc_html__( 221 'Back to Subscribers', 222 'subscription-system' 223 ); ?> 224 </a> 225 </p> 45 226 <?php endif; ?> 46 227 47 228 <?php settings_errors('subscription_messages'); ?> 48 49 <div class="subscription-admin-container"> 50 <div class="subscription-admin-main"> 51 <div class="subscription-admin-card"> 52 <h2><?php echo esc_html__('All Work Logs', 'subscription-system'); ?></h2> 53 54 <?php if (empty($work_logs)): ?> 55 <p><?php echo esc_html__('No work logs found for this user.', 'subscription-system'); ?></p> 56 <?php else: ?> 57 <table class="wp-list-table widefat fixed striped"> 58 <thead> 229 230 <div class="subscription-admin-container"> 231 232 <div class="subscription-admin-main"> 233 <div class="subscription-admin-card"> 234 235 <h2> 236 <?php echo esc_html__( 237 'All Work Logs', 238 'subscription-system' 239 ); ?> 240 </h2> 241 242 <?php if (empty($work_logs)) : ?> 243 244 <p> 245 <?php echo esc_html__( 246 'No work logs found for this subscriber.', 247 'subscription-system' 248 ); ?> 249 </p> 250 251 <?php else : ?> 252 253 <table class="wp-list-table widefat fixed striped"> 254 <thead> 255 <tr> 256 <th> 257 <?php echo esc_html__( 258 'ID', 259 'subscription-system' 260 ); ?> 261 </th> 262 263 <th> 264 <?php echo esc_html__( 265 'Channel', 266 'subscription-system' 267 ); ?> 268 </th> 269 270 <th> 271 <?php echo esc_html__( 272 'Work Item', 273 'subscription-system' 274 ); ?> 275 </th> 276 277 <th> 278 <?php echo esc_html__( 279 'Importance', 280 'subscription-system' 281 ); ?> 282 </th> 283 284 <th> 285 <?php echo esc_html__( 286 'Notes', 287 'subscription-system' 288 ); ?> 289 </th> 290 291 <th> 292 <?php echo esc_html__( 293 'Performed At', 294 'subscription-system' 295 ); ?> 296 </th> 297 298 <th> 299 <?php echo esc_html__( 300 'Created At', 301 'subscription-system' 302 ); ?> 303 </th> 304 305 <th> 306 <?php echo esc_html__( 307 'Actions', 308 'subscription-system' 309 ); ?> 310 </th> 311 </tr> 312 </thead> 313 314 <tbody> 315 <?php foreach ($work_logs as $work_log) : ?> 59 316 <tr> 60 <th><?php echo esc_html__('ID', 'subscription-system'); ?></th> 61 <th><?php echo esc_html__('Channel', 'subscription-system'); ?></th> 62 <th><?php echo esc_html__('Work Item', 'subscription-system'); ?></th> 63 <th><?php echo esc_html__('Importance', 'subscription-system'); ?></th> 64 <th><?php echo esc_html__('Notes', 'subscription-system'); ?></th> 65 <th><?php echo esc_html__('Performed At', 'subscription-system'); ?></th> 66 <th><?php echo esc_html__('Created At', 'subscription-system'); ?></th> 67 <th><?php echo esc_html__('Actions', 'subscription-system'); ?></th> 317 <td> 318 <?php echo esc_html($work_log->id); ?> 319 </td> 320 321 <td> 322 <?php 323 echo esc_html( 324 $work_log->channel_name 325 ? $work_log->channel_name 326 : __('None', 'subscription-system') 327 ); 328 ?> 329 </td> 330 331 <td> 332 <?php 333 echo esc_html( 334 $work_log->work_item_name 335 ? $work_log->work_item_name 336 : __('None', 'subscription-system') 337 ); 338 ?> 339 </td> 340 341 <td> 342 <?php echo esc_html( 343 $work_log->importance 344 ); ?> 345 </td> 346 347 <td> 348 <div class="work-log-notes-preview"> 349 <?php 350 $plain_notes = wp_strip_all_tags( 351 $work_log->notes 352 ); 353 354 echo esc_html( 355 mb_substr($plain_notes, 0, 100) 356 ); 357 358 if (mb_strlen($plain_notes) > 100) { 359 echo '…'; 360 } 361 ?> 362 363 <?php if ( 364 mb_strlen($plain_notes) > 100 365 ) : ?> 366 <a 367 href="#" 368 class="view-full-notes" 369 data-notes="<?php echo esc_attr( 370 $work_log->notes 371 ); ?>" 372 > 373 <?php echo esc_html__( 374 'View Full Notes', 375 'subscription-system' 376 ); ?> 377 </a> 378 <?php endif; ?> 379 </div> 380 </td> 381 382 <td> 383 <?php 384 echo esc_html( 385 date_i18n( 386 get_option('date_format') 387 . ' ' 388 . get_option('time_format'), 389 strtotime( 390 $work_log->performed_at 391 ) 392 ) 393 ); 394 ?> 395 </td> 396 397 <td> 398 <?php 399 echo esc_html( 400 date_i18n( 401 get_option('date_format') 402 . ' ' 403 . get_option('time_format'), 404 strtotime( 405 $work_log->created_at 406 ) 407 ) 408 ); 409 ?> 410 </td> 411 412 <td> 413 <a 414 href="#" 415 class="edit-work-log" 416 data-id="<?php echo esc_attr( 417 $work_log->id 418 ); ?>" 419 data-channel-id="<?php echo esc_attr( 420 $work_log->channel_id 421 ); ?>" 422 data-work-item-id="<?php echo esc_attr( 423 $work_log->work_item_id 424 ); ?>" 425 data-importance="<?php echo esc_attr( 426 $work_log->importance 427 ); ?>" 428 data-notes="<?php echo esc_attr( 429 $work_log->notes 430 ); ?>" 431 data-performed-at="<?php echo esc_attr( 432 date( 433 'Y-m-d H:i', 434 strtotime( 435 $work_log->performed_at 436 ) 437 ) 438 ); ?>" 439 > 440 <?php echo esc_html__( 441 'Edit', 442 'subscription-system' 443 ); ?> 444 </a> 445 446 | 447 448 <a 449 href="<?php 450 echo esc_url( 451 wp_nonce_url( 452 add_query_arg( 453 array( 454 'page' => 'subscription-work-logs', 455 'action' => 'delete', 456 'work_log_id' => absint( 457 $work_log->id 458 ), 459 'user_id' => absint( 460 $user_id 461 ), 462 ), 463 admin_url( 464 'admin.php' 465 ) 466 ), 467 'delete_work_log_' 468 . absint( 469 $work_log->id 470 ) 471 ) 472 ); 473 ?>" 474 class="delete-work-log" 475 onclick="return confirm('<?php 476 echo esc_js( 477 __( 478 'Are you sure you want to delete this work log?', 479 'subscription-system' 480 ) 481 ); 482 ?>');" 483 > 484 <?php echo esc_html__( 485 'Delete', 486 'subscription-system' 487 ); ?> 488 </a> 489 </td> 68 490 </tr> 69 </thead> 70 <tbody> 71 <?php foreach ($work_logs as $work_log): ?> 72 <tr> 73 <td><?php echo esc_html($work_log->id); ?></td> 74 <td><?php echo esc_html($work_log->channel_name ? $work_log->channel_name : __('None', 'subscription-system')); ?></td> 75 <td><?php echo esc_html($work_log->work_item_name ? $work_log->work_item_name : __('None', 'subscription-system')); ?></td> 76 <td><?php echo esc_html($work_log->importance); ?></td> 77 <td> 78 <div class="work-log-notes-preview"> 79 <?php echo wp_kses_post(substr(strip_tags($work_log->notes), 0, 100) . (strlen(strip_tags($work_log->notes)) > 100 ? '...' : '')); ?> 80 <?php if (strlen(strip_tags($work_log->notes)) > 100): ?> 81 <a href="#" class="view-full-notes" data-notes="<?php echo esc_attr($work_log->notes); ?>"><?php echo esc_html__('View Full Notes', 'subscription-system'); ?></a> 82 <?php endif; ?> 83 </div> 84 </td> 85 <td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($work_log->performed_at))); ?></td> 86 <td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($work_log->created_at))); ?></td> 87 <td> 88 <a href="#" class="edit-work-log" 89 data-id="<?php echo esc_attr($work_log->id); ?>" 90 data-channel-id="<?php echo esc_attr($work_log->channel_id); ?>" 91 data-work-item-id="<?php echo esc_attr($work_log->work_item_id); ?>" 92 data-importance="<?php echo esc_attr($work_log->importance); ?>" 93 data-notes="<?php echo esc_attr($work_log->notes); ?>" 94 data-performed-at="<?php echo esc_attr(date('Y-m-d H:i', strtotime($work_log->performed_at))); ?>"> 95 <?php echo esc_html__('Edit', 'subscription-system'); ?> 96 </a> | 97 <a href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=subscription-work-logs&action=delete&work_log_id=' . $work_log->id . '&user_id=' . $user_id), 'delete_work_log_' . $work_log->id)); ?>" class="delete-work-log" onclick="return confirm('<?php echo esc_js(__('Are you sure you want to delete this work log?', 'subscription-system')); ?>')"> 98 <?php echo esc_html__('Delete', 'subscription-system'); ?> 99 </a> 100 </td> 101 </tr> 102 <?php endforeach; ?> 103 </tbody> 104 </table> 105 <?php endif; ?> 106 </div> 491 <?php endforeach; ?> 492 </tbody> 493 </table> 494 495 <?php endif; ?> 496 107 497 </div> 108 109 <div class="subscription-admin-sidebar"> 110 <div class="subscription-admin-card"> 111 <h2><?php echo esc_html__('Add New Work Log', 'subscription-system'); ?></h2> 112 498 </div> 499 500 <div class="subscription-admin-sidebar"> 501 502 <div class="subscription-admin-card"> 503 504 <h2> 505 <?php echo esc_html__( 506 'Add New Work Log', 507 'subscription-system' 508 ); ?> 509 </h2> 510 511 <?php if (empty($channels)) : ?> 512 513 <div class="notice notice-warning inline"> 514 <p> 515 <?php echo esc_html__( 516 'No channels are available. Create an active channel before adding a work log.', 517 'subscription-system' 518 ); ?> 519 </p> 520 </div> 521 522 <?php else : ?> 523 113 524 <form method="post" action=""> 114 <?php wp_nonce_field('subscription_add_work_log', 'subscription_nonce'); ?> 115 <input type="hidden" name="user_id" value="<?php echo esc_attr($user_id); ?>"> 116 525 526 <?php 527 wp_nonce_field( 528 'subscription_add_work_log', 529 'subscription_nonce' 530 ); 531 ?> 532 533 <input 534 type="hidden" 535 name="user_id" 536 value="<?php echo esc_attr($user_id); ?>" 537 > 538 117 539 <div class="form-field"> 118 <label for="channel_id"><?php echo esc_html__('Channel', 'subscription-system'); ?> <span class="required">*</span></label> 119 <select id="channel_id" name="channel_id" required> 120 <option value=""><?php echo esc_html__('-- Select Channel --', 'subscription-system'); ?></option> 121 <?php foreach ($channels as $channel): ?> 122 <?php if ($channel->status === 'active'): ?> 123 <option value="<?php echo esc_attr($channel->id); ?>"><?php echo esc_html($channel->name); ?></option> 540 <label for="channel_id"> 541 <?php echo esc_html__( 542 'Channel', 543 'subscription-system' 544 ); ?> 545 <span class="required">*</span> 546 </label> 547 548 <select 549 id="channel_id" 550 name="channel_id" 551 required 552 > 553 <option value=""> 554 <?php echo esc_html__( 555 '-- Select Channel --', 556 'subscription-system' 557 ); ?> 558 </option> 559 560 <?php foreach ($channels as $channel) : ?> 561 <?php if ( 562 'active' === $channel->status 563 ) : ?> 564 <option 565 value="<?php echo esc_attr( 566 $channel->id 567 ); ?>" 568 > 569 <?php echo esc_html( 570 $channel->name 571 ); ?> 572 </option> 124 573 <?php endif; ?> 125 574 <?php endforeach; ?> 126 575 </select> 127 576 </div> 128 577 129 578 <div class="form-field"> 130 <label for="work_item_id"><?php echo esc_html__('Work Item', 'subscription-system'); ?> <span class="required">*</span></label> 131 <select id="work_item_id" name="work_item_id" required disabled> 132 <option value=""><?php echo esc_html__('-- Select Channel First --', 'subscription-system'); ?></option> 579 <label for="work_item_id"> 580 <?php echo esc_html__( 581 'Work Item', 582 'subscription-system' 583 ); ?> 584 <span class="required">*</span> 585 </label> 586 587 <select 588 id="work_item_id" 589 name="work_item_id" 590 required 591 disabled 592 > 593 <option value=""> 594 <?php echo esc_html__( 595 '-- Select Channel First --', 596 'subscription-system' 597 ); ?> 598 </option> 133 599 </select> 134 600 </div> 135 601 136 602 <div class="form-field"> 137 <label for="importance"><?php echo esc_html__('Importance', 'subscription-system'); ?></label> 138 <textarea id="importance" name="importance" rows="3"></textarea> 603 <label for="importance"> 604 <?php echo esc_html__( 605 'Importance', 606 'subscription-system' 607 ); ?> 608 </label> 609 610 <textarea 611 id="importance" 612 name="importance" 613 rows="3" 614 ></textarea> 139 615 </div> 140 616 141 617 <div class="form-field"> 142 <label for="notes"><?php echo esc_html__('Notes', 'subscription-system'); ?></label> 143 <?php 144 $editor_settings = array( 145 'textarea_name' => 'notes', 146 'textarea_rows' => 10, 147 'media_buttons' => true, 148 'teeny' => true, 149 'quicktags' => true, 618 <label for="notes"> 619 <?php echo esc_html__( 620 'Notes', 621 'subscription-system' 622 ); ?> 623 </label> 624 625 <?php 626 wp_editor( 627 '', 628 'notes', 629 array( 630 'textarea_name' => 'notes', 631 'textarea_rows' => 8, 632 'media_buttons' => true, 633 'teeny' => true, 634 'quicktags' => true, 635 ) 150 636 ); 151 wp_editor('', 'notes', $editor_settings);152 637 ?> 153 638 </div> 154 639 155 640 <div class="form-field"> 156 <label for="performed_at"><?php echo esc_html__('Performed At', 'subscription-system'); ?></label> 157 <input type="text" id="performed_at" name="performed_at" class="datetimepicker" value="<?php echo esc_attr(date_i18n('Y-m-d H:i')); ?>" autocomplete="off"> 641 <label for="performed_at"> 642 <?php echo esc_html__( 643 'Performed At', 644 'subscription-system' 645 ); ?> 646 </label> 647 648 <input 649 type="text" 650 id="performed_at" 651 name="performed_at" 652 class="datetimepicker" 653 value="<?php echo esc_attr( 654 current_time('Y-m-d H:i') 655 ); ?>" 656 autocomplete="off" 657 > 158 658 </div> 159 659 160 660 <p class="submit"> 161 <input type="submit" name="add_work_log" class="button button-primary" value="<?php echo esc_attr__('Add Work Log', 'subscription-system'); ?>"> 661 <input 662 type="submit" 663 name="add_work_log" 664 class="button button-primary" 665 value="<?php echo esc_attr__( 666 'Add Work Log', 667 'subscription-system' 668 ); ?>" 669 > 162 670 </p> 671 163 672 </form> 164 </div> 165 166 <div class="subscription-admin-card" id="edit-work-log-form" style="display: none;"> 167 <h2><?php echo esc_html__('Edit Work Log', 'subscription-system'); ?></h2> 168 169 <form method="post" action=""> 170 <?php wp_nonce_field('subscription_edit_work_log', 'subscription_nonce'); ?> 171 <input type="hidden" id="edit_work_log_id" name="work_log_id"> 172 173 <div class="form-field"> 174 <label for="edit_channel_id"><?php echo esc_html__('Channel', 'subscription-system'); ?> <span class="required">*</span></label> 175 <select id="edit_channel_id" name="channel_id" required> 176 <option value=""><?php echo esc_html__('-- Select Channel --', 'subscription-system'); ?></option> 177 <?php foreach ($channels as $channel): ?> 178 <?php if ($channel->status === 'active'): ?> 179 <option value="<?php echo esc_attr($channel->id); ?>"><?php echo esc_html($channel->name); ?></option> 180 <?php endif; ?> 181 <?php endforeach; ?> 182 </select> 183 </div> 184 185 <div class="form-field"> 186 <label for="edit_work_item_id"><?php echo esc_html__('Work Item', 'subscription-system'); ?> <span class="required">*</span></label> 187 <select id="edit_work_item_id" name="work_item_id" required> 188 <option value=""><?php echo esc_html__('-- Select Channel First --', 'subscription-system'); ?></option> 189 </select> 190 </div> 191 192 <div class="form-field"> 193 <label for="edit_importance"><?php echo esc_html__('Importance', 'subscription-system'); ?></label> 194 <textarea id="edit_importance" name="importance" rows="3"></textarea> 195 </div> 196 197 <div class="form-field"> 198 <label for="edit_notes"><?php echo esc_html__('Notes', 'subscription-system'); ?></label> 199 <?php 200 $editor_settings = array( 673 674 <?php endif; ?> 675 676 </div> 677 678 <div 679 class="subscription-admin-card" 680 id="edit-work-log-form" 681 style="display: none;" 682 > 683 <h2> 684 <?php echo esc_html__( 685 'Edit Work Log', 686 'subscription-system' 687 ); ?> 688 </h2> 689 690 <form method="post" action=""> 691 692 <?php 693 wp_nonce_field( 694 'subscription_edit_work_log', 695 'subscription_nonce' 696 ); 697 ?> 698 699 <input 700 type="hidden" 701 id="edit_work_log_id" 702 name="work_log_id" 703 > 704 705 <div class="form-field"> 706 <label for="edit_channel_id"> 707 <?php echo esc_html__( 708 'Channel', 709 'subscription-system' 710 ); ?> 711 <span class="required">*</span> 712 </label> 713 714 <select 715 id="edit_channel_id" 716 name="channel_id" 717 required 718 > 719 <option value=""> 720 <?php echo esc_html__( 721 '-- Select Channel --', 722 'subscription-system' 723 ); ?> 724 </option> 725 726 <?php foreach ($channels as $channel) : ?> 727 <?php if ( 728 'active' === $channel->status 729 ) : ?> 730 <option 731 value="<?php echo esc_attr( 732 $channel->id 733 ); ?>" 734 > 735 <?php echo esc_html( 736 $channel->name 737 ); ?> 738 </option> 739 <?php endif; ?> 740 <?php endforeach; ?> 741 </select> 742 </div> 743 744 <div class="form-field"> 745 <label for="edit_work_item_id"> 746 <?php echo esc_html__( 747 'Work Item', 748 'subscription-system' 749 ); ?> 750 <span class="required">*</span> 751 </label> 752 753 <select 754 id="edit_work_item_id" 755 name="work_item_id" 756 required 757 > 758 <option value=""> 759 <?php echo esc_html__( 760 '-- Select Channel First --', 761 'subscription-system' 762 ); ?> 763 </option> 764 </select> 765 </div> 766 767 <div class="form-field"> 768 <label for="edit_importance"> 769 <?php echo esc_html__( 770 'Importance', 771 'subscription-system' 772 ); ?> 773 </label> 774 775 <textarea 776 id="edit_importance" 777 name="importance" 778 rows="3" 779 ></textarea> 780 </div> 781 782 <div class="form-field"> 783 <label for="edit_notes"> 784 <?php echo esc_html__( 785 'Notes', 786 'subscription-system' 787 ); ?> 788 </label> 789 790 <?php 791 wp_editor( 792 '', 793 'edit_notes', 794 array( 201 795 'textarea_name' => 'notes', 202 'textarea_rows' => 10,796 'textarea_rows' => 8, 203 797 'media_buttons' => true, 204 'teeny' => true, 205 'quicktags' => true, 206 ); 207 wp_editor('', 'edit_notes', $editor_settings); 208 ?> 209 </div> 210 211 <div class="form-field"> 212 <label for="edit_performed_at"><?php echo esc_html__('Performed At', 'subscription-system'); ?></label> 213 <input type="text" id="edit_performed_at" name="performed_at" class="datetimepicker" autocomplete="off"> 214 </div> 215 216 <p class="submit"> 217 <input type="submit" name="edit_work_log" class="button button-primary" value="<?php echo esc_attr__('Update Work Log', 'subscription-system'); ?>"> 218 <button type="button" class="button cancel-edit"><?php echo esc_html__('Cancel', 'subscription-system'); ?></button> 219 </p> 220 </form> 221 </div> 222 223 <div id="notes-modal" class="modal" style="display: none;"> 224 <div class="modal-content"> 225 <span class="close">×</span> 226 <h3><?php echo esc_html__('Full Notes', 'subscription-system'); ?></h3> 227 <div id="full-notes-content"></div> 798 'teeny' => true, 799 'quicktags' => true, 800 ) 801 ); 802 ?> 228 803 </div> 804 805 <div class="form-field"> 806 <label for="edit_performed_at"> 807 <?php echo esc_html__( 808 'Performed At', 809 'subscription-system' 810 ); ?> 811 </label> 812 813 <input 814 type="text" 815 id="edit_performed_at" 816 name="performed_at" 817 class="datetimepicker" 818 autocomplete="off" 819 > 820 </div> 821 822 <p class="submit"> 823 <input 824 type="submit" 825 name="edit_work_log" 826 class="button button-primary" 827 value="<?php echo esc_attr__( 828 'Update Work Log', 829 'subscription-system' 830 ); ?>" 831 > 832 833 <button 834 type="button" 835 class="button cancel-edit" 836 > 837 <?php echo esc_html__( 838 'Cancel', 839 'subscription-system' 840 ); ?> 841 </button> 842 </p> 843 844 </form> 845 </div> 846 847 <div 848 id="notes-modal" 849 class="modal" 850 style="display: none;" 851 > 852 <div class="modal-content"> 853 <button 854 type="button" 855 class="close" 856 aria-label="<?php echo esc_attr__( 857 'Close', 858 'subscription-system' 859 ); ?>" 860 > 861 × 862 </button> 863 864 <h3> 865 <?php echo esc_html__( 866 'Full Notes', 867 'subscription-system' 868 ); ?> 869 </h3> 870 871 <div id="full-notes-content"></div> 229 872 </div> 230 873 </div> 874 231 875 </div> 876 </div> 232 877 </div> 233 878 -
subscription-system/trunk/public/css/interest-subscribe.css
r3598150 r3633440 33 33 justify-content: center; 34 34 padding: 20px; 35 box-sizing: border-box; 35 36 } 36 37 … … 45 46 } 46 47 48 .ss-interest-modal__dialog, 49 .ss-interest-modal__dialog *, 50 .ss-interest-modal__dialog *::before, 51 .ss-interest-modal__dialog *::after { 52 box-sizing: border-box; 53 } 54 47 55 .ss-interest-modal__dialog { 48 56 position: relative; 57 display: flex; 58 flex-direction: column; 49 59 width: 100%; 50 60 max-width: 560px; 51 max-height: 90vh;61 max-height: calc(100dvh - 40px); 52 62 overflow: hidden; 53 63 background: #fff; 54 64 border-radius: 16px; 55 65 box-shadow: 0 24px 60px rgba(15, 23, 42, 0.25); 56 display: flex;57 flex-direction: column;58 66 } 59 67 … … 64 72 padding: 20px 24px; 65 73 border-bottom: 1px solid #e5e7eb; 74 flex: 0 0 auto; 66 75 } 67 76 … … 82 91 83 92 .ss-interest-modal__body { 93 flex: 1 1 auto; 84 94 padding: 20px 24px; 95 overflow-x: hidden; 85 96 overflow-y: auto; 97 min-height: 0; 98 -webkit-overflow-scrolling: touch; 99 overscroll-behavior: contain; 100 } 101 102 .ss-interest-form { 103 display: flex; 104 flex-direction: column; 105 flex: 1 1 auto; 106 min-height: 0; 107 overflow: hidden; 86 108 } 87 109 … … 232 254 .ss-interest-modal__footer { 233 255 display: flex; 256 flex-direction: column; 257 gap: 12px; 258 padding: 16px 24px 20px; 259 border-top: 1px solid #e5e7eb; 260 flex: 0 0 auto; 261 background: #fff; 262 } 263 264 .ss-interest-modal__footer-actions { 265 display: flex; 266 flex-direction: row; 234 267 justify-content: flex-end; 268 align-items: stretch; 235 269 gap: 10px; 236 padding: 16px 24px 24px; 237 border-top: 1px solid #e5e7eb; 270 width: 100%; 271 } 272 273 .ss-interest-modal__footer-actions .ss-interest-btn { 274 display: inline-flex; 275 align-items: center; 276 justify-content: center; 277 width: auto; 278 max-width: none; 279 min-width: 110px; 280 flex: 0 0 auto; 281 margin: 0; 282 white-space: nowrap; 283 float: none; 284 clear: none; 285 } 286 287 .ss-interest-modal__footer-actions button.ss-interest-btn, 288 .ss-interest-modal__footer-actions a.ss-interest-btn { 289 display: inline-flex; 290 width: auto; 238 291 } 239 292 … … 266 319 } 267 320 321 .ss-interest-modal__cooldown-hint { 322 margin: 0; 323 padding: 10px 12px; 324 border-radius: 8px; 325 background: #f8fafc; 326 border: 1px solid #e2e8f0; 327 color: #475569; 328 font-size: 13px; 329 line-height: 1.5; 330 } 331 268 332 @media (max-width: 600px) { 333 .ss-interest-modal { 334 padding: 0; 335 align-items: flex-end; 336 } 337 269 338 .ss-interest-modal__dialog { 270 max-height: 95vh; 339 max-width: none; 340 max-height: 92dvh; 341 border-radius: 16px 16px 0 0; 342 } 343 344 .ss-interest-modal__header { 345 padding: 16px 18px; 346 } 347 348 .ss-interest-modal__header h2 { 349 font-size: 18px; 350 } 351 352 .ss-interest-modal__body { 353 padding: 16px 18px; 354 } 355 356 .ss-interest-modal__footer { 357 padding: 14px 18px calc(14px + env(safe-area-inset-bottom, 0px)); 358 } 359 360 .ss-interest-modal__footer-actions { 361 flex-direction: row; 362 } 363 364 .ss-interest-modal__footer-actions .ss-interest-btn { 365 flex: 1 1 0; 366 min-width: 0; 271 367 } 272 368 273 369 .ss-interest-checkboxes { 274 370 grid-template-columns: 1fr; 371 max-height: 140px; 372 } 373 374 .ss-interest-guest { 375 padding: 28px 20px 20px; 275 376 } 276 377 } … … 346 447 padding: 40px 32px 28px; 347 448 gap: 12px; 449 flex: 1 1 auto; 450 min-height: 0; 451 overflow-x: hidden; 452 overflow-y: auto; 453 -webkit-overflow-scrolling: touch; 348 454 } 349 455 … … 370 476 .ss-interest-guest__footer { 371 477 justify-content: center; 372 gap: 10px; 478 } 479 480 .ss-interest-guest__footer .ss-interest-modal__footer-actions { 481 justify-content: center; 373 482 } 374 483 -
subscription-system/trunk/public/js/interest-subscribe.js
r3598150 r3633440 3 3 4 4 var vars = (typeof ssInterestVars !== 'undefined') ? ssInterestVars : {}; 5 var DISMISS_KEY = 'ss_interest_modal_dismissed_at'; 5 6 6 7 function getSelectedCount($container) { … … 56 57 } 57 58 59 function getCooldownMs() { 60 return parseInt(vars.auto_open_cooldown_ms, 10) || 0; 61 } 62 63 function getDismissedAt() { 64 return parseInt(localStorage.getItem(DISMISS_KEY), 10) || 0; 65 } 66 67 function getRemainingCooldownMs() { 68 var cooldownMs = getCooldownMs(); 69 var dismissedAt = getDismissedAt(); 70 if (cooldownMs <= 0 || !dismissedAt) { 71 return 0; 72 } 73 return Math.max(0, cooldownMs - (Date.now() - dismissedAt)); 74 } 75 76 function wasRecentlyDismissed() { 77 var cooldownMs = getCooldownMs(); 78 if (cooldownMs <= 0) { 79 return false; 80 } 81 82 var remainingMs = getRemainingCooldownMs(); 83 if (remainingMs > 0) { 84 return true; 85 } 86 87 if (getDismissedAt()) { 88 localStorage.removeItem(DISMISS_KEY); 89 } 90 return false; 91 } 92 93 function recordDismissal() { 94 if (getCooldownMs() <= 0) { 95 return; 96 } 97 localStorage.setItem(DISMISS_KEY, String(Date.now())); 98 } 99 100 function dismissModal($modal) { 101 recordDismissal(); 102 closeModal($modal); 103 } 104 58 105 // ── Helper: remove a query param from the current URL without reload ── 59 106 function removeQueryParam(param) { … … 78 125 openModal($targetModal); 79 126 } 80 } else if (vars.auto_open ) {127 } else if (vars.auto_open && !wasRecentlyDismissed()) { 81 128 var delay = parseInt(vars.auto_open_delay, 10) || 0; 82 129 setTimeout(function () { … … 96 143 97 144 $(document).on('click', '[data-ss-close-modal]', function () { 98 closeModal($(this).closest('.ss-interest-modal'));145 dismissModal($(this).closest('.ss-interest-modal')); 99 146 }); 100 147 101 148 $(document).on('keydown', function (e) { 102 149 if (e.key === 'Escape') { 103 closeModal($('.ss-interest-modal.is-open')); 150 var $open = $('.ss-interest-modal.is-open'); 151 if ($open.length) { 152 dismissModal($open); 153 } 104 154 } 105 155 }); -
subscription-system/trunk/public/partials/interest-subscribe-modal.php
r3598150 r3633440 22 22 $prefill_email = $is_logged_in ? $current_user->user_email : ''; 23 23 $hide_fields = $is_logged_in && (bool) get_option( 'subs_modal_hide_fields_logged_in', 1 ); 24 $cooldown_value = absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 25 $cooldown_label = ( $cooldown_value > 0 && function_exists( 'subs_sys_get_modal_cooldown_label' ) ) 26 ? subs_sys_get_modal_cooldown_label() 27 : ''; 28 $cooldown_hint_text = $cooldown_label 29 ? sprintf( 30 /* translators: %s: human-readable time period such as "2 weeks" */ 31 __( 'If you close this, we won\'t show it again for %s.', 'subscription-system' ), 32 $cooldown_label 33 ) 34 : ''; 24 35 25 36 // Build the login-redirect URL: after login, return here with ?ss_subscribe=1 so the modal auto-reopens. … … 31 42 <div id="<?php echo $modal_id; ?>" class="ss-interest-modal" aria-hidden="true" role="dialog" aria-labelledby="<?php echo $modal_id; ?>-title"> 32 43 <div class="ss-interest-modal__overlay" data-ss-close-modal></div> 33 <div class="ss-interest-modal__dialog ">44 <div class="ss-interest-modal__dialog<?php echo $is_logged_in ? ' ss-interest-modal__dialog--logged-in' : ' ss-interest-modal__dialog--guest'; ?>"> 34 45 <div class="ss-interest-modal__header"> 35 46 <h2 id="<?php echo $modal_id; ?>-title"><?php esc_html_e( 'Subscribe to Updates', 'subscription-system' ); ?></h2> … … 54 65 </div> 55 66 <div class="ss-interest-modal__footer ss-interest-guest__footer"> 67 <?php if ( $cooldown_hint_text ) : ?> 68 <p class="ss-interest-modal__cooldown-hint"><?php echo esc_html( $cooldown_hint_text ); ?></p> 69 <?php endif; ?> 70 <div class="ss-interest-modal__footer-actions"> 56 71 <button type="button" class="ss-interest-btn ss-interest-btn--ghost" data-ss-close-modal> 57 72 <?php esc_html_e( 'Not now', 'subscription-system' ); ?> … … 60 75 <?php esc_html_e( 'Log in to subscribe', 'subscription-system' ); ?> 61 76 </a> 77 </div> 62 78 </div> 63 79 … … 227 243 228 244 <div class="ss-interest-modal__footer"> 245 <?php if ( $cooldown_hint_text ) : ?> 246 <p class="ss-interest-modal__cooldown-hint"><?php echo esc_html( $cooldown_hint_text ); ?></p> 247 <?php endif; ?> 248 <div class="ss-interest-modal__footer-actions"> 229 249 <button type="button" class="ss-interest-btn ss-interest-btn--secondary" data-ss-close-modal> 230 250 <?php esc_html_e( 'Cancel', 'subscription-system' ); ?> … … 233 253 <?php esc_html_e( 'Subscribe', 'subscription-system' ); ?> 234 254 </button> 255 </div> 235 256 </div> 236 257 </form> -
subscription-system/trunk/subscription-system.php
r3598150 r3633440 17 17 * Plugin URI: https://mediahubster.in/subscription-system 18 18 * Description: A comprehensive subscription management system for WordPress. 19 * Version: 1.0. 1819 * Version: 1.0.20 20 20 * Author: Joe Youngblood 21 21 * Author URI: https://mediahubster.in … … 97 97 98 98 // Define plugin constants 99 define('SUBS_SYS_VERSION', '1.0. 18');99 define('SUBS_SYS_VERSION', '1.0.20'); 100 100 define('SUBS_SYS_PLUGIN_DIR', plugin_dir_path(__FILE__)); 101 101 define('SUBS_SYS_PLUGIN_URL', plugin_dir_url(__FILE__)); 102 102 define('SUBS_SYS_PLUGIN_BASENAME', plugin_basename(__FILE__)); 103 104 /** 105 * Valid cooldown units for the subscribe modal re-show delay. 106 * 107 * @return string[] 108 */ 109 function subs_sys_get_modal_cooldown_units() { 110 return array( 'minutes', 'days', 'weeks', 'months' ); 111 } 112 113 /** 114 * Convert modal cooldown settings to milliseconds for frontend use. 115 * 116 * @param int|null $value Cooldown amount. 117 * @param string|null $unit Cooldown unit. 118 * @return int 119 */ 120 function subs_sys_get_modal_cooldown_ms( $value = null, $unit = null ) { 121 $value = null !== $value ? absint( $value ) : absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 122 $unit = null !== $unit ? sanitize_key( $unit ) : sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 123 124 if ( $value <= 0 ) { 125 return 0; 126 } 127 128 $seconds_per_unit = array( 129 'minutes' => MINUTE_IN_SECONDS, 130 'days' => DAY_IN_SECONDS, 131 'weeks' => WEEK_IN_SECONDS, 132 'months' => 30 * DAY_IN_SECONDS, 133 ); 134 135 if ( ! isset( $seconds_per_unit[ $unit ] ) ) { 136 $unit = 'minutes'; 137 } 138 139 return $value * $seconds_per_unit[ $unit ] * 1000; 140 } 141 142 /** 143 * Human-readable label for the modal cooldown period. 144 * 145 * @param int|null $value Cooldown amount. 146 * @param string|null $unit Cooldown unit. 147 * @return string 148 */ 149 function subs_sys_get_modal_cooldown_label( $value = null, $unit = null ) { 150 $value = null !== $value ? absint( $value ) : absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ); 151 $unit = null !== $unit ? sanitize_key( $unit ) : sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ); 152 153 if ( $value <= 0 ) { 154 return ''; 155 } 156 157 switch ( $unit ) { 158 case 'days': 159 /* translators: %d: number of days */ 160 return sprintf( _n( '%d day', '%d days', $value, 'subscription-system' ), $value ); 161 case 'weeks': 162 /* translators: %d: number of weeks */ 163 return sprintf( _n( '%d week', '%d weeks', $value, 'subscription-system' ), $value ); 164 case 'months': 165 /* translators: %d: number of months */ 166 return sprintf( _n( '%d month', '%d months', $value, 'subscription-system' ), $value ); 167 default: 168 /* translators: %d: number of minutes */ 169 return sprintf( _n( '%d minute', '%d minutes', $value, 'subscription-system' ), $value ); 170 } 171 } 103 172 104 173 /** … … 141 210 'ajax_url' => admin_url( 'admin-ajax.php' ), 142 211 'nonce' => wp_create_nonce( 'interest_subscription_nonce' ), 143 'auto_open' => (int) get_option( 'subs_modal_auto_open', 0 ), 144 'auto_open_delay' => absint( get_option( 'subs_modal_auto_open_delay', 3 ) ), 145 'hide_fields_logged_in' => (int) get_option( 'subs_modal_hide_fields_logged_in', 1 ), 146 'is_logged_in' => $is_logged_in ? 1 : 0, 147 'i18n' => array( 212 'auto_open' => (int) get_option( 'subs_modal_auto_open', 0 ), 213 'auto_open_delay' => absint( get_option( 'subs_modal_auto_open_delay', 3 ) ), 214 'auto_open_cooldown' => absint( get_option( 'subs_modal_auto_open_cooldown', 60 ) ), 215 'auto_open_cooldown_unit' => sanitize_key( get_option( 'subs_modal_auto_open_cooldown_unit', 'minutes' ) ), 216 'auto_open_cooldown_ms' => subs_sys_get_modal_cooldown_ms(), 217 'auto_open_cooldown_label' => subs_sys_get_modal_cooldown_label(), 218 'hide_fields_logged_in' => (int) get_option( 'subs_modal_hide_fields_logged_in', 1 ), 219 'is_logged_in' => $is_logged_in ? 1 : 0, 220 'i18n' => array( 148 221 'processing' => __( 'Processing...', 'subscription-system' ), 149 222 'submit' => __( 'Subscribe', 'subscription-system' ),
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)