Son Güncelleme Tarihi Gösteren WordPress Eklentisi Nasıl Yapılır
Bir WordPress sitesi yönetiyorsanız ve içerik tazeliği sizin için önemliyse, ziyaretçilere “bu makale en son ne zaman güncellendi?” sorusunu yanıtlamak düşündüğünüzden çok daha kritik. Google’ın içerik tazeliğini sıralamalarda dikkate aldığını biliyoruz, ama daha önemlisi kullanıcılar 2019’da yazılmış bir teknik makaleye güvenmek istemez. Bu yazıda, sıfırdan bir WordPress eklentisi yazarak yazılarda son güncelleme tarihini nasıl göstereceğimizi adım adım inceleyeceğiz.
Neden Hazır Eklenti Yerine Kendi Eklentimizi Yazıyoruz?
Bu soruyu çok duyuyorum. “Neden 5 tıkla kurulan bir eklenti varken uğraşalım?” Birkaç somut nedenim var:
- Hazır eklentiler genellikle ihtiyaç duyduğunuzdan 10 kat fazla şey yapıyor, yani gereksiz yük
- Temanız değiştiğinde hazır eklentinin çıktısı bozuluyor ve CSS’i düzeltmek için saçma hook’lar arıyorsunuz
- Kendi yazdığınız kodu anlıyorsunuz, hata ayıklama yaparken kafanız karışmıyor
- WordPress eklenti geliştirmeyi öğrenmek uzun vadede size ciddi zaman kazandırıyor
Üstelik bu eklenti, WordPress eklenti geliştirmenin temel kavramlarını öğrenmek için mükemmel bir örnek. Hook sistemi, shortcode, meta veri yönetimi, admin paneli entegrasyonu… hepsine değineceğiz.
Eklenti Dosya Yapısı
WordPress eklentileri wp-content/plugins/ dizinine yerleştirilir. Tek dosyalı basit eklentiler de olabilir, ama iyi bir alışkanlık olarak dizin yapısı kuralım:
wp-content/plugins/
└── son-guncelleme-tarihi/
├── son-guncelleme-tarihi.php # Ana eklenti dosyası
├── includes/
│ ├── class-sgt-display.php # Görüntüleme sınıfı
│ └── class-sgt-settings.php # Ayarlar sınıfı
└── assets/
└── sgt-style.css # Eklenti stilleri
Bu yapıyı terminal üzerinden şöyle oluşturabilirsiniz:
cd /var/www/html/wp-content/plugins/
mkdir -p son-guncelleme-tarihi/includes son-guncelleme-tarihi/assets
touch son-guncelleme-tarihi/son-guncelleme-tarihi.php
touch son-guncelleme-tarihi/includes/class-sgt-display.php
touch son-guncelleme-tarihi/includes/class-sgt-settings.php
touch son-guncelleme-tarihi/assets/sgt-style.css
Ana Eklenti Dosyası
Her WordPress eklentisi bir başlık bloğuyla başlamak zorunda. Bu blok olmazsa WordPress eklentiyi tanımıyor:
<?php
/**
* Plugin Name: Son Güncelleme Tarihi
* Plugin URI: https://siteniz.com/eklenti
* Description: Yazılarda son güncelleme tarihini otomatik veya shortcode ile gösterir.
* Version: 1.0.0
* Author: Siz
* Author URI: https://siteniz.com
* Text Domain: son-guncelleme-tarihi
* License: GPL v2 or later
*/
// Doğrudan erişimi engelle
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Sabitler
define( 'SGT_VERSION', '1.0.0' );
define( 'SGT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'SGT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
// Sınıfları yükle
require_once SGT_PLUGIN_DIR . 'includes/class-sgt-display.php';
require_once SGT_PLUGIN_DIR . 'includes/class-sgt-settings.php';
// Eklentiyi başlat
function sgt_init() {
$display = new SGT_Display();
$settings = new SGT_Settings();
$display->init();
$settings->init();
}
add_action( 'plugins_loaded', 'sgt_init' );
// Aktivasyon hook'u - varsayılan ayarları kaydet
register_activation_hook( __FILE__, 'sgt_activate' );
function sgt_activate() {
$defaults = array(
'sgt_format' => 'd F Y',
'sgt_prefix' => 'Son güncelleme: ',
'sgt_auto_insert' => '1',
'sgt_position' => 'after',
'sgt_post_types' => array( 'post' ),
);
foreach ( $defaults as $key => $value ) {
if ( false === get_option( $key ) ) {
add_option( $key, $value );
}
}
}
// Deaktivasyon hook'u
register_deactivation_hook( __FILE__, 'sgt_deactivate' );
function sgt_deactivate() {
// Şimdilik temizlik yapmıyoruz
// Ayarları silmek isteyenler için uninstall.php kullanılmalı
}
Buradaki if ( ! defined( 'ABSPATH' ) ) { exit; } satırı kritik. Bu, birisi eklenti dosyasına doğrudan URL ile erişmeye çalıştığında çalıştırılmasını engelliyor. WordPress kurulu her sistemde bu satırı görürsünüz ve es geçmeyin.
Görüntüleme Sınıfı
Eklentinin kalbi burada. class-sgt-display.php dosyamızı yazalım:
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class SGT_Display {
public function init() {
// CSS dosyasını yükle
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
// İçeriğe otomatik ekleme
add_filter( 'the_content', array( $this, 'auto_insert_date' ) );
// Shortcode kaydı
add_shortcode( 'son_guncelleme', array( $this, 'shortcode_handler' ) );
}
public function enqueue_styles() {
wp_enqueue_style(
'sgt-style',
SGT_PLUGIN_URL . 'assets/sgt-style.css',
array(),
SGT_VERSION
);
}
/**
* İçeriğe otomatik tarih ekle
*/
public function auto_insert_date( $content ) {
// Sadece ana sorgu ve tekil yazılarda çalış
if ( ! is_singular() || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
// Otomatik ekleme kapalıysa çık
if ( '1' !== get_option( 'sgt_auto_insert', '1' ) ) {
return $content;
}
// Post type kontrolü
$allowed_types = get_option( 'sgt_post_types', array( 'post' ) );
if ( ! in_array( get_post_type(), (array) $allowed_types ) ) {
return $content;
}
$date_html = $this->get_date_html( get_the_ID() );
if ( empty( $date_html ) ) {
return $content;
}
$position = get_option( 'sgt_position', 'after' );
if ( 'before' === $position ) {
return $date_html . $content;
}
return $content . $date_html;
}
/**
* HTML çıktısını üret
*/
public function get_date_html( $post_id = null ) {
if ( null === $post_id ) {
$post_id = get_the_ID();
}
$post = get_post( $post_id );
if ( ! $post ) {
return '';
}
$modified_date = $post->post_modified;
$published_date = $post->post_date;
// Yayın ve güncelleme tarihi aynıysa gösterme
// (İlk yayınlanan içerik için güncelleme tarihi anlamlı değil)
if ( strtotime( $modified_date ) <= strtotime( $published_date ) + 60 ) {
return '';
}
$format = get_option( 'sgt_format', 'd F Y' );
$prefix = get_option( 'sgt_prefix', 'Son güncelleme: ' );
$formatted_date = date_i18n( $format, strtotime( $modified_date ) );
$html = '<div class="sgt-modified-date">';
$html .= '<span class="sgt-prefix">' . esc_html( $prefix ) . '</span>';
$html .= '<time class="sgt-date" datetime="' . esc_attr( $modified_date ) . '">';
$html .= esc_html( $formatted_date );
$html .= '</time>';
$html .= '</div>';
return $html;
}
/**
* Shortcode işleyici
* Kullanım: [son_guncelleme format="d.m.Y" prefix="Güncellendi: "]
*/
public function shortcode_handler( $atts ) {
$atts = shortcode_atts(
array(
'id' => get_the_ID(),
'format' => get_option( 'sgt_format', 'd F Y' ),
'prefix' => get_option( 'sgt_prefix', 'Son güncelleme: ' ),
),
$atts,
'son_guncelleme'
);
return $this->get_date_html( intval( $atts['id'] ) );
}
}
date_i18n() kullanmamızın nedeni önemli: WordPress’in yerleşik tarih formatlaması kullanıcının dil ayarına göre ayı doğru dilde yazıyor. date() kullansaydık Türkçe ay isimleri için ekstra uğraşmamız gerekirdi.
Ayarlar Sınıfı ve Admin Paneli
Şimdi admin paneline bir ayar sayfası ekleyelim. class-sgt-settings.php:
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class SGT_Settings {
public function init() {
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
}
public function add_settings_page() {
add_options_page(
'Son Güncelleme Tarihi Ayarları',
'Son Güncelleme Tarihi',
'manage_options',
'son-guncelleme-tarihi',
array( $this, 'render_settings_page' )
);
}
public function register_settings() {
register_setting( 'sgt_settings_group', 'sgt_format', array(
'sanitize_callback' => 'sanitize_text_field',
'default' => 'd F Y',
));
register_setting( 'sgt_settings_group', 'sgt_prefix', array(
'sanitize_callback' => 'sanitize_text_field',
'default' => 'Son güncelleme: ',
));
register_setting( 'sgt_settings_group', 'sgt_auto_insert', array(
'sanitize_callback' => 'absint',
'default' => 1,
));
register_setting( 'sgt_settings_group', 'sgt_position', array(
'sanitize_callback' => 'sanitize_text_field',
'default' => 'after',
));
register_setting( 'sgt_settings_group', 'sgt_post_types', array(
'sanitize_callback' => array( $this, 'sanitize_post_types' ),
'default' => array( 'post' ),
));
}
public function sanitize_post_types( $input ) {
if ( ! is_array( $input ) ) {
return array();
}
$valid_types = array_keys( get_post_types( array( 'public' => true ) ) );
return array_intersect( $input, $valid_types );
}
public function render_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1>Son Güncelleme Tarihi - Ayarlar</h1>
<?php if ( isset( $_GET['settings-updated'] ) ) : ?>
<div class="notice notice-success is-dismissible">
<p>Ayarlar kaydedildi.</p>
</div>
<?php endif; ?>
<form method="post" action="options.php">
<?php settings_fields( 'sgt_settings_group' ); ?>
<table class="form-table">
<tr>
<th>Tarih Formatı</th>
<td>
<input type="text" name="sgt_format"
value="<?php echo esc_attr( get_option( 'sgt_format', 'd F Y' ) ); ?>"
class="regular-text" />
<p class="description">
PHP tarih formatı kullanın. Örnek: <code>d F Y</code> = 15 Ocak 2025
</p>
</td>
</tr>
<tr>
<th>Önek Metni</th>
<td>
<input type="text" name="sgt_prefix"
value="<?php echo esc_attr( get_option( 'sgt_prefix', 'Son güncelleme: ' ) ); ?>"
class="regular-text" />
</td>
</tr>
<tr>
<th>Otomatik Ekle</th>
<td>
<label>
<input type="checkbox" name="sgt_auto_insert" value="1"
<?php checked( '1', get_option( 'sgt_auto_insert', '1' ) ); ?> />
İçeriğe otomatik olarak ekle
</label>
</td>
</tr>
<tr>
<th>Konum</th>
<td>
<select name="sgt_position">
<option value="after" <?php selected( 'after', get_option( 'sgt_position', 'after' ) ); ?>>
İçerik Sonrası
</option>
<option value="before" <?php selected( 'before', get_option( 'sgt_position', 'after' ) ); ?>>
İçerik Öncesi
</option>
</select>
</td>
</tr>
</table>
<?php submit_button( 'Kaydet' ); ?>
</form>
</div>
<?php
}
}
CSS Dosyası
Güzel bir varsayılan görünüm için assets/sgt-style.css:
.sgt-modified-date {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.875rem;
color: #6b7280;
background-color: #f9fafb;
border-left: 3px solid #3b82f6;
padding: 6px 12px;
margin: 16px 0;
border-radius: 0 4px 4px 0;
}
.sgt-prefix {
font-weight: 600;
}
.sgt-date {
font-style: italic;
}
/* Koyu tema desteği */
@media (prefers-color-scheme: dark) {
.sgt-modified-date {
background-color: #1f2937;
color: #9ca3af;
}
}
Gerçek Dünya Senaryoları ve Sorun Giderme
Senaryo 1: Güncelleme tarihi yayın tarihiyle aynı görünüyor
Bu genellikle yazı kaydedildiğinde WordPress’in her ikisini de güncellediği durumda olur. Kodumuzdaki 60 saniyelik tolerans burada devreye giriyor. Ama bazı durumlarda daha uzun bir eşik gerekebilir. get_date_html() metodundaki şu satırı ihtiyacınıza göre ayarlayın:
// 60 yerine 3600 yaparsanız 1 saatten az süre içinde
// yapılan güncellemeleri "gerçek güncelleme" saymaz
if ( strtotime( $modified_date ) <= strtotime( $published_date ) + 3600 ) {
return '';
}
Senaryo 2: Shortcode’u başka bir eklentinin widget’ında kullanmak
Widget alanlarında shortcode’lar varsayılan olarak çalışmaz. functions.php‘ye şunu ekleyin:
// Widget'larda shortcode çalıştır
add_filter( 'widget_text', 'do_shortcode' );
Senaryo 3: WooCommerce ürünlerinde göstermek
Admin panelinde product post type’ını seçmeniz yeterli, ama WooCommerce’in the_content filtresini bazen farklı işlediğini görürsünüz. Güvenli çözüm:
// WooCommerce ürün sayfaları için özel hook
add_action( 'woocommerce_single_product_summary', function() {
$display = new SGT_Display();
echo $display->get_date_html( get_the_ID() );
}, 25 );
Eklentiyi Test Etmek
Geliştirme sırasında WordPress hata raporlamasını aktif etmek için wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Loglar /wp-content/debug.log dosyasına yazılır. Geliştirme bittikten sonra WP_DEBUG‘ı false yapın veya satırları kaldırın. Production’da açık bırakmak hem performans hem güvenlik açısından sorun.
Eklentiyi aktif etmeden önce dosya izinlerini kontrol edin:
# Eklenti dizininin sahipliği www-data olmalı (Nginx/Apache kullanıcısı)
chown -R www-data:www-data /var/www/html/wp-content/plugins/son-guncelleme-tarihi/
# İzinler
find /var/www/html/wp-content/plugins/son-guncelleme-tarihi/ -type f -exec chmod 644 {} ;
find /var/www/html/wp-content/plugins/son-guncelleme-tarihi/ -type d -exec chmod 755 {} ;
Performans Notları
the_content filtresine eklediğimiz her şey her sayfa yüklemesinde çalışıyor. Eklentimiz çok hafif, ama bunu söylemek istedim: database sorguları yapıyorsanız caching düşünün. Bizim durumumuzda get_post() zaten WordPress’in iç cache’ini kullanıyor, ekstra endişe yok.
Ancak eklentiyi çok sayıda post type’a aktif ederseniz ve her birinde farklı mantık kurgulamaya başlarsanız, o zaman wp_cache_get() / wp_cache_set() kullanmayı düşünün.
Bir de dikkat edilmesi gereken nokta: is_singular() && in_the_loop() && is_main_query() üçlüsü olmadan the_content filtreniz beklenmedik yerlerde tetiklenebilir. RSS feed’leri, widget’lar, API çıktıları… bunların hepsinde the_content çalışabilir. O üç kontrol olmadan eklentiniz sizi şaşırtır.
Sonuç
Gördüğünüz gibi sıfırdan bir WordPress eklentisi yazmak göründüğü kadar karmaşık değil. 200-300 satır kod ile yayın tarihi gösterme, admin paneli entegrasyonu, shortcode desteği ve CSS özelleştirme gibi özelliklere sahip eksiksiz bir eklenti yazdık.
Bu eklentiyi başlangıç noktası olarak kullanabilirsiniz. Buradan genişletmek isteyebileceğiniz birkaç fikir:
- Structured data (JSON-LD) desteği ekleyerek Google’ın tarihi daha doğru okumasını sağlayabilirsiniz
- Yazı düzenleme ekranına meta box ekleyerek tarih görüntülemeyi tekil yazı bazında kapatabilirsiniz
- REST API endpoint’i açarak headless WordPress kurulumlarında tarihi dışarıya sunabilirsiniz
Eklenti geliştirme konusundaki en büyük tavsiyem: WordPress Codex’i ve Hook Reference’ı sık sık okuyun. Aradığınız özelliğin büyük ihtimalle zaten bir hook’u var, sıfırdan yazmaya gerek yok. WordPress’in 20 yıllık geçmişi boyunca biriktirdiği bu altyapı, doğru kullanıldığında gerçekten güçlü.
