যদি আপনার ক্লায়েন্ট আপনার কাছে “একটি সংযোজন এলিমেন্টর, যা লেখকের সর্বশেষ আর্টিকেলগুলোর লিঙ্কসহ একটি অথর বক্স প্রদর্শন করে, আপনাকে দুটি বিকল্প দেয়: একটি শর্টকোড তৈরি করা, অথবা তৈরি একটি সত্যিকারের এলিমেন্টর উইজেট, যা যথাযথভাবে কনফিগারযোগ্য এবং রক্ষণাবেক্ষণযোগ্য। শর্টকোডটি কাজ করে… যতক্ষণ না আপনার কোনো স্টাইল কন্ট্রোল যোগ করার, ফলব্যাক পরিচালনা করার, বা সব জায়গায় CSS লোড করা এড়ানোর প্রয়োজন হয়।

সমস্যাটি / প্রয়োজন

আপনি এমন একটি কাস্টম, পুনঃব্যবহারযোগ্য এলিমেন্টর উইজেট চান যা যেকোনো নেটিভ উইজেটের মতো এডিটরের সাথে একীভূত হয়। এবং সবচেয়ে গুরুত্বপূর্ণ হলো: আপনি চান এটি যেন কনফিগারযোগ্য (কন্টেন্ট + স্টাইল), সুরক্ষিত (স্যানিটাইজেশন/এসকেপিং) হয় এবং সাইটের পারফরম্যান্সে কোনো প্রভাব না ফেলে।

এই নির্দেশিকাটি সেইসব মধ্যম স্তরের ব্যবহারকারীদের জন্য (যারা PHP এবং হুকস ব্যবহারে স্বচ্ছন্দ) যারা WordPress 6.9.4 (এপ্রিল ২০২৬) এবং PHP 8.1+ নিয়ে কাজ করেন। এই নির্দেশিকা শেষে আপনি জানতে পারবেন:

  • একটি মিনি তৈরি করুনপ্লাগ লাগানো যা এলিমেন্টরের উইজেট এপিআই (Widget API) ব্যবহার করে একটি উইজেট নিবন্ধন করে।
  • নিয়ন্ত্রণসমূহ যোগ করুন (টেক্সট, ইউআরএল, ব্যবহারকারী নির্বাচক, আর্টিকেলের সংখ্যা, অপশন টগল)।
  • একটি নিরাপদ (এস্কেপিং) এবং শক্তিশালী (ফলব্যাক) এইচটিএমএল রেন্ডারিং তৈরি করুন।
  • শুধুমাত্র উইজেটটি ব্যবহার করার সময় CSS/JS লোড করুন।

দ্রুত সারসংক্ষেপ

  • আমরা একটি “mu” বা ক্লাসিক প্লাগইন তৈরি করি যা সংযুক্ত হয় elementor/widgets/register.
  • আমরা একটি উইজেট ক্লাস সংজ্ঞায়িত করি যা এক্সটেন্ড করে ElementorWidget_Base.
  • আমরা এর মাধ্যমে নিয়ন্ত্রণ যোগ করি Controls_Manager (বিষয়বস্তু + শৈলী)।
  • আমরা উইজেটটি রেন্ডার করি render() (সামনে) এবং content_template() (সম্পাদকের প্রিভিউ, ঐচ্ছিক)।
  • আমরা অ্যাসেট (CSS/JS) সংরক্ষণ করি এবং এর মাধ্যমে সেগুলো লোড করি। get_style_depends()/get_script_depends().

কখন এই সমাধানটি ব্যবহার করবেন

  • আপনার একটি প্রজেক্ট-নির্দিষ্ট ব্লক (যেমন, “লেখকের তথ্য সংযোজন”, “হোম সিটিএ”, “বিশেষ পণ্য”) আছে, যা আপনার এডিটরদের অবশ্যই দৃশ্যমানভাবে কনফিগার করতে সক্ষম হতে হবে।
  • জটিল CSS ক্লাস না লিখেই আপনাকে এলিমেন্টরের স্টাইল কন্ট্রোলগুলো (টাইপোগ্রাফি, রং, স্পেসিং) প্রকাশ করতে হবে।
  • আপনি এমন ‘জাদুকরী’ শর্টকোড এড়িয়ে চলতে চাইবেন, যা থিম বা বিল্ডার পরিবর্তন করার সময় লেআউট নষ্ট করে দেয়।
  • আপনি যদি একাধিক সাইট পরিচালনা করেন, তবে প্লাগইন হিসেবে প্যাকেজ করা একটি উইজেটের ভার্সন তৈরি ও স্থাপন করা সহজ হয়।

কখন এই সমাধানটি ব্যবহার করা উচিত নয়

  • যদি আপনি শুধু অল্প পরিমাণ স্থির HTML যোগ করতে চান, তাহলে একটি “HTML” উইজেট অথবা একটি এলিমেন্টর টেমপ্লেট ব্যবহার করুন।
  • আপনার শতভাগ ডাইনামিক সার্ভার-সাইড রেন্ডারিং প্রয়োজন, কিন্তু কোনো জটিল UI ছাড়া: এক্ষেত্রে একটি শর্টকোডই যথেষ্ট হতে পারে (এবং এলিমেন্টরের “শর্টকোড” উইজেটের মাধ্যমে এটি ব্যবহার করা যাবে)।
  • আপনি যদি একটি পুনঃব্যবহারযোগ্য ক্রস-বিল্ডার কম্পোনেন্ট খুঁজে থাকেন, তাহলে একটিকে অগ্রাধিকার দিন গুটেনবার্গ ব্লক (ব্লক এডিটর) এবং/অথবা একটি প্যাটার্ন। এলিমেন্টর একটি নির্দিষ্ট ইকোসিস্টেম।
  • কোডের উপর আপনার কোনো নিয়ন্ত্রণ নেই (ক্লায়েন্ট সাইট লক করা): একটি “স্নিপেটস” প্লাগইন সাহায্য করতে পারে, কিন্তু ক্লাস/অ্যাসেট লোডের ক্ষেত্রে এটি খুব কমই ত্রুটিমুক্ত হয়।

শুরু করার আগে পূর্বশর্তসমূহ /

কোডটি স্পর্শ করার আগে:

  • স্টেজিং/লোকাল এনভায়রনমেন্টে (লোকালডব্লিউপি, ডেভকিনস্টা, ডকার…) কাজ করুন।
  • ডাটাবেস এবং ফাইলগুলির ব্যাক আপ নিন (অন্তত wp-content).
  • ভার্সনগুলো যাচাই করুন: WordPress 6.9.4, PHP 8.1+, Elementor হালনাগাদ থাকতে হবে।
  • সক্ষম করা WP_DEBUG et WP_DEBUG_LOG ত্রুটিগুলো দেখার জন্য স্টেজিং-এ রাখা হয়েছে।

প্রয়োজনীয় অনুস্মারক:

