Files
sodino/app/Controllers/AdminController.php

828 lines
31 KiB
PHP

<?php
namespace Sodino\Controllers;
use Sodino\Repositories\RuleRepository;
use Sodino\Repositories\UpsellRepository;
use Sodino\Repositories\BannerRepository;
use Sodino\Models\Rule;
use Sodino\Models\Upsell;
use Sodino\Models\Banner;
/**
* Admin Controller
*/
class AdminController {
private $ruleRepository;
private $upsellRepository;
private $bannerRepository;
private $allowedBannerContentTypes = ['image', 'html', 'shortcode'];
private $allowedBannerPositions = ['top', 'middle', 'bottom', 'product_page', 'cart'];
private $allowedBannerDisplayTypes = ['inline', 'popup', 'floating_bar'];
private $allowedBannerUserTargets = ['all', 'new', 'returning'];
private $allowedBannerDeviceTargets = ['all', 'desktop', 'mobile'];
private $allowedUpsellTriggerTypes = ['product', 'category', 'cart_total'];
private $allowedUpsellDiscountTypes = ['percentage', 'fixed', 'none'];
private $allowedRuleConditionTypes = ['user_type', 'product_category', 'product_ids', 'cart_total_min', 'cart_total_max', 'cart_item_count_min', 'cart_item_count_max'];
private $allowedRuleActionTypes = ['discount_percent', 'discount_fixed', 'set_price', 'free_shipping'];
private $allowedStrategies = ['priority', 'highest_discount', 'first_valid'];
public function __construct(RuleRepository $ruleRepository, UpsellRepository $upsellRepository, BannerRepository $bannerRepository) {
$this->ruleRepository = $ruleRepository;
$this->upsellRepository = $upsellRepository;
$this->bannerRepository = $bannerRepository;
}
/**
* Handle admin menu
*/
public function addMenu() {
add_menu_page(
__('سودینو', 'sodino'),
__('سودینو', 'sodino'),
'manage_options',
'sodino-rules',
[$this, 'rulesPage'],
'dashicons-money-alt',
56
);
add_submenu_page(
'sodino-rules',
__('قوانین قیمت‌گذاری', 'sodino'),
__('قوانین قیمت‌گذاری', 'sodino'),
'manage_options',
'sodino-rules',
[$this, 'rulesPage']
);
add_submenu_page(
'sodino-rules',
__('افزودن قانون', 'sodino'),
__('افزودن قانون', 'sodino'),
'manage_options',
'sodino-add-rule',
[$this, 'addRulePage']
);
add_submenu_page(
'sodino-rules',
__('آپسل (پیشنهاد فروش)', 'sodino'),
__('آپسل (پیشنهاد فروش)', 'sodino'),
'manage_options',
'sodino-upsells',
[$this, 'upsellsPage']
);
add_submenu_page(
'sodino-rules',
__('افزودن آپسل', 'sodino'),
__('افزودن آپسل', 'sodino'),
'manage_options',
'sodino-add-upsell',
[$this, 'addUpsellPage']
);
add_submenu_page(
'sodino-rules',
__('بنرهای هوشمند', 'sodino'),
__('بنرهای هوشمند', 'sodino'),
'manage_options',
'sodino-banners',
[$this, 'bannersPage']
);
add_submenu_page(
'sodino-rules',
__('افزودن بنر', 'sodino'),
__('افزودن بنر', 'sodino'),
'manage_options',
'sodino-add-banner',
[$this, 'addBannerPage']
);
add_submenu_page(
'sodino-rules',
__('ابزارها و سلامت', 'sodino'),
__('ابزارها و سلامت', 'sodino'),
'manage_options',
'sodino-tools',
[$this, 'toolsPage']
);
add_submenu_page(
'sodino-rules',
__('داشبورد سودینو', 'sodino'),
__('داشبورد سودینو', 'sodino'),
'manage_options',
'sodino-dashboard',
[$this, 'dashboardPage']
);
add_submenu_page(
'sodino-rules',
__('تنظیمات', 'sodino'),
__('تنظیمات', 'sodino'),
'manage_options',
'sodino-settings',
[$this, 'settingsPage']
);
}
private function redirectWithNotice($url, $message, $type = 'error') {
$notice = [
'type' => $type,
'message' => $message,
];
set_transient('sodino_admin_notice_' . get_current_user_id(), $notice, 60);
set_transient('sodino_admin_notice', $notice, 60);
if ($type === 'error' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$oldInput = wp_unslash($_POST);
$oldInput['_sodino_page'] = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
set_transient('sodino_old_input_' . get_current_user_id(), $oldInput, 120);
} else {
delete_transient('sodino_old_input_' . get_current_user_id());
}
wp_safe_redirect($url);
exit;
}
private function getBackUrl($fallbackPage) {
return wp_get_referer() ?: admin_url('admin.php?page=' . $fallbackPage);
}
private function requireValue($value, $message, $fallbackPage) {
if (is_string($value)) {
$value = trim($value);
}
if ($value === '' || $value === null) {
$this->redirectWithNotice($this->getBackUrl($fallbackPage), $message, 'error');
}
return $value;
}
private function normalizeDatetime($value) {
$value = trim((string) $value);
if ($value === '') {
return null;
}
$timestamp = strtotime($value);
return $timestamp ? date('Y-m-d H:i:s', $timestamp) : null;
}
/**
* Rules admin page
*/
public function rulesPage() {
$this->listRulesPage();
}
/**
* Dashboard page
*/
public function dashboardPage() {
$settings = $this->getSettings();
$analyticsService = new \Sodino\Services\AnalyticsService(new \Sodino\Repositories\EventRepository(), $this->ruleRepository);
$filters = [
'range' => isset($_GET['range']) ? sanitize_text_field($_GET['range']) : '7d',
'start_date' => isset($_GET['start_date']) ? sanitize_text_field($_GET['start_date']) : '',
'end_date' => isset($_GET['end_date']) ? sanitize_text_field($_GET['end_date']) : '',
'product_id' => isset($_GET['product_id']) ? intval($_GET['product_id']) : 0,
'category_id' => isset($_GET['category_id']) ? intval($_GET['category_id']) : 0,
];
if (!empty($filters['product_id'])) {
$filters['product_ids'] = [$filters['product_id']];
}
$dashboardData = $analyticsService->getDashboardData($filters);
$productOptions = $analyticsService->getProductOptions();
$categoryOptions = $analyticsService->getCategoryOptions();
include SODINO_PLUGIN_DIR . 'admin/views/dashboard.php';
}
/**
* List rules page
*/
private function listRulesPage() {
require_once SODINO_PLUGIN_DIR . 'admin/class-rules-list-table.php';
$rulesTable = new \Sodino_Rules_List_Table($this->ruleRepository);
$rulesTable->prepare_items();
include SODINO_PLUGIN_DIR . 'admin/views/rules-list.php';
}
/**
* Add or edit rule page
*/
public function addRulePage() {
if (isset($_GET['action']) && $_GET['action'] === 'edit') {
return $this->editRulePage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveRule();
} else {
$rule = new Rule();
include SODINO_PLUGIN_DIR . 'admin/views/rule-form.php';
}
}
/**
* Settings page
*/
public function settingsPage() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveSettings();
}
$settings = $this->getSettings();
include SODINO_PLUGIN_DIR . 'admin/views/settings.php';
}
/**
* Upsell list page
*/
public function upsellsPage() {
$this->listUpsellsPage();
}
/**
* Add or edit upsell page
*/
public function addUpsellPage() {
if (isset($_GET['action']) && $_GET['action'] === 'edit') {
return $this->editUpsellPage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveUpsell();
} else {
$upsell = new Upsell();
include SODINO_PLUGIN_DIR . 'admin/views/upsell-form.php';
}
}
/**
* Banners list page
*/
public function bannersPage() {
$this->listBannersPage();
}
/**
* Add or edit banner page
*/
public function addBannerPage() {
if (isset($_GET['action']) && $_GET['action'] === 'edit') {
return $this->editBannerPage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveBanner();
} else {
$banner = new Banner();
include SODINO_PLUGIN_DIR . 'admin/views/banner-form.php';
}
}
/**
* Tools and health page
*/
public function toolsPage() {
$toolsData = $this->getToolsData();
include SODINO_PLUGIN_DIR . 'admin/views/tools.php';
}
public function handleToolsActions() {
if (!current_user_can('manage_options')) {
return;
}
if (($_GET['page'] ?? '') !== 'sodino-tools' || empty($_GET['tool_action'])) {
return;
}
$action = sanitize_key($_GET['tool_action']);
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'sodino_tools_' . $action)) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
if ($action === 'clear_cache') {
\Sodino\Core\Cache::getInstance()->clearAll();
$this->redirectWithNotice(admin_url('admin.php?page=sodino-tools'), __('کش سودینو با موفقیت پاک شد.', 'sodino'), 'success');
}
if ($action === 'run_migrations') {
require_once SODINO_PLUGIN_DIR . 'database/migrations.php';
sodino_create_tables();
$this->redirectWithNotice(admin_url('admin.php?page=sodino-tools'), __('ساختار دیتابیس سودینو بررسی و به‌روزرسانی شد.', 'sodino'), 'success');
}
if ($action === 'prune_events') {
$deleted = $this->deleteOldEvents(90);
$this->redirectWithNotice(
admin_url('admin.php?page=sodino-tools'),
sprintf(__('پاک‌سازی انجام شد. %d رویداد قدیمی حذف شد.', 'sodino'), $deleted),
'success'
);
}
wp_safe_redirect(admin_url('admin.php?page=sodino-tools'));
exit;
}
private function listUpsellsPage() {
require_once SODINO_PLUGIN_DIR . 'admin/class-upsell-list-table.php';
$upsellTable = new \Sodino_Upsell_List_Table($this->upsellRepository);
$upsellTable->prepare_items();
include SODINO_PLUGIN_DIR . 'admin/views/upsell-list.php';
}
private function listBannersPage() {
require_once SODINO_PLUGIN_DIR . 'admin/class-banner-list-table.php';
$bannerTable = new \Sodino_Banner_List_Table($this->bannerRepository);
$bannerTable->prepare_items();
include SODINO_PLUGIN_DIR . 'admin/views/banner-list.php';
}
private function editBannerPage() {
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$banner = $this->bannerRepository->getById($id);
if (!$banner) {
wp_die(__('بنر پیدا نشد', 'sodino'));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveBanner($banner);
} else {
include SODINO_PLUGIN_DIR . 'admin/views/banner-form.php';
}
}
private function editUpsellPage() {
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$upsell = $this->upsellRepository->getById($id);
if (!$upsell) {
wp_die(__('Upsell not found', 'sodino'));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveUpsell($upsell);
} else {
include SODINO_PLUGIN_DIR . 'admin/views/upsell-form.php';
}
}
private function saveUpsell($upsell = null) {
if (!current_user_can('manage_options')) {
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
}
if (!isset($_POST['sodino_upsell_nonce']) || !wp_verify_nonce($_POST['sodino_upsell_nonce'], 'sodino_save_upsell')) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
if (!$upsell) {
$upsell = new Upsell();
}
$triggerType = sanitize_key($_POST['trigger_type'] ?? 'product');
if (!in_array($triggerType, $this->allowedUpsellTriggerTypes, true)) {
$triggerType = 'product';
}
$triggerValues = isset($_POST['trigger_values']) && is_array($_POST['trigger_values']) ? wp_unslash($_POST['trigger_values']) : [];
$triggerValue = sanitize_text_field($triggerValues[$triggerType] ?? '');
$targetProductId = max(0, intval($_POST['target_product_id'] ?? 0));
$discountType = sanitize_key($_POST['discount_type'] ?? 'percentage');
if (!in_array($discountType, $this->allowedUpsellDiscountTypes, true)) {
$discountType = 'percentage';
}
$title = sanitize_text_field($_POST['title'] ?? '');
$this->requireValue($title, __('عنوان آپسل الزامی است.', 'sodino'), 'sodino-add-upsell');
$this->requireValue($triggerValue, __('مقدار شرط آپسل را انتخاب یا وارد کنید.', 'sodino'), 'sodino-add-upsell');
if ($targetProductId <= 0) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-upsell'), __('محصول پیشنهادی آپسل الزامی است.', 'sodino'), 'error');
}
$upsell->title = $title;
$upsell->trigger_type = $triggerType;
$upsell->trigger_value = $triggerValue;
$upsell->target_product_id = max(0, intval($_POST['target_product_id'] ?? 0));
$upsell->discount_type = $discountType;
$upsell->discount_value = max(0, floatval($_POST['discount_value'] ?? 0));
if ($discountType === 'percentage') {
$upsell->discount_value = min(100, $upsell->discount_value);
}
$upsell->priority = max(1, intval($_POST['priority'] ?? 10));
$upsell->status = isset($_POST['status']) ? 1 : 0;
$id = $this->upsellRepository->save($upsell);
if (!$id) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-upsell'), __('ذخیره آپسل انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
}
$this->redirectWithNotice(admin_url('admin.php?page=sodino-upsells'), __('آپسل با موفقیت ذخیره شد.', 'sodino'), 'success');
}
private function saveBanner($banner = null) {
if (!current_user_can('manage_options')) {
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
}
if (!isset($_POST['sodino_banner_nonce']) || !wp_verify_nonce($_POST['sodino_banner_nonce'], 'sodino_save_banner')) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
if (!$banner) {
$banner = new Banner();
}
$contentType = sanitize_key($_POST['content_type'] ?? 'image');
if (!in_array($contentType, $this->allowedBannerContentTypes, true)) {
$contentType = 'image';
}
$contentValues = isset($_POST['content_values']) && is_array($_POST['content_values']) ? wp_unslash($_POST['content_values']) : [];
$contentValue = $contentValues[$contentType] ?? '';
$contentValue = $contentType === 'image' ? esc_url_raw($contentValue) : wp_kses_post($contentValue);
$title = sanitize_text_field($_POST['title'] ?? '');
$this->requireValue($title, __('عنوان بنر الزامی است.', 'sodino'), 'sodino-add-banner');
$this->requireValue($contentValue, __('محتوای بنر الزامی است.', 'sodino'), 'sodino-add-banner');
if ($contentType === 'image' && !filter_var($contentValue, FILTER_VALIDATE_URL)) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('آدرس تصویر بنر معتبر نیست.', 'sodino'), 'error');
}
$startTime = $this->normalizeDatetime($_POST['start_time'] ?? '');
$endTime = $this->normalizeDatetime($_POST['end_time'] ?? '');
if ($startTime && $endTime && strtotime($endTime) < strtotime($startTime)) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('تاریخ پایان بنر نباید قبل از تاریخ شروع باشد.', 'sodino'), 'error');
}
$position = sanitize_key($_POST['position'] ?? 'top');
$displayType = sanitize_key($_POST['display_type'] ?? 'inline');
$userTarget = sanitize_key($_POST['user_target'] ?? 'all');
$deviceTarget = sanitize_key($_POST['device_target'] ?? 'all');
$banner->title = $title;
$banner->content_type = $contentType;
$banner->content_value = $contentValue;
$banner->link_url = esc_url_raw($_POST['link_url'] ?? '');
$banner->position = in_array($position, $this->allowedBannerPositions, true) ? $position : 'top';
$banner->display_type = in_array($displayType, $this->allowedBannerDisplayTypes, true) ? $displayType : 'inline';
$banner->start_time = $startTime;
$banner->end_time = $endTime;
$banner->user_target = in_array($userTarget, $this->allowedBannerUserTargets, true) ? $userTarget : 'all';
$banner->device_target = in_array($deviceTarget, $this->allowedBannerDeviceTargets, true) ? $deviceTarget : 'all';
$banner->priority = max(1, intval($_POST['priority'] ?? 10));
$banner->status = isset($_POST['status']) ? 1 : 0;
$id = $this->bannerRepository->save($banner);
if (!$id) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('ذخیره بنر انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
}
$this->redirectWithNotice(admin_url('admin.php?page=sodino-banners'), __('بنر با موفقیت ذخیره شد.', 'sodino'), 'success');
}
public function handleUpsellActions() {
if (!current_user_can('manage_options')) {
return;
}
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_upsell', 'toggle_upsell_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
return;
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if (!$id) {
return;
}
if ($_GET['action'] === 'delete_upsell') {
$this->upsellRepository->delete($id);
wp_safe_redirect(admin_url('admin.php?page=sodino-upsells'));
exit;
}
if ($_GET['action'] === 'toggle_upsell_status') {
$upsell = $this->upsellRepository->getById($id);
if ($upsell) {
$upsell->status = $upsell->status ? 0 : 1;
$this->upsellRepository->save($upsell);
}
wp_safe_redirect(admin_url('admin.php?page=sodino-upsells'));
exit;
}
}
public function handleBannerActions() {
if (!current_user_can('manage_options')) {
return;
}
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_banner', 'toggle_banner_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
return;
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if (!$id) {
return;
}
if ($_GET['action'] === 'delete_banner') {
$this->bannerRepository->delete($id);
wp_safe_redirect(admin_url('admin.php?page=sodino-banners'));
exit;
}
if ($_GET['action'] === 'toggle_banner_status') {
$banner = $this->bannerRepository->getById($id);
if ($banner) {
$banner->status = $banner->status ? 0 : 1;
$this->bannerRepository->save($banner);
}
wp_safe_redirect(admin_url('admin.php?page=sodino-banners'));
exit;
}
}
public function searchProductsAjax() {
if (!current_user_can('manage_options')) {
wp_send_json([]);
}
if (!check_ajax_referer('sodino_search_products', 'security', false)) {
wp_send_json([]);
}
$term = sanitize_text_field($_POST['term'] ?? '');
if (empty($term) || !function_exists('wc_get_products')) {
wp_send_json([]);
}
$products = wc_get_products([
'limit' => 10,
'status' => 'publish',
'search' => $term,
]);
$results = [];
foreach ($products as $product) {
$results[] = [
'id' => $product->get_id(),
'label' => $product->get_name(),
];
}
wp_send_json($results);
}
private function getToolsData() {
global $wpdb;
$tables = [
'rules' => [
'label' => __('قوانین قیمت‌گذاری', 'sodino'),
'name' => $wpdb->prefix . 'sodino_rules',
],
'upsells' => [
'label' => __('آپسل‌ها', 'sodino'),
'name' => $wpdb->prefix . 'sodino_upsells',
],
'banners' => [
'label' => __('بنرها', 'sodino'),
'name' => $wpdb->prefix . 'sodino_banners',
],
'events' => [
'label' => __('رویدادهای تحلیلی', 'sodino'),
'name' => $wpdb->prefix . 'sodino_events',
],
'analytics_cache' => [
'label' => __('کش تحلیلی', 'sodino'),
'name' => $wpdb->prefix . 'sodino_analytics_cache',
],
];
foreach ($tables as $key => $table) {
$exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table['name']));
$tables[$key]['exists'] = (bool) $exists;
$tables[$key]['count'] = $exists ? (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table['name']}") : 0;
}
$eventsTable = $tables['events']['name'];
$oldEventCount = 0;
$oldestEvent = '';
if ($tables['events']['exists']) {
$cutoff = date('Y-m-d H:i:s', current_time('timestamp') - (90 * DAY_IN_SECONDS));
$oldEventCount = (int) $wpdb->get_var(
$wpdb->prepare("SELECT COUNT(*) FROM {$eventsTable} WHERE created_at < %s", $cutoff)
);
$oldestEvent = (string) $wpdb->get_var("SELECT MIN(created_at) FROM {$eventsTable}");
}
return [
'db_version' => get_option('sodino_db_version', '0'),
'expected_db_version' => defined('SODINO_DB_VERSION') ? SODINO_DB_VERSION : SODINO_VERSION,
'settings' => $this->getSettings(),
'tables' => $tables,
'old_event_count' => $oldEventCount,
'oldest_event' => $oldestEvent,
'actions' => [
'clear_cache' => wp_nonce_url(admin_url('admin.php?page=sodino-tools&tool_action=clear_cache'), 'sodino_tools_clear_cache'),
'run_migrations' => wp_nonce_url(admin_url('admin.php?page=sodino-tools&tool_action=run_migrations'), 'sodino_tools_run_migrations'),
'prune_events' => wp_nonce_url(admin_url('admin.php?page=sodino-tools&tool_action=prune_events'), 'sodino_tools_prune_events'),
],
];
}
private function deleteOldEvents($days) {
global $wpdb;
$days = max(1, (int) $days);
$eventsTable = $wpdb->prefix . 'sodino_events';
$exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $eventsTable));
if (!$exists) {
return 0;
}
$cutoff = date('Y-m-d H:i:s', current_time('timestamp') - ($days * DAY_IN_SECONDS));
$deleted = $wpdb->query($wpdb->prepare("DELETE FROM {$eventsTable} WHERE created_at < %s", $cutoff));
return $deleted === false ? 0 : (int) $deleted;
}
private function getSettingsDefaults() {
return [
'plugin_enabled' => 1,
'pricing_enabled' => 1,
'upsell_enabled' => 1,
'banner_enabled' => 1,
'allow_multiple_rules' => 0,
'strategy' => 'priority',
'max_discount_percent' => 100,
'min_product_price' => 0,
'cache_enabled' => 1,
'cache_duration' => 3600,
'ab_testing_enabled' => 0,
'cart_pricing_enabled' => 1,
'scheduled_campaigns_enabled' => 1,
'debug_mode' => 0,
];
}
private function getSettings() {
return wp_parse_args(get_option('sodino_settings', []), $this->getSettingsDefaults());
}
private function saveSettings() {
if (!current_user_can('manage_options')) {
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
}
if (!isset($_POST['sodino_settings_nonce']) || !wp_verify_nonce($_POST['sodino_settings_nonce'], 'sodino_save_settings')) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
$strategy = sanitize_key($_POST['strategy'] ?? 'priority');
if (!in_array($strategy, $this->allowedStrategies, true)) {
$strategy = 'priority';
}
$settings = [
'plugin_enabled' => isset($_POST['plugin_enabled']) ? 1 : 0,
'pricing_enabled' => isset($_POST['pricing_enabled']) ? 1 : 0,
'upsell_enabled' => isset($_POST['upsell_enabled']) ? 1 : 0,
'banner_enabled' => isset($_POST['banner_enabled']) ? 1 : 0,
'allow_multiple_rules' => isset($_POST['allow_multiple_rules']) ? 1 : 0,
'strategy' => $strategy,
'max_discount_percent' => max(0, min(100, floatval($_POST['max_discount_percent'] ?? 100))),
'min_product_price' => max(0, floatval($_POST['min_product_price'] ?? 0)),
'cache_enabled' => isset($_POST['cache_enabled']) ? 1 : 0,
'cache_duration' => max(60, intval($_POST['cache_duration'] ?? 3600)),
'ab_testing_enabled' => isset($_POST['ab_testing_enabled']) ? 1 : 0,
'cart_pricing_enabled' => isset($_POST['cart_pricing_enabled']) ? 1 : 0,
'scheduled_campaigns_enabled' => isset($_POST['scheduled_campaigns_enabled']) ? 1 : 0,
'debug_mode' => isset($_POST['debug_mode']) ? 1 : 0,
];
update_option('sodino_settings', $settings);
\Sodino\Core\Cache::getInstance()->clearAll();
$this->redirectWithNotice(add_query_arg('updated', 'true', admin_url('admin.php?page=sodino-settings')), __('تنظیمات با موفقیت ذخیره شد.', 'sodino'), 'success');
}
/**
* Edit rule page
*/
private function editRulePage() {
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$rule = $this->ruleRepository->getById($id);
if (!$rule) {
wp_die(__('Rule not found', 'sodino'));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->saveRule($rule);
} else {
include SODINO_PLUGIN_DIR . 'admin/views/rule-form.php';
}
}
/**
* Save rule
*/
private function saveRule($rule = null) {
if (!current_user_can('manage_options')) {
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
}
if (!isset($_POST['sodino_rule_nonce']) || !wp_verify_nonce($_POST['sodino_rule_nonce'], 'sodino_save_rule')) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
if (!$rule) {
$rule = new Rule();
}
$name = sanitize_text_field($_POST['name'] ?? '');
$this->requireValue($name, __('عنوان قانون الزامی است.', 'sodino'), 'sodino-add-rule');
$conditionType = sanitize_key($_POST['condition_type'] ?? 'user_type');
if (!in_array($conditionType, $this->allowedRuleConditionTypes, true)) {
$conditionType = 'user_type';
}
$conditionValue = sanitize_text_field($_POST['condition_value'] ?? '');
$this->requireValue($conditionValue, __('مقدار شرط قانون الزامی است.', 'sodino'), 'sodino-add-rule');
$actionType = sanitize_key($_POST['action_type'] ?? 'discount_percent');
if (!in_array($actionType, $this->allowedRuleActionTypes, true)) {
$actionType = 'discount_percent';
}
$actionValue = sanitize_text_field($_POST['action_value'] ?? '0');
if ($actionType !== 'free_shipping' && floatval($actionValue) <= 0) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-rule'), __('مقدار عملیات باید بزرگ‌تر از صفر باشد.', 'sodino'), 'error');
}
$rule->name = $name;
$rule->priority = max(1, intval($_POST['priority'] ?? 10));
$rule->usage_limit = max(0, intval($_POST['usage_limit'] ?? 0));
$rule->user_roles = array_map('sanitize_text_field', (array) ($_POST['user_roles'] ?? []));
$rule->conditions = [
[
'type' => $conditionType,
'value' => $conditionValue,
],
];
$rule->actions = [
[
'type' => $actionType,
'value' => $actionValue,
],
];
$rule->syncLegacyFields();
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
$id = $this->ruleRepository->save($rule);
if (!$id) {
$this->redirectWithNotice($this->getBackUrl('sodino-add-rule'), __('ذخیره قانون انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
}
$this->redirectWithNotice(admin_url('admin.php?page=sodino-rules'), __('قانون با موفقیت ذخیره شد.', 'sodino'), 'success');
}
/**
* Handle delete action
*/
public function handleDelete() {
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'delete_rule')) {
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$this->ruleRepository->delete($id);
wp_safe_redirect(admin_url('admin.php?page=sodino-rules'));
exit;
}
}