براتی طراح سایت

افزودن یادداشت به پیشخوان وردپرس

مشخصات مقاله:
بروزرسانی: شهریور ۱۴۰۴
افزودن یادداشت به پیشخوان وردپرس
فهرست مطالب

خلاصه:

با یک افزونه ساده و رایگان تمام یادداشت ها برای خودتان و دیگران را مدیریت کنید

افزودن یادداشت به پیشخوان وردپرس: مدیریت هوشمندتر 

پیشخوان وردپرس شما هم شلوغ و به‌هم‌ریخته است؟ 🤯 برگه‌های یادداشت چسبان روی مانیتور، فایل‌های متنی پراکنده روی دسکتاپ و یادآوری‌های مختلف در اپلیکیشن‌های گوناگون… بیایید روراست باشیم، مدیریت یک وب‌سایت، به‌خصوص زمانی که چندین پروژه در دست داریم، می‌تواند به یک کابوس تبدیل شود.

اما خبر خوب اینجاست که برای نظم بخشیدن به این آشفتگی، یک راه‌حل ساده و قدرتمند وجود دارد: افزودن یادداشت به پیشخوان وردپرس با یک ابزار مناسب. در این مقاله، می‌خواهیم افزونه‌ای را به شما معرفی کنیم که نه تنها این کار را به بهترین شکل انجام می‌دهد، بلکه مجموعه‌ای از ابزارهای کاربردی دیگر را نیز مستقیماً به قلب وردپرس شما می‌آورد.

افزودن یادداشت به وردپرس

اهمیت افزودن یادداشت به پیشخوان وردپرس

شاید در نگاه اول، داشتن یک دفترچه یادداشت در پیشخوان وردپرس یک ویژگی لوکس به نظر برسد. اما وقتی عمیق‌تر به آن فکر می‌کنیم، متوجه می‌شویم که این قابلیت چقدر می‌تواند در افزایش بهره‌وری و کاهش خطاها مؤثر باشد.

  • تمرکز، کلید موفقیت: وقتی تمام وظایف، ایده‌ها و یادآوری‌هایتان در همان محیطی باشد که کار می‌کنید (یعنی پیشخوان وردپرس)، دیگر نیازی به جابجایی بین نرم‌افزارهای مختلف ندارید. این یعنی تمرکز بیشتر و حواس‌پرتی کمتر.

  • هیچ‌چیز را فراموش نکنید: ایده‌ای برای مقاله بعدی به ذهنتان رسیده؟ باید یک افزونه را بروزرسانی کنید؟ یا محتوای یک برگه خاص را تغییر دهید؟ همه این‌ها را می‌توانید در لحظه یادداشت کنید تا از قلم نیفتند.

  • مدیریت وظایف روزانه: با قابلیت اولویت‌بندی و تعیین وضعیت، یادداشت‌های شما از یک متن ساده به یک لیست وظایف (To-Do List) هوشمند تبدیل می‌شوند.

در واقع، افزودن یادداشت به پیشخوان وردپرس فقط یک قابلیت فانتزی نیست، بلکه یک استراتژی برای افزایش بهره‌وری و مدیریت متمرکز وظایف است.

افزونه Notex نوتکس فراتر از یک دفترچه 

افزونه‌ای که امروز معرفی می‌کنیم، یک ابزار ساده برای یادداشت‌برداری نیست. این یک دستیار همه‌کاره است که با ظرافت در پیشخوان شما جای می‌گیرد. بیایید با ویژگی‌های جذاب آن آشنا شویم:

۱. سیستم یادداشت‌برداری حرفه‌ای 🚦

شما می‌توانید برای هر یادداشت، اولویت (بالا، متوسط، کم) و وضعیت (در حال انجام، تمام شده، بدون تغییر) تعریف کنید. این ویژگی به شما کمک می‌کند تا به سرعت مهم‌ترین کارها را شناسایی کرده و پیشرفت آن‌ها را دنبال کنید. سیستم صفحه‌بندی هوشمند نیز از شلوغ شدن محیط جلوگیری می‌کند و دسترسی به یادداشت‌های قدیمی را آسان می‌سازد.

افزودن یادداشت به پیشخوان وردپرس

۲. ابزارهای جانبی یکپارچه 🗺️

این افزونه فراتر از انتظار عمل می‌کند و ابزارهای زیر را در کنار دفترچه یادداشت به شما هدیه می‌دهد:

  • وضعیت آب و هوا (Weather): 🌦️ با انتخاب شهر خود، همیشه از دمای هوای لحظه‌ای باخبر باشید.

  • اوقات شرعی (Prayer Times): 🕌 برای کاربران ایرانی، نمایش اوقات شرعی روز یک قابلیت بسیار کاربردی و محترم است.

  • تقویم شمسی و میلادی: 📅 یک تقویم جمع‌وجور و زیبا که تاریخ روز را به هر دو صورت شمسی و میلادی نمایش می‌دهد.

  • صفحه بندی: گزینه ها یادداشت زیاد بشه = صففحه بندی فعال میشه

 