নিরাপত্তা সতর্কতা: একটি এলিমেন্টর উইজেট ডাটাবেস থেকে ডেটা (ব্যবহারকারী, পোস্ট) প্রদর্শন করতে পারে। আপনি যদি আউটপুটটি সঠিকভাবে এস্কেপ না করেন, তবে আপনি স্টোর্ড XSS দুর্বলতার পথ খুলে দেন। আমি ইতোমধ্যেই একাধিক লেখকের সাইটে এই পরিস্থিতি দেখেছি, যেখানে একটি ত্রুটিপূর্ণভাবে ফিল্টার করা 'display_name' শেষ পর্যন্ত একটি HTML অ্যাট্রিবিউটে প্রবেশ করে গেছে।

সরল পন্থা (এবং কেন তা পরিহার করা উচিত)

আমি প্রায়শই যা দেখি: একটি শর্টকোড যা এর মাধ্যমে অপশনগুলো পুনরুদ্ধার করে। $_GET অথবা আনফিল্টার্ড অ্যাট্রিবিউট, তারপর র HTML প্রিন্ট করে। সাধারণ উদাহরণ (ব্যবহার করবেন না):

<?php
// ❌ Exemple volontairement mauvais : pas de sanitization, pas d'escaping, requête non bornée.
add_shortcode('author_box', function($atts) {
    $atts = shortcode_atts([
        'user' => 1,
        'count' => 5,
        'title' => 'Auteur'
    ], $atts);

    $user = get_user_by('id', $atts['user']);
    echo '<div class="author-box">';
    echo '<h3>' . $atts['title'] . '</h3>';
    echo '<p>' . $user->display_name . '</p>';
    echo '</div>';
});

বাস্তব সমস্যা:

  • নিরাপত্তা : $atts['title'] et $user->display_name তারা না নিয়েই চলে যায় esc_html().
  • জন্য perf ক্যাশে নেই, কঠোর সীমা নেই, এবং এর ফলে একটি এলিমেন্টর পেজে বারবার অনুরোধ লোড হওয়ার ঝুঁকি থাকে।
  • UX এলিমেন্টরে, এডিটর নেটিভ কন্ট্রোলগুলো দেখতে পায় না (কাস্টম CSS ছাড়া কোনো টাইপোগ্রাফি/কালার দেখা যায় না)।
  • রক্ষণাবেক্ষণ এর ফলে আপনি ১২টি শর্টকোড পাবেন, যার প্রতিটির নিজস্ব লজিক এবং সামগ্রিক CSS থাকবে।

সঠিক পদ্ধতি — ধাপে ধাপে নির্দেশিকা

ধাপ ১ — একটি ন্যূনতম প্লাগইন তৈরি করুন

একটি ফোল্ডার তৈরি করুন: wp-content/plugins/bpcab-elementor-widgets

মূল ফাইলটি তৈরি করুন: wp-content/plugins/bpcab-elementor-widgets/bpcab-elementor-widgets.php

ধাপ ২ — যাচাই করুন যে এলিমেন্টর লোড হয়েছে (এবং সঠিক সময়ে)

একটি চিরাচরিত ভুল: উইজেটটি খুব তাড়াতাড়ি সেভ করা (যেমন, অন initএবং এই ধরনের একটি ত্রুটি পান 'ElementorWidget_Base' ক্লাসটি খুঁজে পাওয়া যায়নিআমরা এলিমেন্টরের নির্দিষ্ট হুকগুলোর সাথে নিজেদের সংযুক্ত করি।

ধাপ ৩ — একটি “লেখক প্যানেল + সর্বশেষ পোস্ট” উইজেট ঘোষণা করুন

আমরা এমন একটি উইজেট কোড করতে যাচ্ছি যা:

  • একটি কন্ট্রোলের মাধ্যমে একজন ব্যবহারকারীকে (লেখক) নির্বাচন করা হয়।
  • অবতার + নাম + জীবনবৃত্তান্ত (ঐচ্ছিক) প্রদর্শন করে।
  • তার সাম্প্রতিকতম প্রবন্ধগুলোর তালিকা (সংখ্যা পরিবর্তনযোগ্য)।
  • স্টাইলের বিকল্পসমূহ (রং, টাইপোগ্রাফি, স্পেসিং) প্রদর্শন করে।

ধাপ ৪ — শুধুমাত্র প্রয়োজন হলে CSS/JS লোড করুন

এলিমেন্টর আপনাকে এর মাধ্যমে নির্ভরতা ঘোষণা করার সুযোগ দেয় get_style_depends() et get_script_depends()আমার অভিজ্ঞতায়, এটি একটি বড় সুবিধা: এর ফলে সব পেজে একটি গ্লোবাল CSS ফাইল লোড হওয়া এড়ানো যায়।

ধাপ ৫ — এলিমেন্টর স্টাইল কন্ট্রোল যোগ করুন

উইজেটের জন্য নির্দিষ্ট CSS তৈরি করতে এলিমেন্টরের 'সিলেক্টর' ব্যবহার করা হবে। এর ফলে ডজন ডজন ক্লাস কোড করার প্রয়োজন হয় না এবং Avada/Divi-এর সাথে দ্বন্দ্ব কমে যায়।

ধাপ ৬ — নিরাপদ এবং শক্তিশালী রেন্ডারিং

মূল বিষয়:

  • স্যানিটাইজেশন সেটিংস: absint, sanitize_text_field, esc_url_raw প্রয়োজন হলে
  • প্রস্থান পথ দিয়ে পালানো: esc_html, esc_attr, esc_url.
  • বিকল্প ব্যবস্থা: লেখক খুঁজে পাওয়া যায়নি, কোনো নিবন্ধ নেই, জীবনবৃত্তান্ত খালি।

সম্পূর্ণ কোড

যেমন আছে তেমনই কপি ও পেস্ট করুন। এই প্লাগইনটি ১টি এলিমেন্টর উইজেট সংরক্ষণ করে। এটি ইচ্ছাকৃতভাবে সংক্ষিপ্ত, কিন্তু সম্পূর্ণ এবং পরীক্ষার জন্য প্রস্তুত।

২) প্রধান প্লাগইন ফাইল

<?php
/**
 * Plugin Name: BPCAB - Widgets Elementor (Exemple)
 * Description: Exemple pédagogique : widget Elementor personnalisé (encart auteur + derniers articles).
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCAB
 *
 * Sécurité : ce plugin est un exemple. Testez en staging avant production.
 */

declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

final class BPCAB_Elementor_Widgets_Plugin {

	private const MIN_PHP = '8.1';

	public static function init(): void {
		add_action('plugins_loaded', [__CLASS__, 'bootstrap']);
	}

	public static function bootstrap(): void {
		// Vérif PHP (utile si le site est downgradé par erreur).
		if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
			add_action('admin_notices', [__CLASS__, 'notice_php_version']);
			return;
		}

		// Ne rien faire si Elementor n'est pas actif.
		if (!did_action('elementor/loaded')) {
			add_action('admin_notices', [__CLASS__, 'notice_elementor_missing']);
			return;
		}

