Re: genel rss ekleme uygulaması wordpress
Gönderilme zamanı: Sal Şub 18, 2025 4:48 pm
bu uygulamada döngüde bir sorun var. rss admin ayarlarını yaptıktan sonra bir kaç haber çektikten sonra daha çekim yapmıyor bunu bir kontrol eder misin.Aşağıdaki örnekte, RSS kaynaklarınızı her seferinde (bütün döngüyü) kontrol edecek şekilde batch (parça) mantığını güncelliyoruz. Böylece, örneğin tek bir kaynak varsa veya kaynak sayınız azsa, döngü sonunda offset null yerine 0’ye (yani tekrar en başa) ayarlanıyor. Ayrıca, JavaScript tarafında da; eğer bir döngüde hiç yeni haber eklenmezse döngü sonlanıyor, aksi halde yine kaynaklar kontrol ediliyor.
Aşağıdaki kodu inceleyebilirsiniz:Kod:Tümünü seç
<?php /* Plugin Name: Genel RSS Haber Scraper Description: Excel benzeri tablo üzerinden RSS kaynaklarını ekleyip, RSS haberlerini çekmenize olanak tanır. Ayarlarda; RSS adresi, kategoriler, RSS haber etiket kodu, link eklensin mi, resim HTML kodu ve bağlantı kontrolü yapabilirsiniz. Ayrıca, yazı içeriğinden cümle başları hariç büyük harfli olan ve en çok geçen kelimelerden otomatik 5 etiket oluşturur. Görsel URL’leri için <img src="..."> içindeki .jpg gibi URL’lerde, query string kısmı hariç çekilir. Version: 1.1 Author: Örnek Geliştirici License: GPL2 */ if ( ! defined( 'ABSPATH' ) ) { exit; } class Genel_RSS_Haber_Scraper { private $option_name = 'genel_rss_haber_scraper_sources'; public function __construct() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'wp_ajax_genel_rss_scrape', array( $this, 'handle_scrape_request' ) ); add_action( 'wp_ajax_genel_rss_connection_check', array( $this, 'handle_connection_check' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'frontend_enqueue_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); } public function add_admin_menu() { add_menu_page( 'Genel RSS Haber Scraper', 'RSS Haber Scraper', 'manage_options', 'genel-rss-haber-scraper', array( $this, 'admin_page' ), 'dashicons-rss', 20 ); } public function register_settings() { // Ayarları kaydederken sanitize callback ekliyoruz. register_setting( 'genel_rss_haber_scraper_options', $this->option_name, array( $this, 'sanitize_options' ) ); } /** * Ayarları sanitize eder. * * @param array $options Gönderilen ayarlar dizisi. * @return array Temizlenmiş ayarlar dizisi. */ public function sanitize_options( $options ) { if ( is_array( $options ) ) { foreach ( $options as $key => $source ) { // Eğer aktif seçeneği gönderilmemişse 0 yapıyoruz. $options[$key]['active'] = isset( $source['active'] ) ? 1 : 0; // Link seçeneği için de aynı işlemi yapıyoruz. $options[$key]['add_link'] = isset( $source['add_link'] ) ? 1 : 0; // Diğer alanları da uygun şekilde sanitize ediyoruz. $options[$key]['rss_url'] = isset( $source['rss_url'] ) ? esc_url_raw( $source['rss_url'] ) : ''; $options[$key]['categories'] = isset( $source['categories'] ) ? sanitize_text_field( $source['categories'] ) : ''; $options[$key]['tag'] = isset( $source['tag'] ) ? sanitize_text_field( $source['tag'] ) : ''; $options[$key]['image_html'] = isset( $source['image_html'] ) ? wp_kses_post( $source['image_html'] ) : ''; } } return $options; } public function admin_enqueue_scripts( $hook ) { if ( $hook == 'toplevel_page_genel-rss-haber-scraper' ) { wp_enqueue_script( 'genel-rss-admin', plugin_dir_url( __FILE__ ) . 'js/genel-rss-admin.js', array( 'jquery' ), '1.0', true ); wp_localize_script( 'genel-rss-admin', 'genelRssAdmin', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) ); } } public function frontend_enqueue_scripts() { wp_enqueue_script( 'genel-rss-frontend', plugin_dir_url( __FILE__ ) . 'js/genel-rss-frontend.js', array( 'jquery' ), '1.0', true ); wp_localize_script( 'genel-rss-frontend', 'genelRssAjax', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) ); } public function admin_page() { // Kayıtlı RSS kaynaklarını alıyoruz $sources = get_option( $this->option_name, array() ); ?> <div class="wrap"> <h1>Genel RSS Haber Scraper</h1> <form method="post" action="options.php"> <?php settings_fields( 'genel_rss_haber_scraper_options' ); ?> <table class="widefat" id="rss-sources-table"> <thead> <tr> <th>RSS Adresi</th> <th>Kategoriler (virgülle ayrılmış)</th> <th>RSS Haber Etiket</th> <th>Etkin mi?</th> <th>Link Eklensin mi?</th> <th>Resim HTML Kodu</th> <th>Bağlantı Kontrol</th> <th>İşlem</th> </tr> </thead> <tbody> <?php if ( ! empty( $sources ) ) { foreach ( $sources as $index => $source ) { $rss_url = isset( $source['rss_url'] ) ? $source['rss_url'] : ''; $categories = isset( $source['categories'] ) ? $source['categories'] : ''; $tag = isset( $source['tag'] ) ? $source['tag'] : ''; // Varsayılan olarak aktif; veritabanında 0 kaydedildiyse işaretlenmeyecek. $active = isset( $source['active'] ) ? $source['active'] : 1; $add_link = isset( $source['add_link'] ) ? $source['add_link'] : 0; $image_html = isset( $source['image_html'] ) ? $source['image_html'] : ''; ?> <tr> <td> <input type="text" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][rss_url]" value="<?php echo esc_attr( $rss_url ); ?>" size="40" /> </td> <td> <input type="text" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][categories]" value="<?php echo esc_attr( $categories ); ?>" size="30" placeholder="Kategori1,Kategori2" /> </td> <td> <input type="text" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][tag]" value="<?php echo esc_attr( $tag ); ?>" size="10" placeholder="item veya entry" /> </td> <td> <input type="checkbox" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][active]" value="1" <?php checked( $active, 1 ); ?> /> </td> <td> <input type="checkbox" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][add_link]" value="1" <?php checked( $add_link, 1 ); ?> /> </td> <td> <input type="text" name="<?php echo $this->option_name; ?>[<?php echo $index; ?>][image_html]" value="<?php echo esc_attr( $image_html ); ?>" size="30" placeholder="<img src='...' />" /> </td> <td> <button class="button connection-check" data-row-index="<?php echo $index; ?>">Bağlantıyı Kontrol Et</button> <span class="connection-result" id="conn-result-<?php echo $index; ?>"></span> </td> <td> <button class="button remove-row">Kaldır</button> </td> </tr> <?php } } ?> </tbody> </table> <p> <button type="button" class="button" id="add-row">Yeni Satır Ekle</button> </p> <?php submit_button(); ?> </form> <hr> <h2>Manuel Haber Çekme (Batch Processing)</h2> <p>RSS haberlerini çekmek için aşağıdaki butona tıklayın. İşlem, kaynakları parça parça (batch) kontrol ederek yapılır.</p> <button id="fetch-rss-news" class="button button-primary">Haberleri Çek</button> <div id="rss-news-log" style="margin-top:20px;padding:10px;background:#f9f9f9;border:1px solid #ddd;max-height:300px;overflow-y:auto;"></div> <script> jQuery(document).ready(function($) { var log = $('#rss-news-log'); $('#fetch-rss-news').on('click', function() { log.html('<p>Haber çekme işlemi başlatıldı...</p>'); processBatch(0); }); function processBatch(offset) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'genel_rss_scrape', offset: offset }, success: function(response) { console.log(response); if(response.success) { log.append('<p style="color:green;">' + response.data.message + '</p>'); // Eğer bu döngüde yeni haber eklendiyse offset’i döngüye devam etmek için kullanıyoruz. if ( response.data.new_posts > 0 ) { processBatch( response.data.next_offset ); } else { log.append('<p style="font-weight:bold;">Tüm kaynaklar işlendi.</p>'); } } else { log.append('<p style="color:red;">' + response.data.message + '</p>'); } }, error: function() { log.append('<p style="color:red;">Bağlantı hatası!</p>'); } }); } }); </script> </div> <?php } // AJAX: Manuel haber çekme işlemi (batch processing) public function handle_scrape_request() { @set_time_limit(30); ini_set('default_socket_timeout', 15); $offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0; $batch_size = 2; $result = $this->fetch_news_batch( $offset, $batch_size ); if ( $result['new_posts'] > 0 ) { wp_send_json_success( array( 'message' => $result['new_posts'] . ' yeni haber eklendi.', 'new_posts' => $result['new_posts'], 'next_offset' => $result['next_offset'] ) ); } else { wp_send_json_success( array( 'message' => 'İşlenen ' . $result['processed'] . ' kaynaktan yeni haber bulunamadı.', 'new_posts' => 0, 'next_offset' => $result['next_offset'] ) ); } } // AJAX: Bağlantı kontrolü için RSS adresi ve etiket bilgisini kontrol eder public function handle_connection_check() { $rss_url = isset( $_POST['rss_url'] ) ? esc_url_raw( $_POST['rss_url'] ) : ''; $tag = isset( $_POST['tag'] ) ? sanitize_text_field( $_POST['tag'] ) : ''; if ( empty( $rss_url ) || empty( $tag ) ) { wp_send_json_error( array( 'message' => 'RSS URL ve Etiket bilgisi gerekli.' ) ); } $rss_feed = @simplexml_load_file( $rss_url ); if ( ! $rss_feed ) { wp_send_json_error( array( 'message' => 'RSS beslemesi yüklenemedi.' ) ); } if ( $tag === 'item' && isset( $rss_feed->channel->item ) ) { wp_send_json_success( array( 'message' => 'Bağlantı başarılı: <item> bulundu.' ) ); } elseif ( $tag === 'entry' && isset( $rss_feed->entry ) ) { wp_send_json_success( array( 'message' => 'Bağlantı başarılı: <entry> bulundu.' ) ); } else { wp_send_json_error( array( 'message' => 'Belirtilen etiket bulunamadı.' ) ); } } /** * Belirtilen offset ve batch size'a göre RSS kaynaklarını kontrol eder. * Döngü sonunda, tüm kaynaklar işlendiğinde offset 0 olarak ayarlanır (yani döngü yeniden başlar). * * @param int $offset İşleme başlanacak kaynak indeksi. * @param int $batch_size Bir istekte işlenecek kaynak sayısı. * @return array İşlenen kaynak sayısı, eklenen yeni haber sayısı ve sonraki offset. */ private function fetch_news_batch( $offset, $batch_size ) { $sources = get_option( $this->option_name, array() ); $total = count( $sources ); if ( $total === 0 ) { return array( 'processed' => 0, 'new_posts' => 0, 'next_offset' => null ); } $processed = 0; $new_posts = 0; $end = min( $offset + $batch_size, $total ); for ( $i = $offset; $i < $end; $i++ ) { $inserted = $this->process_source( $sources[ $i ] ); $new_posts += $inserted; $processed++; } // Eğer döngü sona erdiyse (tüm kaynaklar işlendi) offset’i 0 yapıyoruz. $next_offset = ($offset + $batch_size) >= $total ? 0 : ($offset + $batch_size); return array( 'processed' => $processed, 'new_posts' => $new_posts, 'next_offset' => $next_offset ); } /** * Verilen bir RSS kaynağından (tek kaynak) haber çekmeye çalışır. * * @param array $source Kaynak bilgileri. * @return int Eklenen yeni haber sayısı. */ private function process_source( $source ) { // Eğer kaynak devre dışıysa işleme almayız. if ( empty( $source['active'] ) ) { return 0; } require_once( ABSPATH . 'wp-admin/includes/post.php' ); $rss_url = isset( $source['rss_url'] ) ? trim( $source['rss_url'] ) : ''; $categories_str = isset( $source['categories'] ) ? trim( $source['categories'] ) : ''; $tag = isset( $source['tag'] ) ? trim( $source['tag'] ) : ''; $add_link = isset( $source['add_link'] ) ? $source['add_link'] : 0; $image_html = isset( $source['image_html'] ) ? trim( $source['image_html'] ) : ''; if ( empty( $rss_url ) || empty( $tag ) ) { return 0; } $rss_feed = @simplexml_load_file( $rss_url ); if ( ! $rss_feed ) { error_log( 'RSS yüklenemedi: ' . $rss_url ); return 0; } $inserted = 0; if ( $tag === 'item' && isset( $rss_feed->channel->item ) ) { foreach ( $rss_feed->channel->item as $item ) { $title = (string) $item->title; if ( post_exists( $title ) ) { continue; } $link = (string) $item->link; $description = (string) strip_tags( $item->description ); $content = isset( $item->children( 'content', true )->encoded ) ? (string) strip_tags( $item->children( 'content', true )->encoded ) : ''; $image_url = isset( $item->enclosure['url'] ) ? (string) $item->enclosure['url'] : ''; if ( empty( $image_url ) ) { $combined_text = $description . ' ' . $content; if ( preg_match( '/(https?:\/\/[^\s"\']+\.(?:png|jpg|webp))(\?[^\s"\']*)?/i', $combined_text, $matches ) ) { $image_url = $matches[1]; } } if ( empty( $title ) || ( empty( $content ) && empty( $description ) ) ) { continue; } $post_content = '<p>' . esc_html( $description ) . '</p><p>' . esc_html( $content ) . '</p>'; if ( $add_link ) { $post_content .= '<p><a href="' . esc_url( $link ) . '" target="_blank" rel="noopener noreferrer">Kaynak</a></p>'; } $category_ids = array(); if ( ! empty( $categories_str ) ) { $categories = array_map( 'trim', explode( ',', $categories_str ) ); foreach ( $categories as $cat_name ) { $cat_id = get_cat_ID( $cat_name ); if ( ! $cat_id ) { $cat_id = wp_create_category( $cat_name ); } $category_ids[] = $cat_id; } } $post_id = wp_insert_post( array( 'post_title' => wp_strip_all_tags( $title ), 'post_content' => $post_content, 'post_status' => 'publish', 'post_author' => 1, 'post_category' => $category_ids, 'post_type' => 'post' ) ); if ( ! is_wp_error( $post_id ) ) { if ( ! empty( $image_url ) ) { require_once( ABSPATH . 'wp-admin/includes/media.php' ); require_once( ABSPATH . 'wp-admin/includes/file.php' ); require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attachment_id = media_sideload_image( $image_url, $post_id, $title, 'id' ); if ( ! is_wp_error( $attachment_id ) ) { set_post_thumbnail( $post_id, $attachment_id ); } } if ( ! empty( $image_html ) ) { $post_content .= "\n" . $image_html; wp_update_post( array( 'ID' => $post_id, 'post_content' => $post_content ) ); } $tags = $this->extract_tags_from_content( $description . ' ' . $content ); if ( ! empty( $tags ) ) { wp_set_post_tags( $post_id, $tags ); } $inserted++; } } } elseif ( $tag === 'entry' && isset( $rss_feed->entry ) ) { foreach ( $rss_feed->entry as $entry ) { $title = (string) $entry->title; if ( post_exists( $title ) ) { continue; } $link = ''; if ( isset( $entry->link ) ) { foreach ( $entry->link as $l ) { $attributes = $l->attributes(); if ( isset( $attributes['rel'] ) && (string) $attributes['rel'] === 'alternate' ) { $link = (string) $attributes['href']; break; } } if ( empty( $link ) ) { $link = (string) $entry->link; } } $description = isset( $entry->summary ) ? (string) strip_tags( $entry->summary ) : ''; $content = isset( $entry->content ) ? (string) strip_tags( $entry->content ) : ''; $image_url = ''; if ( empty( $image_url ) ) { $combined_text = $description . ' ' . $content; if ( preg_match( '/(https?:\/\/[^\s"\']+\.(?:png|jpg|webp))(\?[^\s"\']*)?/i', $combined_text, $matches ) ) { $image_url = $matches[1]; } } if ( empty( $title ) || ( empty( $content ) && empty( $description ) ) ) { continue; } $post_content = '<p>' . esc_html( $description ) . '</p><p>' . esc_html( $content ) . '</p>'; if ( $add_link ) { $post_content .= '<p><a href="' . esc_url( $link ) . '" target="_blank" rel="noopener noreferrer">Kaynak</a></p>'; } $category_ids = array(); if ( ! empty( $categories_str ) ) { $categories = array_map( 'trim', explode( ',', $categories_str ) ); foreach ( $categories as $cat_name ) { $cat_id = get_cat_ID( $cat_name ); if ( ! $cat_id ) { $cat_id = wp_create_category( $cat_name ); } $category_ids[] = $cat_id; } } $post_id = wp_insert_post( array( 'post_title' => wp_strip_all_tags( $title ), 'post_content' => $post_content, 'post_status' => 'publish', 'post_author' => 1, 'post_category' => $category_ids, 'post_type' => 'post' ) ); if ( ! is_wp_error( $post_id ) ) { if ( ! empty( $image_url ) ) { require_once( ABSPATH . 'wp-admin/includes/media.php' ); require_once( ABSPATH . 'wp-admin/includes/file.php' ); require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attachment_id = media_sideload_image( $image_url, $post_id, $title, 'id' ); if ( ! is_wp_error( $attachment_id ) ) { set_post_thumbnail( $post_id, $attachment_id ); } } $tags = $this->extract_tags_from_content( $description . ' ' . $content ); if ( ! empty( $tags ) ) { wp_set_post_tags( $post_id, $tags ); } $inserted++; } } } return $inserted; } /** * Yazı içeriğinden, cümle başları hariç, ilk kelimeyi atlayarak * büyük harfli (ilk harfi büyük) kelimelerin frekansını hesaplar ve * en çok geçen 5 kelimeyi etiket olarak döndürür. */ private function extract_tags_from_content( $content ) { $sentences = preg_split( '/(?<=[.?!])\s+/', $content, -1, PREG_SPLIT_NO_EMPTY ); $words = array(); foreach ( $sentences as $sentence ) { $sentence_words = preg_split( '/\s+/', $sentence, -1, PREG_SPLIT_NO_EMPTY ); if ( count( $sentence_words ) > 1 ) { for ( $i = 1; $i < count( $sentence_words ); $i++ ) { $words[] = $sentence_words[$i]; } } } $filtered = array(); foreach ( $words as $word ) { $clean = trim( $word, ".,!?()[]{}\"'" ); if ( strlen( $clean ) > 2 && ctype_upper( substr( $clean, 0, 1 ) ) ) { $filtered[] = $clean; } } $freq = array(); foreach ( $filtered as $w ) { $key = strtolower( $w ); if ( isset( $freq[ $key ] ) ) { $freq[ $key ]++; } else { $freq[ $key ] = 1; } } arsort( $freq ); $top_tags = array_slice( array_keys( $freq ), 0, 5 ); return $top_tags; } } new Genel_RSS_Haber_Scraper();Açıklamalar
Batch İşlemi (fetch_news_batch) Güncellemesi:
Döngü sonunda, eğer offset + batch_size toplam kaynak sayısına ulaşırsa, offset sıfırlanıyor (yani next_offset = 0). Böylece; örneğin sadece 1 veya 2 kaynağınız varsa, döngüde bir kere tamamlandıktan sonra sonraki AJAX çağrısı yeniden tüm kaynakları kontrol eder.
JavaScript Tarafı:
Döngü her seferinde gelen next_offset değeriyle devam eder.
Eğer döngüde hiç yeni haber eklenmezse (new_posts 0 ise) döngü sonlanır. Böylece gereksiz AJAX çağrıları yapılmaz.
Bu güncellemelerle, ayarlar yapıldıktan ve birkaç haber çekildikten sonra da (örneğin, tek bir kaynak varsa) sistem her seferinde tüm kaynakları yeniden kontrol edecek ve eğer gerçekten yeni haber varsa ekleyecektir.