Yazı Sonuna İmza Ekleyen WordPress Eklentisi
WordPress ile çalışırken en çok karşılaştığım sorulardan biri şu oluyor: “Tüm yazıların sonuna otomatik olarak bir şey ekleyebilir miyiz?” Pazarlama ekibi yazar biyografisi istiyor, hukuk departmanı sorumluluk reddi beyanı istiyor, SEO ekibi belirli bir anahtar kelime bloğu istiyor. Her seferinde tek tek düzenlemek yerine bir eklenti yazarak bu işi otomatize etmek hem zaman kazandırıyor hem de kontrol sizde kalıyor. Bu yazıda sıfırdan bir WordPress eklentisi oluşturacağız, üstelik sadece “çalışan” değil, gerçekten üretim ortamında kullanılabilir, ayarlanabilir bir şey yapacağız.
Eklenti Klasörünü ve Temel Dosyayı Oluşturmak
WordPress eklentileri wp-content/plugins/ dizini altında yaşar. Her eklentinin kendi klasörü olmalı ve o klasörde en az bir PHP dosyası bulunmalıdır. Basit eklentiler tek dosyayla da çalışır ama biz işleri biraz daha düzgün tutacağız.
mkdir /var/www/html/wp-content/plugins/yazi-imzasi
touch /var/www/html/wp-content/plugins/yazi-imzasi/yazi-imzasi.php
touch /var/www/html/wp-content/plugins/yazi-imzasi/README.md
ls -la /var/www/html/wp-content/plugins/yazi-imzasi/
Klasörü oluşturduktan sonra ana PHP dosyasını açıyoruz. WordPress bir eklentiyi tanıması için dosyanın başında belirli bir yorum bloğu arıyor. Bu blok olmadan eklenti listesinde görünmüyor bile:
cat > /var/www/html/wp-content/plugins/yazi-imzasi/yazi-imzasi.php << 'EOF'
<?php
/**
* Plugin Name: Yazı İmzası
* Plugin URI: https://siteniz.com/yazi-imzasi
* Description: Tüm yazıların veya seçili kategorilerin sonuna otomatik imza/açıklama ekler.
* Version: 1.0.0
* Author: Sistem Yöneticisi
* Author URI: https://siteniz.com
* License: GPL v2 or later
* Text Domain: yazi-imzasi
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
EOF
ABSPATH kontrolü kritik. Birisi dosyaya doğrudan tarayıcıdan erişmeye çalışırsa WordPress yüklü olmayacağı için bu satır PHP çalışmasını durduruyor. Prodüksiyon ortamında her zaman bu kontrol olmalı.
Ana İmza Fonksiyonu
Eklentinin kalbi the_content filtresi. WordPress bir yazının içeriğini ekranda göstermeden önce bu filtreden geçiriyor. Biz de bu noktaya kancayı takıyoruz:
<?php
// Önceki koda devam ediyoruz
function yazi_imzasi_ekle( $icerik ) {
// Sadece tekil yazı sayfalarında çalış
if ( ! is_single() ) {
return $icerik;
}
// Sadece 'post' tipindeki yazılara ekle
if ( get_post_type() !== 'post' ) {
return $icerik;
}
// Ana döngü içindeyiz, değil miyiz?
if ( ! in_the_loop() ) {
return $icerik;
}
$ayarlar = get_option( 'yazi_imzasi_ayarlar', array() );
$imza_metni = isset( $ayarlar['imza_metni'] )
? wp_kses_post( $ayarlar['imza_metni'] )
: '';
if ( empty( $imza_metni ) ) {
return $icerik;
}
$imza_html = '<div class="yazi-imzasi-kutu">' . $imza_metni . '</div>';
return $icerik . $imza_html;
}
add_filter( 'the_content', 'yazi_imzasi_ekle' );
Burada dikkat edilmesi gereken birkaç nokta var. is_single() kontrolü yapmazsanız ana sayfada, kategori sayfalarında, her yerde imza görünür. in_the_loop() kontrolü ise başka eklentilerin the_content‘i manuel tetiklemesi durumunda çift imza eklenmesini önlüyor. Bu ikisini atlayanların sitelerinde garip davranışlar çıkıyor, tecrübeyle sabittir.
Kategori Bazlı Filtreleme
Gerçek dünyada “sadece şu kategorideki yazılara ekle” ihtiyacı çok sık çıkıyor. Diyelim ki “Haberler” kategorisindeki yazılara bir sorumluluk reddi beyanı eklemek istiyorsunuz ama “Nasıl Yapılır” yazılarına eklemek istemiyorsunuz. Bunu da fonksiyona entegre edelim:
<?php
function yazi_imzasi_ekle( $icerik ) {
if ( ! is_single() || ! in_the_loop() || get_post_type() !== 'post' ) {
return $icerik;
}
$ayarlar = get_option( 'yazi_imzasi_ayarlar', array() );
$imza_metni = isset( $ayarlar['imza_metni'] ) ? wp_kses_post( $ayarlar['imza_metni'] ) : '';
if ( empty( $imza_metni ) ) {
return $icerik;
}
// Kategori filtresi aktif mi kontrol et
$kategori_filtresi = isset( $ayarlar['kategori_filtresi'] ) ? (bool) $ayarlar['kategori_filtresi'] : false;
if ( $kategori_filtresi ) {
$secili_kategoriler = isset( $ayarlar['kategoriler'] ) ? (array) $ayarlar['kategoriler'] : array();
if ( ! empty( $secili_kategoriler ) ) {
$mevcut_kategoriler = wp_get_post_categories( get_the_ID(), array( 'fields' => 'ids' ) );
$kesisim = array_intersect( $secili_kategoriler, $mevcut_kategoriler );
if ( empty( $kesisim ) ) {
return $icerik;
}
}
}
$imza_sinifi = isset( $ayarlar['ozel_sinif'] ) && ! empty( $ayarlar['ozel_sinif'] )
? sanitize_html_class( $ayarlar['ozel_sinif'] )
: 'yazi-imzasi-kutu';
$imza_html = sprintf( '<div class="%s">%s</div>', esc_attr( $imza_sinifi ), $imza_metni );
return $icerik . $imza_html;
}
array_intersect kullanmak burada çok pratik. Yazının kategorileri ile admin’in seçtiği kategorilerin kesişimi boşsa imza ekleme, doluysa ekle. Basit ama etkili.
Yönetim Paneli Ayar Sayfası
Komut satırından wp option update ile ayarları güncelleyebilirsiniz ama editörleriniz için bir arayüz şart. WordPress Settings API biraz verbose ama standart ve güvenli:
<?php
// Menü ekle
function yazi_imzasi_menu_ekle() {
add_options_page(
'Yazı İmzası Ayarları',
'Yazı İmzası',
'manage_options',
'yazi-imzasi',
'yazi_imzasi_ayar_sayfasi'
);
}
add_action( 'admin_menu', 'yazi_imzasi_menu_ekle' );
// Ayarları kaydet
function yazi_imzasi_ayarlari_kaydet() {
register_setting(
'yazi_imzasi_grup',
'yazi_imzasi_ayarlar',
array(
'sanitize_callback' => 'yazi_imzasi_sanitize',
'default' => array(),
)
);
}
add_action( 'admin_init', 'yazi_imzasi_ayarlari_kaydet' );
// Sanitizasyon fonksiyonu
function yazi_imzasi_sanitize( $girdi ) {
$temiz = array();
if ( isset( $girdi['imza_metni'] ) ) {
$temiz['imza_metni'] = wp_kses_post( $girdi['imza_metni'] );
}
$temiz['kategori_filtresi'] = isset( $girdi['kategori_filtresi'] ) ? 1 : 0;
if ( isset( $girdi['kategoriler'] ) && is_array( $girdi['kategoriler'] ) ) {
$temiz['kategoriler'] = array_map( 'absint', $girdi['kategoriler'] );
}
if ( isset( $girdi['ozel_sinif'] ) ) {
$temiz['ozel_sinif'] = sanitize_html_class( $girdi['ozel_sinif'] );
}
return $temiz;
}
wp_kses_post burada neden kullandık? Çünkü imza içeriği HTML içerebilir, belki bir link, belki kalın yazı. Ama wp_kses_post, gibi tehlikeli etiketleri temizliyor. Sadece sanitize_text_field kullansaydık tüm HTML’i silmiş olurduk.
Ayar Sayfasının HTML Kısmı
<?php
function yazi_imzasi_ayar_sayfasi() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Bu sayfaya erişim yetkiniz yok.' );
}
$ayarlar = get_option( 'yazi_imzasi_ayarlar', array() );
$kategoriler = get_categories( array( 'hide_empty' => false ) );
$secili_kat = isset( $ayarlar['kategoriler'] ) ? (array) $ayarlar['kategoriler'] : array();
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form method="post" action="options.php">
<?php settings_fields( 'yazi_imzasi_grup' ); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="imza_metni">İmza İçeriği</label>
</th>
<td>
<?php
$imza_metni = isset( $ayarlar['imza_metni'] ) ? $ayarlar['imza_metni'] : '';
wp_editor( $imza_metni, 'yazi_imzasi_ayarlar_imza_metni', array(
'textarea_name' => 'yazi_imzasi_ayarlar[imza_metni]',
'textarea_rows' => 6,
'media_buttons' => false,
) );
?>
<p class="description">Yazıların sonuna eklenecek içerik. HTML kullanabilirsiniz.</p>
</td>
</tr>
<tr>
<th scope="row">Kategori Filtresi</th>
<td>
<label>
<input type="checkbox"
name="yazi_imzasi_ayarlar[kategori_filtresi]"
value="1"
<?php checked( 1, isset( $ayarlar['kategori_filtresi'] ) ? $ayarlar['kategori_filtresi'] : 0 ); ?> />
Sadece seçili kategorilere uygula
</label>
</td>
</tr>
<?php if ( ! empty( $kategoriler ) ) : ?>
<tr>
<th scope="row">Kategoriler</th>
<td>
<?php foreach ( $kategoriler as $kategori ) : ?>
<label style="display:block; margin-bottom:5px;">
<input type="checkbox"
name="yazi_imzasi_ayarlar[kategoriler][]"
value="<?php echo esc_attr( $kategori->term_id ); ?>"
<?php checked( in_array( $kategori->term_id, $secili_kat ), true ); ?> />
<?php echo esc_html( $kategori->name ); ?>
<span style="color:#999;">(<?php echo intval( $kategori->count ); ?> yazı)</span>
</label>
<?php endforeach; ?>
</td>
</tr>
<?php endif; ?>
</table>
<?php submit_button( 'Ayarları Kaydet' ); ?>
</form>
</div>
<?php
}
CSS Stilini Eklentiye Dahil Etmek
İmza kutusunun görünümü için inline stil yazmak yerine düzgün bir şekilde CSS dosyası ekleyelim. Hem önbellek dostu olur hem de tema geliştiricileri override edebilir:
mkdir /var/www/html/wp-content/plugins/yazi-imzasi/assets
cat > /var/www/html/wp-content/plugins/yazi-imzasi/assets/style.css << 'EOF'
.yazi-imzasi-kutu {
margin-top: 2rem;
padding: 1.2rem 1.5rem;
background-color: #f8f9fa;
border-left: 4px solid #0073aa;
border-radius: 0 4px 4px 0;
font-size: 0.9rem;
line-height: 1.6;
color: #444;
}
.yazi-imzasi-kutu p:last-child {
margin-bottom: 0;
}
.yazi-imzasi-kutu a {
color: #0073aa;
text-decoration: underline;
}
@media ( max-width: 768px ) {
.yazi-imzasi-kutu {
padding: 1rem;
font-size: 0.85rem;
}
}
EOF
Sonra PHP tarafında bu CSS’i doğru şekilde yüklüyoruz:
<?php
function yazi_imzasi_css_yukle() {
if ( is_single() && get_post_type() === 'post' ) {
wp_enqueue_style(
'yazi-imzasi-stil',
plugin_dir_url( __FILE__ ) . 'assets/style.css',
array(),
'1.0.0'
);
}
}
add_action( 'wp_enqueue_scripts', 'yazi_imzasi_css_yukle' );
Neden is_single() kontrolü koyduk? CSS’i sadece gerekli olduğu yerde yüklemek için. Bazı eklentiler her sayfada onlarca CSS dosyası yüklüyor, bu sayfa hızını olumsuz etkiliyor. Koşullu yükleme iyi bir pratik.
WP-CLI ile Eklentiyi Yönetmek
Sunucu tarafında çalışan biri olarak WP-CLI olmadan WordPress yönetimi düşünemiyorum. Eklentimizi komut satırından da yönetelim:
# Eklentiyi aktifleştir
wp plugin activate yazi-imzasi --path=/var/www/html
# Mevcut ayarları kontrol et
wp option get yazi_imzasi_ayarlar --path=/var/www/html
# İmza metnini komut satırından güncelle
wp option update yazi_imzasi_ayarlar
'{"imza_metni":"<p>Bu yazı <strong>SysAdmin Blog</strong> ekibi tarafından hazırlanmıştır.</p>","kategori_filtresi":0}'
--format=json
--path=/var/www/html
# Eklentinin düzgün yüklenip yüklenmediğini doğrula
wp plugin list --path=/var/www/html | grep yazi-imzasi
# Cache temizle (W3TC veya WP Rocket varsa)
wp cache flush --path=/var/www/html
Özellikle birden fazla WordPress kurulumu yönetiyorsanız, bu komutları bir Bash betiğine koyup tüm sitelere aynı imzayı dağıtabilirsiniz. Tek tek panele girip ayar yapmak yerine:
#!/bin/bash
# Tum WordPress sitelerine imza ekle
SITES=("/var/www/site1" "/var/www/site2" "/var/www/site3")
IMZA='{"imza_metni":"<p>İçerik ekibimiz tarafından hazırlanmıştır. <a href="https://example.com">Daha fazlası</a></p>","kategori_filtresi":0}'
for site in "${SITES[@]}"; do
echo "İşleniyor: $site"
wp plugin activate yazi-imzasi --path="$site" 2>/dev/null
wp option update yazi_imzasi_ayarlar "$IMZA" --format=json --path="$site"
echo "Tamamlandı: $site"
done
Eklentiyi Paketlemek ve Dağıtmak
İç projeler için bile eklentinizi düzgün paketlemek iyi bir alışkanlık. Hem yedekleme kolaylaşıyor hem de farklı ortamlara taşımak daha temiz oluyor:
# Eklenti dizinine git
cd /var/www/html/wp-content/plugins/
# Gereksiz dosyaları hariç tutarak zip oluştur
zip -r yazi-imzasi-v1.0.0.zip yazi-imzasi/
--exclude "yazi-imzasi/.git/*"
--exclude "yazi-imzasi/.gitignore"
--exclude "yazi-imzasi/node_modules/*"
--exclude "yazi-imzasi/*.log"
# Zip içeriğini doğrula
unzip -l yazi-imzasi-v1.0.0.zip
# Checksum al, transfer sonrası bütünlük kontrolü için
md5sum yazi-imzasi-v1.0.0.zip > yazi-imzasi-v1.0.0.zip.md5
# Farklı sunucuya kopyala
scp yazi-imzasi-v1.0.0.zip kullanici@hedef-sunucu:/tmp/
Hedef sunucuda:
# WP-CLI ile zip'ten direkt yükle
wp plugin install /tmp/yazi-imzasi-v1.0.0.zip
--activate
--path=/var/www/html
Gerçek Dünya Kullanım Senaryosu: Hukuki Uyumluluk
Bir e-ticaret sitesi düşünün. Her blog yazısının sonuna “Bu içerik bilgi amaçlıdır, yatırım tavsiyesi değildir” gibi bir disclaimer eklemek istiyorsunuz. Avukat her ay metni güncelliyor. Bu eklenti sayesinde avukat panele giriyor, tek bir yerden güncelleme yapıyor, tüm yazılara anında yansıyor. Alternatifleri:
- Her yazıya elle disclaimer eklemek: 500 yazı varsa imkansız
- Temaya hardcode yazmak: Tema güncellemesinde gider
- Shortcode kullanmak: Her yazıya shortcode eklemek gerekir, eski yazıları ele almaz
the_content filtresi yaklaşımının güzel tarafı geriye dönük çalışması. Eklentiyi aktif ettiğinizde mevcut tüm yazılara imza ekleniyor, deaktif ettiğinizde hepsi temizleniyor. Veritabanında hiçbir iz kalmıyor çünkü içeriği değiştirmiyoruz, sadece render anında ekliyoruz.
Performans ve Önbellek Uyumluluğu
Nginx FastCGI cache, Varnish veya Redis page cache kullanıyorsanız bir soru aklınıza gelebilir: “İmza değiştiğinde eski cache’lenmiş sayfalar ne olacak?” Cevap: Onları geçersiz kılmanız gerekiyor. Çoğu önbellek eklentisi options.php post edildiğinde cache temizleme yapıyor ama buna güvenmemek daha sağlıklı:
# W3 Total Cache
wp w3-total-cache flush all --path=/var/www/html
# WP Super Cache
wp super-cache flush --path=/var/www/html
# WP Rocket
wp rocket clean --confirm --path=/var/www/html
# Nginx FastCGI Cache manuel temizleme
find /var/cache/nginx/ -type f -delete
nginx -s reload
Eklentinin ayar kaydedildiğinde otomatik cache temizlemesi yapması da mümkün. Settings API’nin sanitize_callback‘ine hook atarak:
<?php
function yazi_imzasi_sanitize( $girdi ) {
// ... önceki sanitizasyon kodu ...
// WP Rocket varsa cache temizle
if ( function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
// W3TC varsa
if ( function_exists( 'w3tc_flush_all' ) ) {
w3tc_flush_all();
}
return $temiz;
}
Sonuç
Sıfırdan yazdığımız bu eklenti basit görünebilir ama içinde ciddi kavramlar var. WordPress filter hooks, Settings API, sanitizasyon katmanları, koşullu CSS yükleme, WP-CLI entegrasyonu. Bunların hepsini öğrenmek için büyük projeler yapmanıza gerek yok; “yazı sonuna imza ekle” gibi somut bir problem en iyi öğretmen oluyor.
Bu yapıyı genişletebilirsiniz. Kullanıcı rolüne göre farklı imzalar, yazı yaşına göre farklı uyarılar, belirli etiketlere özel içerikler. Temel iskelet değişmiyor, sadece filtre mantığını zenginleştiriyorsunuz.
Prodüksiyona almadan önce şunu yapın: Eklentiyi bir staging ortamında test edin, Query Monitor eklentisiyle ekstra database query üretip üretmediğini kontrol edin ve tema değiştirdiğinizde CSS’in hala düzgün göründüğünden emin olun. Küçük eklentiler bile dikkatsizce yazıldığında ciddi yük yaratabilir, ama bu örnekteki yaklaşım bu riskleri minimize ediyor.