		// Enregistrer le widget au bon hook.
		add_action('elementor/widgets/register', [__CLASS__, 'register_widgets']);

		// Enregistrer les assets (CSS/JS) utilisables par les widgets.
		add_action('wp_enqueue_scripts', [__CLASS__, 'register_front_assets']);
	}

	public static function register_front_assets(): void {
		$ver = '1.0.0';

		wp_register_style(
			'bpcab-author-box',
			plugins_url('assets/author-box.css', __FILE__),
			[],
			$ver
		);

		// JS optionnel : ici on ne fait rien de critique, mais c'est prêt si vous en avez besoin.
		wp_register_script(
			'bpcab-author-box',
			plugins_url('assets/author-box.js', __FILE__),
			[],
			$ver,
			true
		);
	}

	public static function register_widgets($widgets_manager): void {
		// Charger la classe du widget.
		require_once __DIR__ . '/widgets/class-bpcab-author-box-widget.php';

		// Elementor 3.x+ : register() existe sur le manager.
		$widgets_manager->register(new BPCAB_Author_Box_Widget());
	}

	public static function notice_elementor_missing(): void {
		if (!current_user_can('activate_plugins')) {
			return;
		}

		$plugin_page = admin_url('plugins.php');

		echo '<div class="notice notice-warning"><p>';
		echo esc_html__('BPCAB - Widgets Elementor : Elementor n’est pas actif. Activez Elementor pour charger le widget.', 'bpcab');
		echo ' ';
		echo '<a href="' . esc_url($plugin_page) . '">' . esc_html__('Aller aux extensions', 'bpcab') . '</a>';
		echo '</p></div>';
	}

	public static function notice_php_version(): void {
		if (!current_user_can('manage_options')) {
			return;
		}

		echo '<div class="notice notice-error"><p>';
		echo esc_html__('BPCAB - Widgets Elementor : PHP 8.1+ est requis.', 'bpcab');
		echo '</p></div>';
	}
}

BPCAB_Elementor_Widgets_Plugin::init();

২) উইজেট ক্লাস

তৈরি করুন: wp-content/plugins/bpcab-elementor-widgets/widgets/class-bpcab-author-box-widget.php

<?php
declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

use ElementorWidget_Base;
use ElementorControls_Manager;
use ElementorGroup_Control_Typography;
use ElementorGroup_Control_Border;
use ElementorGroup_Control_Box_Shadow;

final class BPCAB_Author_Box_Widget extends Widget_Base {

	public function get_name(): string {
		return 'bpcab_author_box';
	}

	public function get_title(): string {
		return esc_html__('Encart Auteur (BPCAB)', 'bpcab');
	}

	public function get_icon(): string {
		// Icône Elementor (dashicons-like). Vous pouvez la changer.
		return 'eicon-user-circle-o';
	}

	public function get_categories(): array {
		// Catégorie standard. Vous pouvez créer votre propre catégorie si besoin.
		return ['general'];
	}

	public function get_keywords(): array {
		return ['author', 'auteur', 'bio', 'posts', 'bpcab'];
	}

	public function get_style_depends(): array {
		return ['bpcab-author-box'];
	}

	public function get_script_depends(): array {
		return ['bpcab-author-box'];
	}

	protected function register_controls(): void {

		// SECTION : Contenu
		$this->start_controls_section(
			'section_content',
			[
				'label' => esc_html__('Contenu', 'bpcab'),
				'tab' => Controls_Manager::TAB_CONTENT,
			]
		);

		$this->add_control(
			'title',
			[
				'label' => esc_html__('Titre', 'bpcab'),
				'type' => Controls_Manager::TEXT,
				'default' => esc_html__('À propos de l’auteur', 'bpcab'),
				'placeholder' => esc_html__('Ex : À propos de Marie', 'bpcab'),
				'label_block' => true,
			]
		);

		// Contrôle simple : ID utilisateur (numérique).
		// Variante plus avancée : select2 alimenté en AJAX (hors scope), ou select statique.
		$this->add_control(
			'user_id',
			[
				'label' => esc_html__('ID utilisateur (auteur)', 'bpcab'),
				'type' => Controls_Manager::NUMBER,
				'min' => 1,
				'step' => 1,
				'default' => (int) get_current_user_id(),
				'description' => esc_html__('Astuce : récupérez l’ID dans Utilisateurs > Tous les utilisateurs.', 'bpcab'),
			]
		);

		$this->add_control(
			'show_bio',
			[
				'label' => esc_html__('Afficher la bio', 'bpcab'),
				'type' => Controls_Manager::SWITCHER,
				'label_on' => esc_html__('Oui', 'bpcab'),
				'label_off' => esc_html__('Non', 'bpcab'),
				'return_value' => 'yes',
				'default' => 'yes',
			]
		);

		$this->add_control(
			'show_posts',
			[
				'label' => esc_html__('Afficher les derniers articles', 'bpcab'),
				'type' => Controls_Manager::SWITCHER,
				'label_on' => esc_html__('Oui', 'bpcab'),
				'label_off' => esc_html__('Non', 'bpcab'),
				'return_value' => 'yes',
				'default' => 'yes',
			]
		);

		$this->add_control(
			'posts_count',
			[
				'label' => esc_html__('Nombre d’articles', 'bpcab'),
				'type' => Controls_Manager::NUMBER,
				'min' => 1,
				'max' => 12,
				'step' => 1,
				'default' => 3,
				'condition' => [
					'show_posts' => 'yes',
				],
			]
		);

		$this->add_control(
			'profile_url',
			[
				'label' => esc_html__('URL du profil (optionnel)', 'bpcab'),
				'type' => Controls_Manager::URL,
				'placeholder' => 'https://',
				'show_external' => true,
				'description' => esc_html__('Si vide, le nom n’est pas cliquable.', 'bpcab'),
			]
		);

		$this->end_controls_section();

		// SECTION : Style (encart)
		$this->start_controls_section(
			'section_style_box',
			[
				'label' => esc_html__('Style : Encart', 'bpcab'),
				'tab' => Controls_Manager::TAB_STYLE,
			]
		);

		$this->add_control(
			'box_bg',
			[
				'label' => esc_html__('Couleur de fond', 'bpcab'),
				'type' => Controls_Manager::COLOR,
				'default' => '',
				'selectors' => [
					'{{WRAPPER}} .bpcab-author-box' => 'background-color: {{VALUE}};',
				],
			]
		);

		$this->add_responsive_control(
			'box_padding',
			[
				'label' => esc_html__('Padding', 'bpcab'),
				'type' => Controls_Manager::DIMENSIONS,
				'size_units' => ['px', 'em', 'rem', '%'],
				'selectors' => [
					'{{WRAPPER}} .bpcab-author-box' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				],
			]
		);

		$this->add_group_control(
			Group_Control_Border::get_type(),
			[
				'name' => 'box_border',
				'selector' => '{{WRAPPER}} .bpcab-author-box',
			]
		);

		$this->add_group_control(
			Group_Control_Box_Shadow::get_type(),
			[
				'name' => 'box_shadow',
				'selector' => '{{WRAPPER}} .bpcab-author-box',
			]
		);

		$this->end_controls_section();

		// SECTION : Style (typos)
		$this->start_controls_section(
			'section_style_text',
			[
				'label' => esc_html__('Style : Texte', 'bpcab'),
				'tab' => Controls_Manager::TAB_STYLE,
			]
		);

		$this->add_control(
			'title_color',
			[
				'label' => esc_html__('Couleur du titre', 'bpcab'),
				'type' => Controls_Manager::COLOR,
				'selectors' => [
					'{{WRAPPER}} .bpcab-author-box__title' => 'color: {{VALUE}};',
				],
			]
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			[
				'name' => 'title_typography',
				'selector' => '{{WRAPPER}} .bpcab-author-box__title',
			]
		);

		$this->add_control(
			'name_color',
			[
				'label' => esc_html__('Couleur du nom', 'bpcab'),
				'type' => Controls_Manager::COLOR,
				'selectors' => [
					'{{WRAPPER}} .bpcab-author-box__name' => 'color: {{VALUE}};',
				],
			]
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			[
				'name' => 'name_typography',
				'selector' => '{{WRAPPER}} .bpcab-author-box__name',
			]
		);

		$this->add_control(
			'bio_color',
			[
				'label' => esc_html__('Couleur de la bio', 'bpcab'),
				'type' => Controls_Manager::COLOR,
				'selectors' => [
					'{{WRAPPER}} .bpcab-author-box__bio' => 'color: {{VALUE}};',
				],
				'condition' => [
					'show_bio' => 'yes',
				],
			]
		);

		$this->end_controls_section();
	}