فرآیند افزودن یادداشت به پیشخوان وردپرس با این افزونه به شکل شگفت‌انگیزی ساده است.

  1. نصب و فعال‌سازی: مانند هر افزونه دیگری، آن را نصب و فعال کنید.

  2. پیدا کردن ویجت: بلافاصله پس از فعال‌سازی، ویجت زیبای “Notex tools wp” در بالای پیشخوان وردپرس شما ظاهر می‌شود.

  3. شروع استفاده: در تب “نوشتن یادداشت”، متن خود را وارد کنید، اولویت و وضعیت آن را مشخص کرده و روی دکمه “ذخیره” کلیک کنید. به همین راحتی!

  4. کاوش در تب‌ها: می‌توانید یادداشت‌های خود را بر اساس اولویت فیلتر کنید یا به تب‌های اوقات شرعی و تقویم بروید و از امکانات دیگر لذت ببرید.

  • ۵.یکپارچگی کامل: تمام ابزارها (یادداشت، آب و هوا، تقویم و…) در یک ویجت واحد و هماهنگ قرار دارند.
  •  
  • ۶.طراحی شده برای کاربر ایرانی: توجه به جزئیاتی مانند تقویم شمسی و اوقات شرعی، نشان‌دهنده احترامی است که سازنده برای مخاطب ایرانی قائل شده است.
  •  
  • ۷.سبک و بهینه: کدنویسی استاندارد و بهینه افزونه تضمین می‌کند که هیچ تأثیر منفی بر سرعت سایت شما نخواهد داشت. 🚀

دانلود افزونه Notex نوتکس

برای دانلود افزونه Notex نوتکس روی این گزینه کلیک کنید

افزونه Notex نوتکس

یا بهترکه کدهاشو توی افزونه wp code قرار دهید

<?php
/*
Plugin Name: دفترچه یادداشت وردپرس
Plugin URI: https://Mrbarati.com
Description: ویجت پیشخوان: یادداشت، آب و هوا، اوقات شرعی و تقویم ساده — نسخه نهایی با تنظیمات کاربر.
Version: 1.0.2
Author: مهندس براتی
Author URI: https://Mrbarati.com
License: GPLv2 or later
*/

// جلوگیری از دسترسی مستقیم به فایل
if (!defined('ABSPATH')) exit;

class Notex_Tools_Wp {

    // آرایه‌ای برای نگهداری نام شهرها
    private $cities_data = [];

    public function __construct() {
        // لیست کامل مراکز ۳۱ استان ایران (مرتب شده بر اساس الفبا)
        $this->cities_data = [
            'Arak' => 'اراک', 'Ardabil' => 'اردبیل', 'Urmia' => 'ارومیه', 'Isfahan' => 'اصفهان',
            'Ahvaz' => 'اهواز', 'Ilam' => 'ایلام', 'Bojnurd' => 'بجنورد', 'BandarAbbas' => 'بندرعباس',
            'Bushehr' => 'بوشهر', 'Birjand' => 'بیرجند', 'Tabriz' => 'تبریز', 'Tehran' => 'تهران',
            'Khorramabad' => 'خرم‌آباد', 'Rasht' => 'رشت', 'Zahedan' => 'زاهدان', 'Zanjan' => 'زنجان',
            'Sari' => 'ساری', 'Semnan' => 'سمنان', 'Sanandaj' => 'سنندج', 'Shahrekord' => 'شهرکرد',
            'Shiraz' => 'شیراز', 'Qazvin' => 'قزوین', 'Qom' => 'قم', 'Karaj' => 'کرج',
            'Kerman' => 'کرمان', 'Kermanshah' => 'کرمانشاه', 'Gorgan' => 'گرگان', 'Mashhad' => 'مشهد',
            'Hamedan' => 'همدان', 'Yasuj' => 'یاسوج', 'Yazd' => 'یزد'
        ];

        // ثبت هوک‌ها (قلاب‌ها) در وردپرس
        add_action('wp_dashboard_setup', array($this, 'add_dashboard_widget'));
        add_action('wp_ajax_notex_tools_wp_action', array($this, 'handle_ajax_actions'));
        register_activation_hook(__FILE__, array($this, 'activate_plugin'));
        register_uninstall_hook(__FILE__, array('Notex_Tools_Wp', 'uninstall_plugin'));
    }