	protected function render(): void {
		$settings = $this->get_settings_for_display();

		$title = isset($settings['title']) ? sanitize_text_field((string) $settings['title']) : '';
		$user_id = isset($settings['user_id']) ? absint($settings['user_id']) : 0;

		$show_bio = (!empty($settings['show_bio']) && $settings['show_bio'] === 'yes');
		$show_posts = (!empty($settings['show_posts']) && $settings['show_posts'] === 'yes');

		$posts_count = isset($settings['posts_count']) ? absint($settings['posts_count']) : 3;
		$posts_count = max(1, min(12, $posts_count));

		$user = $user_id ? get_user_by('id', $user_id) : false;

		echo '<div class="bpcab-author-box">';

		if ($title !== '') {
			echo '<div class="bpcab-author-box__title">' . esc_html($title) . '</div>';
		}

		if (!$user instanceof WP_User) {
			// Fallback propre : évite une box vide.
			echo '<p>' . esc_html__('Auteur introuvable (vérifiez l’ID utilisateur).', 'bpcab') . '</p>';
			echo '</div>';
			return;
		}

		$display_name = (string) $user->display_name;
		$description = (string) get_user_meta((int) $user->ID, 'description', true);

		$avatar = get_avatar((int) $user->ID, 96, '', $display_name, [
			'class' => 'bpcab-author-box__avatar',
		]);

		// URL de profil optionnelle (Elementor URL control).
		$profile_url = '';
		$profile_is_external = false;
		$profile_nofollow = false;

		if (!empty($settings['profile_url']) && is_array($settings['profile_url'])) {
			$profile_url = !empty($settings['profile_url']['url']) ? esc_url($settings['profile_url']['url']) : '';
			$profile_is_external = !empty($settings['profile_url']['is_external']);
			$profile_nofollow = !empty($settings['profile_url']['nofollow']);
		}

		echo '<div class="bpcab-author-box__header">';
		echo '<div class="bpcab-author-box__avatar-wrap">' . $avatar . '</div>';
		echo '<div class="bpcab-author-box__meta">';

		$name_html = '<span class="bpcab-author-box__name">' . esc_html($display_name) . '</span>';

		if ($profile_url) {
			$rel = [];
			if ($profile_is_external) {
				// target blank sans noopener = vulnérable.
				// Elementor gère souvent ça côté UI, mais on le force côté rendu.
				$rel[] = 'noopener';
			}
			if ($profile_nofollow) {
				$rel[] = 'nofollow';
			}

			$target = $profile_is_external ? ' target="_blank"' : '';
			$rel_attr = !empty($rel) ? ' rel="' . esc_attr(implode(' ', array_unique($rel))) . '"' : '';

			$name_html = '<a class="bpcab-author-box__name bpcab-author-box__name--link" href="' . esc_url($profile_url) . '"' . $target . $rel_attr . '>' . esc_html($display_name) . '</a>';
		}

		echo $name_html;

		if ($show_bio && $description !== '') {
			// Bio : autoriser un sous-ensemble HTML ? Ici on reste strict : texte simple.
			echo '<div class="bpcab-author-box__bio">' . esc_html($description) . '</div>';
		}

		echo '</div>'; // meta
		echo '</div>'; // header

		if ($show_posts) {
			$posts = get_posts([
				'post_type' => 'post',
				'post_status' => 'publish',
				'author' => (int) $user->ID,
				'numberposts' => $posts_count,
				'no_found_rows' => true,
				'ignore_sticky_posts' => true,
				'suppress_filters' => false,
			]);

			echo '<div class="bpcab-author-box__posts">';
			if (!empty($posts)) {
				echo '<ul class="bpcab-author-box__posts-list">';
				foreach ($posts as $post) {
					$permalink = get_permalink($post);
					$post_title = get_the_title($post);

					echo '<li class="bpcab-author-box__posts-item">';
					echo '<a class="bpcab-author-box__posts-link" href="' . esc_url($permalink) . '">' . esc_html($post_title) . '</a>';
					echo '</li>';
				}
				echo '</ul>';
			} else {
				echo '<p class="bpcab-author-box__empty">' . esc_html__('Aucun article récent.', 'bpcab') . '</p>';
			}
			echo '</div>';
		}

		echo '</div>'; // box
	}

	// Optionnel : aperçu dans l’éditeur (JS template). On le laisse simple.
	// Si vous ne le faites pas, Elementor affichera un rendu serveur en preview (souvent suffisant).
	protected function content_template(): void {}
}

৩) উইজেট সিএসএস

তৈরি করুন: wp-content/plugins/bpcab-elementor-widgets/assets/author-box.css

.bpcab-author-box{
	display:block;
	border-radius:12px;
	background:#fff;
}

.bpcab-author-box__title{
	font-weight:700;
	margin:0 0 12px 0;
}

.bpcab-author-box__header{
	display:flex;
	gap:12px;
	align-items:flex-start;
}

.bpcab-author-box__avatar{
	border-radius:999px;
	display:block;
}

.bpcab-author-box__meta{
	display:block;
	min-width:0;
}

.bpcab-author-box__name{
	display:inline-block;
	font-weight:700;
	text-decoration:none;
}

.bpcab-author-box__bio{
	margin-top:6px;
	opacity:.9;
}

.bpcab-author-box__posts{
	margin-top:14px;
}

.bpcab-author-box__posts-list{
	margin:0;
	padding-left:18px;
}

.bpcab-author-box__posts-item{
	margin:6px 0;
}

৪) জেএস (ঐচ্ছিক)

তৈরি করুন: wp-content/plugins/bpcab-elementor-widgets/assets/author-box.js

/* Fichier volontairement vide.
   Gardez-le si vous prévoyez d'ajouter des interactions.
   Sinon, supprimez get_script_depends() et l'enregistrement du script. */

কোডের ব্যাখ্যা

পর্দার আড়ালে যা ঘটে (সরল সংস্করণ)

প্লাগইনটি এলিমেন্টর লোড হওয়ার জন্য অপেক্ষা করে। তারপর, এটি একটি উইজেট সংরক্ষণ করে। এলিমেন্টর এই উইজেটটিকে এডিটরে তালিকাভুক্ত করে, এবং যখন আপনি এটিকে কোনো পৃষ্ঠায় ড্রপ করেন, এলিমেন্টর:

  • আপনার নিয়ন্ত্রণগুলি (বিষয়বস্তু + শৈলী) প্রদর্শন করে,
  • পেজের JSON (পোস্ট মেটা)-তে সেটিংস সংরক্ষণ করে,
  • কল render() ফ্রন্ট-এন্ড এইচটিএমএল তৈরি করতে।

এই হুকগুলো কেন, অন্যগুলো নয় কেন?

  • plugins_loaded পরিবেশ এবং এলিমেন্টরের উপস্থিতি যাচাই করার জন্য এটি একটি ভালো সময়।
  • did_action('elementor/loaded') Elementor ক্লাসগুলো অটোলোড হওয়ার আগে কল করা এড়িয়ে চলে।
  • elementor/widgets/register উইজেট নিবন্ধন করার জন্য ব্যবহৃত হুক। এটিই "ক্লাস খুঁজে পাওয়া যায়নি" ত্রুটি প্রতিরোধ করে।
  • wp_register_style আমরা সম্পদগুলো প্রস্তুত করি, কিন্তু সেগুলোকে বিশ্বব্যাপী একত্রিত করি না।

পরিচ্ছন্নতা বনাম পলায়ন (যে ভুলগুলো আমি প্রায়শই দেখি)