    // تابع فعال‌سازی: ایجاد جدول در دیتابیس
    public function activate_plugin() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'notex_tools_wp_notes';
        $charset_collate = $wpdb->get_charset_collate();
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta("CREATE TABLE {$table_name} (
            id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            created_at DATETIME NOT NULL,
            note_content LONGTEXT NOT NULL,
            priority VARCHAR(20) NOT NULL DEFAULT 'medium',
            status VARCHAR(20) NOT NULL DEFAULT 'no_change',
            PRIMARY KEY (id)
        ) {$charset_collate};");
    }

    // تابع حذف افزونه: پاک کردن کامل جدول از دیتابیس
    public static function uninstall_plugin() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'notex_tools_wp_notes';
        $wpdb->query("DROP TABLE IF EXISTS {$table_name}");
    }

    /* ---------- توابع کمکی ---------- */
    
    // توابع تبدیل تاریخ برای حالت جایگزین (Fallback)
    private function gregorian_to_jalali($g_y,$g_m,$g_d) {
        $g_days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
        $j_days_in_month = array(31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);
        $gy = $g_y - 1600; $gm = $g_m - 1; $gd = $g_d - 1;
        $g_day_no = 365 * $gy + floor(($gy + 3) / 4) - floor(($gy + 99) / 100) + floor(($gy + 399) / 400);
        for ($i = 0; $i < $gm; ++$i) $g_day_no += $g_days_in_month[$i];
        if ($gm > 1 && (($g_y % 4 == 0 && $g_y % 100 != 0) || ($g_y % 400 == 0))) $g_day_no++;
        $g_day_no += $gd; $j_day_no = $g_day_no - 79;
        $j_np = floor($j_day_no / 12053); $j_day_no %= 12053;
        $jy = 979 + 33 * $j_np + 4 * floor($j_day_no / 1461); $j_day_no %= 1461;
        if ($j_day_no >= 366) { $jy += floor(($j_day_no - 1) / 365); $j_day_no = ($j_day_no - 1) % 365; }
        for ($i = 0; $i < 11 && $j_day_no >= $j_days_in_month[$i]; ++$i) $j_day_no -= $j_days_in_month[$i];
        $jm = $i + 1; $jd = $j_day_no + 1;
        return array($jy, $jm, $jd);
    }
    
    // تابع اصلاح شده و دقیق برای تبدیل شمسی به میلادی
    private function jalali_to_gregorian($j_y, $j_m, $j_d) {
        $j_y = intval($j_y); $j_m = intval($j_m); $j_d = intval($j_d);
        $jy = $j_y - 979; $jm = $j_m - 1; $jd = $j_d - 1;
        $j_day_no = 365 * $jy + intval($jy / 33) * 8 + intval((($jy % 33) + 3) / 4);
        $j_days_in_month = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
        for ($i = 0; $i < $jm; ++$i) $j_day_no += $j_days_in_month[$i];
        $j_day_no += $jd;
        $g_day_no = $j_day_no + 79;
        $gy = 1600 + 400 * intval($g_day_no / 146097);
        $g_day_no = $g_day_no % 146097;
        $leap = true;
        if ($g_day_no >= 36525) {
            $g_day_no--;
            $gy += 100 * intval($g_day_no / 36524);
            $g_day_no = $g_day_no % 36524;
            if ($g_day_no >= 365) $g_day_no++;
            else $leap = false;
        }
        $gy += 4 * intval($g_day_no / 1461);
        $g_day_no %= 1461;
        if ($g_day_no >= 366) {
            $leap = false;
            $g_day_no--;
            $gy += intval($g_day_no / 365);
            $g_day_no = $g_day_no % 365;
        }
        $g_days_in_month = [31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        for ($i = 0; $g_day_no >= $g_days_in_month[$i]; $i++) $g_day_no -= $g_days_in_month[$i];
        $gm = $i + 1;
        $gd = $g_day_no + 1;
        return [$gy, $gm, $gd];
    }

    private function format_jalali($jy,$jm,$jd) {
        $map = [1=>'فروردین',۲=>'اردیبهشت',۳=>'خرداد',۴=>'تیر',۵=>'مرداد',۶=>'شهریور',۷=>'مهر',۸=>'آبان',۹=>'آذر',۱۰=>'دی',۱۱=>'بهمن',۱۲=>'اسفند'];
        $week_days = ['یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'];
        $day_of_week = $week_days[date('w', current_time('timestamp'))];
        return $day_of_week . '، ' . sprintf('%02d %s %04d', $jd, $map[$jm], $jy);
    }

    private function get_time_ir_data() {
        $transient_key = 'notex_time_ir_data';
        $cached_data = get_transient($transient_key);
        if (false !== $cached_data) {
            return $cached_data;
        }
        $response = wp_remote_get('https://time.ir/api/Fa/Time/GetNow', ['timeout' => 10]);
        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
            return null;
        }
        $data = json_decode(wp_remote_retrieve_body($response), true);
        if (is_array($data) && isset($data['year'])) {
            set_transient($transient_key, $data, 60);
            return $data;
        }
        return null;
    }

    private function excerpt_words($text, $limit = 5) {
        return wp_trim_words(wp_strip_all_tags($text), $limit, '...');
    }

    public function add_dashboard_widget() {
        wp_add_dashboard_widget('notex_tools_wp_dashboard_widget', 'Notex tools wp', array($this, 'render_dashboard_widget'));
    }

    public function render_dashboard_widget() {
        $time_data = $this->get_time_ir_data();
        if ($time_data) {
            $jalali_display = $time_data['fullDate'];
            $gregorian_timestamp = strtotime($time_data['gregorianDate']);
            $gregorian_display_formatted = date('Y - m - d', $gregorian_timestamp);
            $gregorian_day_of_week = $time_data['dayOfWeek'];
            $initial_clock = $time_data['hour'] . ':' . $time_data['minute'] . ':' . $time_data['second'];
        } else {
            $now = current_time('timestamp');
            list($g_y, $g_m, $g_d) = [date('Y', $now), date('n', $now), date('j', $now)];
            list($jy, $jm, $jd) = $this->gregorian_to_jalali($g_y, $g_m, $g_d);
            $jalali_display = $this->format_jalali($jy, $jm, $jd);
            $gregorian_display_formatted = date('Y - m - d', $now);
            $gregorian_day_of_week = date('l', $now);
            $initial_clock = date('H:i:s', $now);
        }
        ?>
        <style>
            #notex_tools_wp_dashboard_widget .hndle,
            #notex_tools_wp_dashboard_widget .handle-order-higher,
            #notex_tools_wp_dashboard_widget .handle-order-lower { display: none !important; }
            #notex_tools_wp_dashboard_widget .inside { margin: 0; padding: 0; }
            .notex-wrapper { display:flex; direction:rtl; background:#f7f3fb; border:1px solid #e3dfe8; border-radius:8px; overflow:hidden; }
            .notex-col { padding:20px; box-sizing:border-box; display:flex; flex-direction:column; }
            .notex-col-main { width: 65%; background:#fff; }
            .notex-col-sidebar { width: 35%; border-left:1px solid #e3dfe8; justify-content: space-between; gap: 20px; text-align: center; }
            .notex-tabs { display:flex; flex-wrap:nowrap; overflow-x: auto; gap:8px; border-bottom:1px solid #e9e6ee; padding-bottom:10px; margin-bottom:12px; }
            .notex-tab-link { padding:8px 12px; background:#f4eef8; border:1px solid #e9e6ee; border-radius:8px; cursor:pointer; white-space: nowrap; }
            .notex-tab-link.active { background:#8a2be2; color:#fff; border-color:#8a2be2; }
            .time-box .clock { font-size:3.2rem; font-weight:800; line-height:1.1; margin-bottom: 10px; }
            .time-box .date { font-size:1rem; color:#333; font-weight:700; line-height: 1.8; }
            .weather-box { background:#fff; border-radius:8px; padding:15px; border:1px solid #e9e6ee; }
            #note-content { width:100%; min-height:160px; padding:12px; border-radius:8px; border:1px solid #e9e6ee; resize:vertical; margin-bottom: 10px;}
            .note-form-dropdowns { display: flex; gap: 10px; margin-bottom: 10px; }
            .note-form-dropdowns select { flex-grow: 1; padding: 10px; border-radius: 8px; border: 1px solid #e9e6ee; background-color: #fff; }
            #save-note-btn { width: 100%; background:#1e88e5; color:#fff; padding:12px; border-radius:8px; border:none; cursor:pointer; font-weight:700; }
            .notes-list .note-list-item { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:10px 6px; border-bottom:1px solid #f0eef3; }
            .note-left { display:flex; align-items:center; gap:10px; min-width:0; flex-grow: 1; }
            .note-text-content { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
            .note-meta { display:flex; gap:8px; align-items:center; direction:ltr; justify-content:flex-end; font-size:0.9em; color:#555; }
            .note-delete { background:#e74c3c; color:#fff; padding:6px 10px; border-radius:6px; text-decoration:none; }
            .note-edit { background:#8a2be2; color:#fff; padding:6px 10px; border-radius:6px; text-decoration:none; }
            .priority-dot { width:12px; height:12px; border-radius:50%; flex-shrink:0; display: inline-block; margin-left: 5px; }
            .priority-dot.high{ background:#e74c3c; } .priority-dot.medium{ background:#f39c12; } .priority-dot.low{ background:#f1c40f; }
            .status-dot { width:12px; height:12px; border-radius:50%; flex-shrink:0; display: inline-block; margin-left: 5px; }
            .status-dot.completed { background: #28a745; }
            #prayer-times-output { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; padding: 20px; text-align: center; }
            #prayer-times-output div { background-color: #f9f9f9; border: 1px solid #eee; border-radius: 8px; padding: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
            #prayer-times-output strong { display: block; font-size: 1.2em; color: #8a2be2; margin-bottom: 8px; }
            #prayer-times-output span { font-size: 1.5em; font-weight: bold; color: #333; }
            .simple-calendar { display:grid; grid-template-columns: repeat(7, 1fr); gap:10px; padding:8px; }
            .cal-day { height:50px; border-radius:8px; display:flex; flex-direction:column; align-items:center; justify-content:center; font-size:1rem; line-height:1.2; background: #fff; border: 1px solid #ddd; }
            .cal-day.cal-today { font-weight: bold; border: 2px solid #8a2be2; }
            .cal-day .gregorian-date { font-size: 0.75em; color: #A52A2A; font-weight: normal; }
            .parsidate-notice { font-size: 1em !important; color: #444 !important; }
        </style>

        <div class="notex-wrapper" id="notex-widget-root">
            <div class="notex-col notex-col-main">
                <div class="notex-tabs">
                    <button class="notex-tab-link active" data-tab="new-note">نوشتن/ویرایش یادداشت</button>
                    <button class="notex-tab-link" data-tab="all-notes" data-filter="all">نمایش همه</button>
                    <button class="notex-tab-link" data-tab="high-priority-notes" data-filter="high">اولویت بالا</button>
                    <button class="notex-tab-link" data-tab="medium-priority-notes" data-filter="medium">اولویت متوسط</button>
                    <button class="notex-tab-link" data-tab="low-priority-notes" data-filter="low">اولویت کم</button>
                    <button class="notex-tab-link" data-tab="prayer-times">اوقات شرعی</button>
                    <button class="notex-tab-link" data-tab="calendar">تقویم</button>
                </div>
                <div id="new-note" class="notex-tab-content" style="display:block;">
                    <?php wp_nonce_field('notex_tools_wp_nonce_form', 'notex_form_nonce'); ?>
                    <input type="hidden" id="note-id-to-edit" value="">
                    <textarea id="note-content" placeholder="...متن خود را اینجا بنویسید"></textarea>
                    <div class="note-form-dropdowns">
                        <select id="note-priority">
                            <option value="high">اولویت بالا</option>
                            <option value="medium" selected>اولویت متوسط</option>
                            <option value="low">اولویت کم</option>
                        </select>
                         <select id="note-status">
                            <option value="in_progress">در حال انجام</option>
                            <option value="no_change" selected>بدون تغییر</option>
                            <option value="completed">تمام شده</option>
                        </select>
                    </div>
                    <button id="save-note-btn">ذخیره یادداشت</button>
                </div>
                <div id="all-notes" class="notex-tab-content notes-list" style="display:none;"></div>
                <div id="high-priority-notes" class="notex-tab-content notes-list" style="display:none;"></div>
                <div id="medium-priority-notes" class="notex-tab-content notes-list" style="display:none;"></div>
                <div id="low-priority-notes" class="notex-tab-content notes-list" style="display:none;"></div>
                <div id="prayer-times" class="notex-tab-content" style="display:none;"><div id="prayer-times-output"></div></div>
                <div id="calendar" class="notex-tab-content" style="display:none;"><div id="simple-calendar" class="simple-calendar"></div></div>
            </div>

            <div class="notex-col notex-col-sidebar">
                <div class="time-box">
                    <div class="clock" id="notex-clock" data-initial-time="<?php echo esc_attr($initial_clock); ?>">۰۰:۰۰</div>
                    <div class="date">
                        <div><?php echo esc_html($jalali_display); ?></div>
                        <div><?php echo esc_html($gregorian_display_formatted); ?></div>
                        <div><?php echo esc_html($gregorian_day_of_week); ?></div>
                    </div>
                </div>
                <div class="weather-box" id="notex-weather-info">برای نمایش آب و هوا، شهر را انتخاب کنید.</div>
                <div class="sidebar-bottom">
                    <div style="font-weight:700; margin-bottom:8px;">انتخاب شهر (برای آب و هوا)</div>
                    <select id="sidebar-city-selector" style="width:100%; padding:12px; border-radius:8px; border:1px solid #e9e6ee;">
                        <option value="">-- انتخاب کنید --</option>
                        <?php foreach ($this->cities_data as $key => $fa): ?>
                            <option value="<?php echo esc_attr($key); ?>"><?php echo esc_html($fa); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <div class="parsidate-notice" style="margin-top: 10px;">
                        افزونه برای نمایش فارسی به پلاگین <a href="https://wordpress.org/plugins/wp-parsidate/" target="_blank">پارسی دیت</a> نیاز دارد.
                    </div>
                </div>
            </div>
        </div>

        <script>
        (function($){
            $(function() {
                const widget = $('#notex_tools_wp_dashboard_widget');
                if (widget.length) {
                    $('#dashboard-widgets.metabox-holder').prepend(widget);
                    widget.css('margin-bottom', '20px');
                }
            });

            const nonce = '<?php echo wp_create_nonce("notex_tools_wp_nonce"); ?>';
            function doAjax(sub_action, data = {}, cb) {
                $.post(ajaxurl, $.extend({ action: 'notex_tools_wp_action', sub_action: sub_action, _ajax_nonce: nonce }, data), cb, 'json');
            }

            function initializeClock() {
                const clockElement = $('#notex-clock');
                const initialTime = clockElement.data('initial-time');
                let serverTime;
                if (initialTime && initialTime.split(':').length === 3) {
                    const parts = initialTime.split(':');
                    serverTime = new Date();
                    serverTime.setHours(parseInt(parts[0], 10), parseInt(parts[1], 10), parseInt(parts[2], 10));
                } else {
                    serverTime = new Date();
                }
                function updateClock() {
                    serverTime.setSeconds(serverTime.getSeconds() + 1);
                    const h = String(serverTime.getHours()).padStart(2, '0');
                    const m = String(serverTime.getMinutes()).padStart(2, '0');
                    clockElement.text(h + ':' + m);
                }
                updateClock();
                setInterval(updateClock, 1000);
            }
            initializeClock();
            
            $('.notex-tab-link').on('click', function(){
                const tab = $(this).data('tab');
                $('.notex-tab-link').removeClass('active');
                $(this).addClass('active');
                $('.notex-tab-content').hide();
                $('#' + tab).show();
                if ($(this).data('filter')) loadNotes($(this).data('filter'), 1, '#' + tab);
                if (tab === 'calendar') renderCalendar();
            });

            function escapeHtml(text) {
                if (typeof text !== 'string') return '';
                return text.replace(/[&<>"'`=\/]/g, s => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;','`':'&#x60;','=':'&#x3D;','/':'&#x2F;'})[s]);
            }
            
            function buildNoteItemHtml(note, number) {
                let statusDot = note.status === 'completed' ? `<span class="status-dot completed" title="تمام شده"></span>` : '';
                return `<div class="note-list-item">
                    <div class="note-left">
                        <span>${number}.</span>
                        ${statusDot}
                        <span class="priority-dot ${escapeHtml(note.priority)}"></span>
                        <span class="note-text-content" title="${escapeHtml(note.note_content)}">${escapeHtml(note.excerpt)}</span>
                    </div>
                    <div class="note-meta">
                        <a href="#" class="note-edit" data-id="${note.id}" data-content="${escapeHtml(note.note_content)}" data-priority="${note.priority}" data-status="${note.status}">ویرایش</a>
                        <a href="#" class="note-delete" data-id="${note.id}">حذف</a>
                        <span>${note.created_at_display || ''}</span>
                    </div>
                </div>`;
            }

            function loadNotes(filter, page = 1, containerSelector = '#all-notes') {
                $(containerSelector).html('در حال بارگذاری...');
                doAjax('get_notes', { filter, page }, function(res){
                    if (!res || !res.success) { $(containerSelector).html('خطا در دریافت یادداشت‌ها.'); return; }
                    let html = (res.data.notes || []).map((note, idx) => buildNoteItemHtml(note, res.data.offset + idx + 1)).join('');
                    $(containerSelector).html(html || 'یادداشتی یافت نشد.');
                });
            }

            $('body').on('click', '.note-delete', function(e){
                e.preventDefault();
                if (!confirm('آیا از حذف این یادداشت مطمئن هستید؟')) return;
                const id = $(this).data('id');
                doAjax('delete_note', { note_id: id }, function(res){
                    if (res && res.success) $('.notex-tab-link.active').trigger('click');
                    else alert(res.data.message || 'خطا در حذف.');
                });
            });

            $('body').on('click', '.note-edit', function(e){
                e.preventDefault();
                const btn = $(this);
                $('.notex-tab-link[data-tab="new-note"]').trigger('click');
                $('#note-id-to-edit').val(btn.data('id'));
                $('#note-content').val(btn.data('content'));
                $('#note-priority').val(btn.data('priority'));
                $('#note-status').val(btn.data('status'));
                $('#save-note-btn').text('بروزرسانی یادداشت');
            });

            $('#save-note-btn').on('click', function(){
                const noteData = {
                    note_id: $('#note-id-to-edit').val(),
                    content: $('#note-content').val(),
                    priority: $('#note-priority').val(),
                    status: $('#note-status').val(),
                    nonce: $('#notex_form_nonce').val()
                };
                doAjax('save_note', noteData, function(res){
                    if (res && res.success) {
                        $('#note-content').val('');
                        $('#note-id-to-edit').val('');
                        $('#save-note-btn').text('ذخیره یادداشت');
                        $('.notex-tab-link[data-filter="all"]').trigger('click');
                    } else {
                        alert(res.data.message || 'خطا در ذخیره سازی.');
                    }
                });
            });

            $('#sidebar-city-selector').on('change', function(){
                const city = $(this).val();
                if (!city) return;
                $('#notex-weather-info').html('در حال دریافت...');
                $('#prayer-times-output').html('در حال دریافت...');
                doAjax('get_city_data', { city }, function(res){
                    if (res && res.success) {
                        const w = res.data.weather;
                        const p = res.data.prayer;
                        let weather_html = `<strong>${escapeHtml(w.temp)}°C</strong> — دمای الان`;
                        let prayer_html = Object.entries(p).map(([key, val]) => `<div><strong>${escapeHtml(key)}</strong><span>${escapeHtml(val)}</span></div>`).join('');
                        $('#notex-weather-info').html(weather_html);
                        $('#prayer-times-output').html(prayer_html || 'اوقات شرعی دریافت نشد.');
                    } else {
                         $('#notex-weather-info').html('خطا در دریافت اطلاعات آب و هوا.');
                         $('#prayer-times-output').html('خطا در دریافت اوقات شرعی.');
                    }
                });
            });

            function renderCalendar() {
                $('#simple-calendar').html('در حال بارگذاری تقویم...');
                doAjax('get_calendar_info', {}, function(res){
                    if (!res || !res.success) {
                        $('#simple-calendar').html('خطا در دریافت تقویم.');
                        return;
                    }
                    let html = '';
                    const weekDays = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'];
                    weekDays.forEach(day => {
                        html += `<div class="cal-day" style="background:#f0eef3; font-weight:bold;">${day}</div>`;
                    });
                    for(let i=0; i < res.data.first_day_offset; i++) html += '<div></div>';
                    (res.data.days || []).forEach(day => {
                        html += `<div class="cal-day ${day.is_today ? 'cal-today' : ''}">
                                    <div>${day.jalali_day}</div>
                                    <div class="gregorian-date">${day.gregorian_str}</div>
                                 </div>`;
                    });
                    $('#simple-calendar').html(html);
                });
            }
            loadNotes('all', 1, '#all-notes');
        })(jQuery);
        </script>
        <?php
    }

    public function handle_ajax_actions() {
        check_ajax_referer('notex_tools_wp_nonce');
        if (!current_user_can('edit_dashboard')) {
            wp_send_json_error(['message' => 'عدم دسترسی.'], ۴۰۳);
        }
        $action = isset($_POST['sub_action']) ? sanitize_key($_POST['sub_action']) : '';
        switch ($action) {
            case 'save_note': $this->ajax_save_note(); break;
            case 'get_notes': $this->ajax_get_notes(); break;
            case 'delete_note': $this->ajax_delete_note(); break;
            case 'get_city_data': $this->ajax_get_city_data(); break;
            case 'get_calendar_info': $this->ajax_get_calendar_info(); break;
            default: wp_send_json_error(['message' => 'عملیات نامعتبر.']);
        }
    }

    private function ajax_save_note() {
        global $wpdb;
        $note_id = isset($_POST['note_id']) ? intval($_POST['note_id']) : 0;
        $content = isset($_POST['content']) ? wp_kses_post($_POST['content']) : '';
        $priority = isset($_POST['priority']) ? sanitize_key($_POST['priority']) : 'medium';
        $status = isset($_POST['status']) ? sanitize_key($_POST['status']) : 'no_change';
        if (empty(trim($content))) wp_send_json_error(['message' => 'متن یادداشت خالی است.']);
        if (!in_array($priority, ['high', 'medium', 'low'])) wp_send_json_error(['message' => 'اولویت نامعتبر است.']);
        if (!in_array($status, ['in_progress', 'no_change', 'completed'])) wp_send_json_error(['message' => 'وضعیت نامعتبر است.']);
        $data = ['note_content' => $content, 'priority' => $priority, 'status' => $status];
        $table_name = $wpdb->prefix . 'notex_tools_wp_notes';
        if ($note_id > 0) $result = $wpdb->update($table_name, $data, ['id' => $note_id]);
        else {
            $data['created_at'] = current_time('mysql', 1);
            $result = $wpdb->insert($table_name, $data);
        }
        if ($result !== false) wp_send_json_success();
        else wp_send_json_error(['message' => 'خطا در پایگاه داده.']);
    }
    
    private function ajax_get_notes() {
        global $wpdb;
        $filter = isset($_POST['filter']) && in_array($_POST['filter'], ['all', 'high', 'medium', 'low']) ? sanitize_key($_POST['filter']) : 'all';
        $where = ($filter !== 'all') ? $wpdb->prepare("WHERE priority = %s", $filter) : '';
        $table_name = $wpdb->prefix . 'notex_tools_wp_notes';
        $notes = $wpdb->get_results("SELECT * FROM {$table_name} {$where} ORDER BY created_at DESC LIMIT 20");
        $status_map = ['in_progress' => 'در حال انجام', 'no_change' => 'بدون تغییر', 'completed' => 'تمام شده'];
        $out = [];
        foreach ($notes as $n) {
            $timestamp = strtotime($n->created_at);
            $status_text = isset($status_map[$n->status]) ? $status_map[$n->status] : esc_html($n->status);
            $date_text = function_exists('parsidate') ? parsidate('Y/m/d', $timestamp) : date('Y-m-d', $timestamp);
            $out[] = [
                'id' => intval($n->id),
                'note_content' => wp_kses_post($n->note_content),
                'priority' => esc_html($n->priority),
                'status' => esc_html($n->status),
                'excerpt' => $this->excerpt_words($n->note_content, 5),
                'created_at_display' => $status_text . ' - ' . $date_text,
            ];
        }
        wp_send_json_success(['notes' => $out, 'offset' => 0]);
    }

    private function ajax_delete_note() {
        global $wpdb;
        $note_id = isset($_POST['note_id']) ? intval($_POST['note_id']) : 0;
        if ($note_id > 0) {
            $table_name = $wpdb->prefix . 'notex_tools_wp_notes';
            if ($wpdb->delete($table_name, ['id' => $note_id], ['%d'])) wp_send_json_success();
            else wp_send_json_error(['message' => 'یادداشت یافت نشد یا خطا در حذف.']);
        } else {
            wp_send_json_error(['message' => 'شناسه نامعتبر.']);
        }
    }

    private function ajax_get_city_data() {
        $city = isset($_POST['city']) ? sanitize_text_field($_POST['city']) : '';
        if (empty($city)) wp_send_json_error(['message' => 'نام شهر مشخص نشده است.']);
        $weather_res = wp_remote_get("https://wttr.in/{$city}?format=j1", ['timeout' => 10]);
        $weather_data = ['temp' => 'N/A'];
        if (!is_wp_error($weather_res) && wp_remote_retrieve_response_code($weather_res) === 200) {
            $wt = json_decode(wp_remote_retrieve_body($weather_res), true);
            if (!empty($wt['current_condition'][0])) $weather_data['temp'] = esc_html($wt['current_condition'][0]['temp_C']);
        }
        $prayer_res = wp_remote_get("https://api.aladhan.com/v1/timingsByCity?city={$city}&country=Iran&method=8", ['timeout' => 10]);
        $prayer_times = [];
        if (!is_wp_error($prayer_res) && wp_remote_retrieve_response_code($prayer_res) === 200) {
            $pr_api = json_decode(wp_remote_retrieve_body($prayer_res), true);
            if (!empty($pr_api['data']['timings'])) {
                $map = ['Fajr'=>'اذان صبح','Sunrise'=>'طلوع آفتاب','Dhuhr'=>'اذان ظهر','Maghrib'=>'اذان مغرب','Isha'=>'عشا'];
                foreach ($map as $en => $fa) if (isset($pr_api['data']['timings'][$en])) $prayer_times[esc_html($fa)] = esc_html($pr_api['data']['timings'][$en]);
            }
        }
        wp_send_json_success(['weather' => $weather_data, 'prayer' => $prayer_times]);
    }

    // تابع ساخت تقویم به صورت داخلی در صورت عدم دسترسی به API
    private function generate_calendar_fallback() {
        $now = current_time('timestamp');
        list($g_y, $g_m, $g_d) = [date('Y', $now), date('n', $now), date('j', $now)];
        list($jy, $jm, $jd) = $this->gregorian_to_jalali($g_y, $g_m, $g_d);
        $is_leap = in_array(($jy % 33), [1, 5, 9, 13, 17, 22, 26, 30]);
        $days_in_month = ($jm <= 6) ? 31 : (($jm <= 11) ? 30 : ($is_leap ? 30 : 29));
        $calendar_days = [];
        for ($day = 1; $day <= $days_in_month; $day++) {
            list($cal_g_y, $cal_g_m, $cal_g_d) = $this->jalali_to_gregorian($jy, $jm, $day);
            $calendar_days[] = [
                'jalali_day'    => intval($day),
                'gregorian_str' => sprintf('%02d/%02d', $cal_g_d, $cal_g_m),
                'is_today'      => ($day == $jd),
            ];
        }
        list($first_g_y, $first_g_m, $first_g_d) = $this->jalali_to_gregorian($jy, $jm, 1);
        $php_w = date('w', strtotime("$first_g_y-$first_g_m-$first_g_d"));
        $offset = ($php_w + 1) % 7; // شنبه=۰, یکشنبه=۱, ...
        return ['days' => $calendar_days, 'first_day_offset' => $offset];
    }
    
    // تابع دریافت اطلاعات تقویم با قابلیت جایگزینی
    private function ajax_get_calendar_info() {
        $time_data = $this->get_time_ir_data();
        if ($time_data) {
            $jy = $time_data['year'];
            $jm = $time_data['month'];
            $jd = $time_data['dayOfMonth'];
            $api_url = "https://time.ir/api/Fa/Month/GetCalender?Year={$jy}&Month={$jm}";
            $response = wp_remote_get($api_url, ['timeout' => 10]);
            if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
                $cal_data = json_decode(wp_remote_retrieve_body($response), true);
                if (isset($cal_data['days']) && isset($cal_data['dayOfWeek'])) {
                    $calendar_days = [];
                    foreach ($cal_data['days'] as $day_data) {
                        $calendar_days[] = [
                            'jalali_day'    => intval($day_data['day']),
                            'gregorian_str' => esc_html($day_data['gregorian']),
                            'is_today'      => (intval($day_data['day']) == $jd),
                        ];
                    }
                    $offset = intval($cal_data['dayOfWeek']);
                    wp_send_json_success(['days' => $calendar_days, 'first_day_offset' => $offset]);
                    return;
                }
            }
        }
        $fallback_data = $this->generate_calendar_fallback();
        wp_send_json_success($fallback_data);
    }
}

new Notex_Tools_Wp();

افزونه Notex نوتکس از چه جدولی استفاده می‌کند؟

این افزونه از یک جدول اختصاصی (Custom Table) در دیتابیس وردپرس برای ذخیره و بازخوانی یادداشت‌ها استفاده می‌کند.نام این جدول به صورت پویا و با استفاده از پیشوند جداول وردپرس شما ساخته می‌شود. برای مثال، اگر پیشوند دیتابیس شما wp_ باشد، نام کامل جدول به این صورت خواهد بود:

مقاله مرتبط:  دسترسی به سایت در زمان قطعی اینترنت

wp_notex_wp_notes

جدول اختصاصی در نوتکس Notex بهینه ؟

بله، این روش کاملاً بهینه و بهترین رویکرد (Best Practice) برای این نوع داده است. در واقع، این انتخاب نشان‌دهنده درک عمیق برنامه‌نویس از معماری وردپرس و بهینه‌سازی عملکرد است. ✅

  1. عملکرد فوق‌العاده (High Performance):

    • ایندکس‌گذاری دقیق: ستون‌های جدول (idprioritystatus) می‌توانند ایندکس‌گذاری شوند. این باعث می‌شود جستجو، فیلتر و مرتب‌سازی یادداشت‌ها (مثلاً نمایش یادداشت‌های با اولویت بالا) بسیار سریع و با کمترین فشار روی سرور انجام شود.

    • جلوگیری از نفخ (Bloat): داده‌های یادداشت‌ها، جداول اصلی وردپرس مانند wp_posts یا wp_options را سنکین و گند نمی‌کنند.

  2. مقیاس‌پذیری (Scalability):

    • شما می‌توانید هزاران یادداشت بدون نگرانی از افت سرعت سایت ذخیره کنید. ساختار اختصاصی جدول برای مدیریت حجم بالای داذه بهینه است.

  3. ساختار داده مشخص و تمیز (Clear Data Structure):

    • هر ستون دقیقاً برای یک نوع داده خاص طراحی شده است (note_content برای متن، priority برای اولویت و…). این کار خوانایی کد و مدیریت دیتابیس را بسیار ساده‌تر می‌کند.

  4. جداسازی داده‌ها (Data Isolation):

    • داده‌های افزونه کاملاً از محتوای اصلی سایت (نوشته‌ها، برگه‌ها) جدا هستند. این کار مدیریت، پشتیبان‌گیری و مهم‌تر از همه، حذف کامل افزونه را بسیار تمیز و بی‌دردسر می‌کند. وقتی افزونه را حذف می‌کنید، جدول اختصاصی آن نیز حذف شده و هیچ ردپایی در دیتابس شما باقی نمی‌ماند. 💯

مقاله مرتبط:  نمایش کد در مقالات سایت وردپرس

آیا از جدول Options استفاده می‌کند؟

خیر، این افزونه به درستی از جدول wp_options برای ذخیره یادداشت‌ها استفاده نمی‌کند.جدول wp_options برای ذخیره تنظیمات کلی سایت و افزونه‌ها طراحی شده است،مشابه این افزونه در مخزن وردپرس هست

آموزش های رایگان بیشتر : طراحی سایت شرکتی

۴/۵ - (۱ امتیاز)
Telegram
WhatsApp
Email
LinkedIn
پشتیبان سایت

یک پاسخ

دیدگاهتان را بنویسید

سه × 5 =