দুটি নিয়ম:

  • স্যানিটাইজেশন যখন আপনি কোনো মানকে স্বাভাবিক করেন (যেমন: absint একটি পরিচয়পত্রের জন্য, sanitize_text_field (একটি শিরোনামের জন্য)।
  • পলায়নপর যখন আপনি HTML-এ প্রিন্ট করেন (যেমন: esc_html, esc_url, esc_attr).

উইজেটে, রেন্ডার করার সময় সেটিংসগুলো স্যানিটাইজ করা হয়, এবং তারপর প্রদর্শনের সময় পদ্ধতিগতভাবে এস্কেপ করা হয়। হ্যাঁ, এলিমেন্টর বেশিরভাগ সময়ই 'পরিষ্কার' ভ্যালু সংরক্ষণ করে, কিন্তু এর উপর পুরোপুরি নির্ভর করবেন না: একটি JSON ইম্পোর্ট, কপি-পেস্ট, বা কোনো থার্ড-পার্টি প্লাগইন অপ্রত্যাশিত স্ট্রিং ঢুকিয়ে দিতে পারে।

সম্পদের শর্তসাপেক্ষ লোডিং

get_style_depends() একটি সংরক্ষিত স্টাইল হ্যান্ডেল ফেরত দেয়। উইজেটটি পেজে উপস্থিত থাকলেই এলিমেন্টর এই স্টাইলটি লোড করবে। এটি একটি সহজ প্যাটার্ন যা সাইট-ব্যাপী CSS ব্যবহার এড়িয়ে চলে।

সর্বশেষ নিবন্ধের জন্য অনুরোধ

আমরা ব্যবহার করি get_posts() সঙ্গে

  • no_found_rows পৃষ্ঠা সংখ্যা না থাকায় গণনা করার প্রয়োজন নেই।
  • ignore_sticky_posts অপ্রত্যাশিত ফলাফল এড়ানো যায়।
  • numberposts ১২-তে সীমাবদ্ধ: একটি “লাইট” উইজেট রাখে।

সম্ভাব্য বিকল্প: WP_Query যদি আপনার আরও নিয়ন্ত্রণের প্রয়োজন হয় কিন্তু এখানে get_posts() পর্যাপ্ত এবং পাঠযোগ্য থাকে।

বিভিন্ন রূপ এবং ব্যবহারের ক্ষেত্র

সংস্করণ ১ — আরও ব্যবহার-বান্ধব লেখক নির্বাচক (ড্রপ-ডাউন তালিকা)

উদাহরণ হিসেবে 'ইউজার আইডি' কন্ট্রোলটি সুবিধাজনক, কিন্তু আপনার পাবলিশাররা আপনাকে তিরস্কার করবে। এর একটি সহজ বিকল্প হলো: ব্যবহারকারীদের থেকে অপশনের একটি তালিকা তৈরি করা (হাজার হাজার ব্যবহারকারী আছে এমন সাইটের ক্ষেত্রে সতর্ক থাকবেন)।

মধ্যে register_controls()নিয়ন্ত্রণ প্রতিস্থাপন করুন user_id দ্বারা:

<?php
// ✅ Variante : select (attention : coûteux si beaucoup d'utilisateurs).
$users = get_users([
	'fields' => ['ID', 'display_name'],
	'number' => 200, // borne volontaire
	'orderby' => 'display_name',
	'order' => 'ASC',
]);

$options = [];
foreach ($users as $u) {
	$options[(string) $u->ID] = $u->display_name . ' (#' . $u->ID . ')';
}

$this->add_control(
	'user_id',
	[
		'label' => esc_html__('Auteur', 'bpcab'),
		'type' => Controls_Manager::SELECT,
		'options' => $options,
		'default' => (string) get_current_user_id(),
	]
);

বিশেষ পরিস্থিতি: ৫০,০০০ অ্যাকাউন্ট আছে এমন একটি মেম্বারশিপ সাইটে এই SELECT ব্যবহার করা যায় না। এই ক্ষেত্রে, একটি AJAX কন্ট্রোল (Select2) ব্যবহার করুন অথবা UI হেল্প সহ একটি "ID" ফিল্ড (কিংবা একটি REST সার্চ) বাধ্যতামূলক করুন।

বিকল্প ২ — আর্টিকেলের পরিবর্তে একটি CPT (যেমন, “portfolio”) প্রদর্শন করুন

শুধু পরিবর্তন করুন 'post_type' => 'post' en 'post_type' => 'portfolio' (অথবা একটি টেবিল)। পোস্ট টাইপটিকে এর মাধ্যমে কনফিগারযোগ্য করার কথা বিবেচনা করুন। SELECT যদি আপনার একাধিক থাকে

সংস্করণ ৩ — লেখকের দ্বারা লাইটওয়েট ক্যাশিং (ট্রানজিয়েন্ট)

যদি আপনার উইজেটটি একটি পেজে ১০ বার ব্যবহৃত হয় (এমনটা 'টিম' ল্যান্ডিং পেজগুলোতে হয়ে থাকে), তাহলে আপনি পোস্টের তালিকাটি লুকিয়ে ফেলতে পারেন। সরলীকৃত উদাহরণ:

<?php
$cache_key = 'bpcab_ab_posts_' . (int) $user->ID . '_' . (int) $posts_count;
$posts = get_transient($cache_key);

if ($posts === false) {
	$posts = get_posts([
		'post_type' => 'post',
		'post_status' => 'publish',
		'author' => (int) $user->ID,
		'numberposts' => $posts_count,
		'no_found_rows' => true,
		'ignore_sticky_posts' => true,
	]);

	// Cache 10 minutes.
	set_transient($cache_key, $posts, 10 * MINUTE_IN_SECONDS);
}

দ্রষ্টব্য: আপনি যদি ঘন ঘন প্রকাশ করেন, তাহলে ক্যাশে প্রদর্শনে বিলম্ব ঘটাতে পারে। একটি উচ্চ-ট্র্যাফিক সাইটের জন্য, আপনি একটি স্থায়ী অবজেক্ট ক্যাশে (Redis) এবং ইনভ্যালিডেশন (হুকস) পছন্দ করবেন। save_postএকটি নির্দিষ্ট TTL-এর পরিবর্তে।

Divi 5 / Elementor / Avada-এর সাথে সামঞ্জস্যপূর্ণ

এলিমেন্টর (এডিটর এবং ফ্রন্ট এন্ড)

  • উইজেটটি “সাধারণ” ক্যাটাগরিতে প্রদর্শিত হয়।
  • শৈলীগুলি এর মাধ্যমে পরিমাপ করা হয় {{WRAPPER}}যা CSS সংঘর্ষ সীমিত করে।
  • আপনি যদি এলিমেন্টর ক্যাশে/সিএসএস জেনারেশন ব্যবহার করে থাকেন, তাহলে পরিবর্তন করার পর তা ক্লিয়ার করে দিন।

দিভি ৫

ডিভি এলিমেন্টর উইজেট এপিআই ব্যবহার করে না। আপনার উইজেটটি ডিভি বিল্ডারে পাওয়া যাবে না।

যদি আপনাকে ডিভি ৫-কেও সমর্থন করতে হয়, তাহলে একটি বাস্তবসম্মত পন্থা হলো:

  • একটি “compat” শর্টকোড তৈরি করুন যা একই PHP লজিক (শেয়ার্ড ফাংশন) পুনঃব্যবহার করে, তারপর এটিকে একটি Divi কোড/শর্টকোড মডিউলের মাধ্যমে সন্নিবেশ করুন।
  • অথবা একটি ডেডিকেটেড ডিভি ৫ মডিউল তৈরি করুন (যা আরও পরিচ্ছন্ন ও দীর্ঘ)।

আভাদা (ফিউশন বিল্ডার)

একই যুক্তি: Avada, Elementor উইজেট ব্যবহার করে না। Avada-র ক্ষেত্রে:

  • শর্টকোড সামঞ্জস্যপূর্ণ (ফিউশন বিল্ডার এগুলো একীভূত করতে জানে)।
  • অথবা আপনার যদি নেটিভ Avada UI-এর প্রয়োজন হয়, তাহলে একটি কাস্টম Fusion এলিমেন্ট ব্যবহার করতে পারেন।

একটি “মাল্টি-বিল্ডার” টিপস যা আমি প্রায়শই প্রয়োগ করি

বিজনেস লজিক একটি আলাদা PHP ক্লাসে রাখুন (যেমন, BPCAB_Author_Box_Rendererএবং অ্যাডাপ্টার তৈরি করুন:

  • উইজেট এলিমেন্টর → রেন্ডারারকে কল করে।
  • শর্টকোড → রেন্ডারারকে কল করে।
  • সংযোজন গুটেনবার্গ dynamic → রেন্ডারারকে কল করে।

আপনি কোয়েরি লজিকের পুনরাবৃত্তি, ফলব্যাক এবং এস্কেপিং এড়িয়ে চলেন।

ইনস্টলেশন-পরবর্তী পরীক্ষা

  1. প্লাগইনটি সক্রিয় করুন এক্সটেনশানগুলি.
  2. এলিমেন্টর দিয়ে একটি পৃষ্ঠা খুলুন।
  3. “Author Insert (BPCAB)” অনুসন্ধান করুন।
  4. উইজেটটি ড্রপ করুন, একটি বৈধ ইউজার আইডি প্রবেশ করান।
  5. সামনের দিকটা পরীক্ষা করুন:
    • অবতার প্রদর্শিত
    • প্রদর্শিত নাম (ইউআরএল প্রদান করা হলে ক্লিকযোগ্য),
    • সক্রিয় হলে বায়ো প্রদর্শিত হবে।
    • প্রদর্শিত ও সীমিত আইটেমসমূহের তালিকা।
  6. স্টাইল কন্ট্রোলগুলো পরীক্ষা করুন: ব্যাকগ্রাউন্ড কালার, টাইটেলের টাইপোগ্রাফি, প্যাডিং।

আপনার যদি কোনো ক্যাশিং সিস্টেম (ক্যাশ প্লাগইন, ক্লাউডফ্লেয়ার, সার্ভার ক্যাশ) থাকে, তবে তা পরিষ্কার করুন। আমি প্রায়শই দেখেছি যে ডেভেলপাররা মনে করেন "উইজেটটি লোড হচ্ছে না," অথচ প্রকৃতপক্ষে অ্যাক্টিভেশনের আগে ফ্রন্ট-এন্ড একটি লুকানো HTML পেজ পরিবেশন করছিল।

যদি সেটা কাজ না করে

দ্রুত চেকলিস্ট (ক্রমানুসারে)

  1. অ্যাক্টিভেশনের পর ত্রুটি ৫০০ দেখুন wp-content/debug.log (অথবা সার্ভার লগ)।
  2. এলিমেন্টরে অদৃশ্য উইজেট এলিমেন্টর কি সক্রিয়? হুক elementor/widgets/register সে কি প্রভাবিত হয়েছে?
  3. ক্লাস খুঁজে পাওয়া যায়নি সম্ভবত আপনি ক্লাসটি খুব তাড়াতাড়ি লোড করেছেন, অথবা পাথটি require_once মিথ্যা।
  4. CSS প্রয়োগ করা হয়নি ফাইলটি কি সঠিক পথে আছে? হ্যান্ডেলটি bpcab-author-box এটি কি সঠিকভাবে সংরক্ষিত হয়েছে? এলিমেন্টরের ক্যাশে পরিষ্কার করুন।
  5. কিছুই প্রদর্শিত হচ্ছে না একটি বিদ্যমান ইউজার আইডি পরীক্ষা করুন, তারপর কোয়েরিটি আলাদা করার জন্য “সর্বশেষ আর্টিকেল দেখান” অপশনটি নিষ্ক্রিয় করুন।

ডায়াগনস্টিক চার্ট

লক্ষণ সম্ভাব্য কারণ প্রতিপাদন সমাধান
এলিমেন্টর তালিকায় উইজেটটি অনুপস্থিত হুক কার্যকর হয়নি / এলিমেন্টর লোড হয়নি দেখুন যদি did_action('elementor/loaded') সত্য (লগ), এক্সটেনশনগুলি পরীক্ষা করুন এলিমেন্টর সক্রিয় করুন, এড়িয়ে চলুন init, ব্যবহার করুন elementor/widgets/register
ত্রুটি “ক্লাস 'ElementorWidget_Base' খুঁজে পাওয়া যায়নি” ক্লাস খুব তাড়াতাড়ি ভর্তি হয়ে গিয়েছিল। স্ট্যাক ট্রেস debug.log রেকর্ডিংটি সরান elementor/widgets/register এবং রাখুন require_once মধ্যে
CSS লোড হয়নি হ্যান্ডেল নিবন্ধিত নয় অথবা ভুল পথ নেটওয়ার্ক ট্যাবে, খুঁজুন author-box.css সঠিক plugins_url()চেক get_style_depends()ক্যাশে পরিষ্কার করুন
লেখকের নাম দেখা যাচ্ছে কিন্তু অ্যাভাটার দেখা যাচ্ছে না। গ্রাভাটার ব্লক করা হয়েছে / অবতার কনফিগারেশন সেটিংস > চ্যাট > অবতার, অথবা নেটওয়ার্ক সীমাবদ্ধতা গ্রাভাটার চালু করুন অথবা স্থানীয় অ্যাভাটার ব্যবহার করুন (প্লাগইন) / ফলব্যাক
আইটেমের খালি তালিকা কোনো প্রকাশিত পোস্ট নেই এমন লেখক / পোস্টের ধরণ খারাপ পোস্টগুলোর লেখক এবং "প্রকাশিত" অবস্থা যাচাই করুন। আইডি পরিবর্তন করুন, একটি পোস্ট প্রকাশ করুন, সামঞ্জস্য করুন post_type
অদৃশ্য পরিবর্তন ব্রাউজার ক্যাশে / পেজ ক্যাশে / এলিমেন্টর সিএসএস ক্যাশে প্রাইভেট ব্রাউজিং মোডে পরীক্ষা + প্লাগইন ক্যাশে পরিষ্কার করা + CSS পুনর্জন্ম ক্যাশে মুছে ফেলুন, সক্রিয় থাকলে Elementor CSS ফাইলগুলি পুনরায় তৈরি করুন।

সাধারণ ফাঁদ এবং ভুলগুলি

এরর কারণ সমাধান
কোডটি কপি করুন functions.php থিমের ভুল জায়গা: থিম পরিবর্তিত হতে পারে, এবং লোডিং অর্ডার এলিমেন্টরকে অকার্যকর করে দিতে পারে। একটি ডেডিকেটেড প্লাগইন (যেমন এটি) অথবা একটি MU-প্লাগইন ব্যবহার করুন।
উইজেট ক্লাসে সেমিকোলন দিতে ভুলে যাওয়া মারাত্মক পিএইচপি ত্রুটি সক্ষম করা WP_DEBUG_LOGনির্দেশিত লাইনটি পুনরায় পড়ুন, একটি IDE ব্যবহার করুন।
ব্যবহার add_action('init', ...) উইজেটটি সংরক্ষণ করতে এলিমেন্টর এখনও লোড হয়নি ব্যবহার elementor/widgets/register এবং পরীক্ষা did_action('elementor/loaded')
CSS/JS “খুঁজে পাওয়া যায়নি” ভুল পথে plugins_url() অথবা ফাইল তৈরি করা হয়নি ডিরেক্টরি কাঠামো এবং ফাইলের সঠিক নামগুলো যাচাই করুন।
থিমের সাথে শৈলীর সংঘাত (আভাদা/দিভি) CSS খুব সাধারণ (যেমন: .title) আপনার ক্লাসগুলির শুরুতে উপসর্গ যোগ করুন (bpcab-) এবং ব্যবহার করুন {{WRAPPER}} এলিমেন্টর সিলেক্টরগুলিতে
PHP আপডেটের পর ত্রুটি কঠোর টাইপিং + বেমানান পুরানো কোড PHP 8.1+ ব্যবহার করুন এবং সতর্কবার্তা/টাইপএরর (লগ) ঠিক করুন।
সরাসরি উৎপাদনে পরীক্ষা করা হচ্ছে সাদা পর্দার ঝুঁকি স্টেজিং + সংস্করণযুক্ত ডেপ্লয়মেন্ট + রোলব্যাক
পরিচ্ছন্নকরণ এবং পলায়নের মধ্যে বিভ্রান্তি “পরিষ্কার” ডেটা ধরে নেওয়া প্রবেশপথগুলো জীবাণুমুক্ত করুন, বহির্গমন পথগুলো খালি করুন, পদ্ধতিগতভাবে।

নিরাপত্তা, কর্মক্ষমতা এবং রক্ষণাবেক্ষণের পরামর্শ

  • XSS নিরাপত্তা প্রসঙ্গ অনুযায়ী প্রতিটি প্রস্থান পথ থেকে বেরিয়ে যান (esc_html পাঠ্য, esc_url URL- এ esc_attr বৈশিষ্ট্য)। রেফারেন্স: developer.wordpress.org/apis/security/escaping
  • অনুমতিসমূহ এখানে কোনো ফ্রন্ট-এন্ড ফর্ম নেই, তাই কোনো ননস (nonce) নেই। যদি আপনি অ্যাকশন যোগ করেন (যেমন, একটি 'লেখককে অনুসরণ করুন' বাটন), তাহলে ব্যবহার করুন wp_nonce_field এবং পরীক্ষা করুন current_user_can.
  • সম্পাদন পোস্টের সংখ্যা সীমিত করুন এবং উইজেটটির পুনরাবৃত্তি হলে ক্যাশিং চালু করুন। বড় ওয়েবসাইটগুলিতে এড়িয়ে চলুন get_users() সীমাহীন।
  • সঙ্গতি আপনার ক্লাস এবং হ্যান্ডেলগুলোর নামের শুরুতে প্রিফিক্স রাখুন। অন্যান্য এলিমেন্টর অ্যাড-অনের সাথে নামের সংঘর্ষ এড়ানোর জন্য এটিই সর্বোত্তম উপায়।
  • রক্ষণাবেক্ষণ প্লাগইনটির ভার্সন (Git) তৈরি করুন এবং একটি চেঞ্জলগ অন্তর্ভুক্ত করুন। স্নিপেটস প্লাগইনে "পেস্ট করা" স্নিপেট ব্যবহার করা থেকে বিরত থাকুন: আমি দেখেছি যে স্নিপেটস প্লাগইনটি এক্সিকিউশন অর্ডার পরিবর্তন করে দেওয়ায় আপডেটের ফলে অটোলোডেড ক্লাসগুলো অকার্যকর হয়ে যায়।
  • এসইও এই উইজেটটি কোনো লুকানো কন্টেন্ট যোগ করে না, তাই এতে কোনো এসইও ফাঁদ নেই। শুধুমাত্র জাভাস্ক্রিপ্ট ব্যবহার করে শর্তসাপেক্ষ কন্টেন্ট যোগ করার ক্ষেত্রে সতর্ক থাকুন।

সম্পদ

FAQ

চাইল্ড থিমে কোড না রেখে প্লাগইন তৈরি করার কারণ কী?

কারণ এলিমেন্টর উইজেট হলো একটি ফিচার। আপনি যদি থিম পরিবর্তন করেন (অথবা যদি আভাডা/ডিভি এর পরিবর্তে অন্য কিছু ব্যবহার করেন), আপনি উইজেটটি রাখতে চাইবেন। এবং এর ফলে লোডিং অর্ডারের অপ্রত্যাশিত সমস্যাও এড়ানো যায়।

এলিমেন্টর প্রো ইনস্টল করা না থাকলে কি এই উইজেটটি কাজ করবে?

হ্যাঁ। কোডটি এলিমেন্টর উইজেট এপিআই (ফ্রি ভার্সন) ব্যবহার করে। এলিমেন্টর প্রো-তে অতিরিক্ত ফিচার (থিম বিল্ডার, ইত্যাদি) রয়েছে, কিন্তু এই উইজেটটি তার থেকে স্বাধীন।

এলিমেন্টরের “শর্টকোড” উইজেটে শর্টকোড ব্যবহার করা যায় না কেন?

আপনি বেশিরভাগ নেটিভ স্টাইল কন্ট্রোল হারান এবং শেষ পর্যন্ত আপনাকে গ্লোবাল CSS ইনজেক্ট করতে হয়। পুনরাবৃত্তিমূলক কম্পোনেন্টের জন্য উইজেট একটি বেশি পরিচ্ছন্ন পদ্ধতি।

এলিমেন্টরে “General”-এর পরিবর্তে “BPCAB” ক্যাটাগরিটি কীভাবে যোগ করব?

আপনি ক্যাটাগরি হুকস-এর মাধ্যমে একটি ডেডিকেটেড এলিমেন্টর ক্যাটাগরি সেভ করতে পারেন (আপনার এলিমেন্টর ভার্সনের উপর নির্ভর করে)। আমার যখন ৫টির বেশি উইজেট থাকে, তখন আমি এটা করি; অন্যথায়, UI-কে খণ্ডিত হওয়া থেকে বাঁচাতে আমি "জেনারেল" ক্যাটাগরিটিই ব্যবহার করি।

Pourquoi get_settings_for_display() এবং না get_settings() ?

get_settings_for_display() কিছু অভ্যন্তরীণ এলিমেন্টর রূপান্তর সহ প্রদর্শনের জন্য প্রস্তুত মান ফেরত দেয়। সাধারণত এটিই সঠিক পছন্দ। render().

ইউজার আইডি প্রবেশ করানো কীভাবে এড়ানো যায় (যা ব্যবহারকারী-বান্ধব নয়)?

একটি ব্যবহার করুন SELECT অ্যাক্সেস সীমিত করুন (সর্বোচ্চ ২০০ জন ব্যবহারকারী) অথবা একটি এজেক্স (AJAX) কন্ট্রোল প্রয়োগ করুন। অধিক ব্যবহারকারী-বহুল সাইটগুলিতে, একটি আইডি ফিল্ড এবং অভ্যন্তরীণ ডকুমেন্টেশন কখনও কখনও সবচেয়ে স্থিতিশীল সমাধান হয়ে থাকে।

পরিবর্তনের পরেও আমার CSS কেন আপডেট হচ্ছে না?

ক্যাশে পরিষ্কার করুন:

  • ব্রাউজার ক্যাশে
  • আপনার ক্যাশিং প্লাগইনের ক্যাশে,
  • এবং যদি আপনি এলিমেন্টরের সিএসএস জেনারেশন ব্যবহার করেন, তাহলে (আপনার কনফিগারেশন অনুযায়ী) রিজেনারেশন জোরপূর্বক করুন।

এই উইজেটটি কি এলিমেন্টর থিম বিল্ডার টেমপ্লেটে (সিঙ্গেল পোস্ট) ব্যবহার করা যাবে?

হ্যাঁ। এটি আসলে একটি ভালো ব্যবহার: কোনো আর্টিকেলের নিচে লেখকের নাম দেখানোর বক্স। সেক্ষেত্রে, লজিকটি উন্নত করে বর্তমান পোস্টের লেখককে ব্যবহার করার ব্যবস্থা করুন যদি... user_id খালি (যোগ করার একটি সহজ বিকল্প)।

আমি কীভাবে উইজেটটিকে এমনভাবে পরিবর্তন করতে পারি যাতে এটি স্বয়ংক্রিয়ভাবে বর্তমান পোস্টের লেখককে প্রদর্শন করে?

মধ্যে render()হ্যাঁ $user_id মূল্য ০, পুনরুদ্ধার করুন get_post_field('post_author', get_the_ID()) যখন আপনি কোনো লুপ বা টেমপ্লেটের মধ্যে থাকবেন, তখন পোস্ট কনটেক্সটবিহীন স্ট্যাটিক পেজগুলোর ব্যাপারে সতর্ক থাকুন।

এই কোডটি কি PHP 8.2/8.3/8.4 এর সাথে সামঞ্জস্যপূর্ণ?

হ্যাঁ, নীতিগতভাবে (আধুনিক সিনট্যাক্স, স্ট্রিক্ট টাইপিং)। মূল যে বিষয়টির দিকে খেয়াল রাখতে হবে তা হলো এলিমেন্টরের সাথে সামঞ্জস্যতা এবং অন্যান্য প্লাগইন থেকে আসা ডেপ্রিকেটেড সতর্কবার্তা।

সাইটটি নষ্ট না করে আমি কীভাবে সঠিকভাবে পরীক্ষা করতে পারি?

স্টেজিং, লগ চালু এবং একটি সহজ পদ্ধতি হলো: প্লাগইনটি সক্রিয় করুন, উইজেটটি একটি টেস্ট পেজে ড্রপ করুন, ফ্রন্ট-এন্ড পরীক্ষা করুন, তারপর ফলব্যাকগুলো যাচাই করার জন্য একটি “লেখক খুঁজে পাওয়া যায়নি” (অস্তিত্বহীন আইডি) কেস পরীক্ষা করুন।