WordPress.org

Plugin Directory

Changeset 450793


Ignore:
Timestamp:
10/13/11 21:27:39 (3 years ago)
Author:
sirzooro
Message:

check badwords in tags, check and enforce 1st link position, check min link count, configure build-in wp defines, delete abandoned drafts, do not save invalid posts, lock users who abandons drafts, check post thumbnails, added filters and few fixes

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wypiekacz/trunk/wypiekacz.php

    r259125 r450793  
    55Description: Checks if posts submitted for review and posted satisfies set of rules. 
    66Author: Daniel Frużyński 
    7 Version: 2.1.1 
     7Version: 2.2 
    88Author URI: http://www.poradnik-webmastera.com/ 
    99Text Domain: wypiekacz 
     10License: GPL2 
    1011*/ 
    1112 
    12 /*  Copyright 2009-2010  Daniel Frużyński  (email : daniel [A-T] poradnik-webmastera.com) 
     13/*  Copyright 2009-2011  Daniel Frużyński  (email : daniel [A-T] poradnik-webmastera.com) 
    1314 
    1415    This program is free software; you can redistribute it and/or modify 
    15     it under the terms of the GNU General Public License as published by 
    16     the Free Software Foundation; either version 2 of the License, or 
    17     (at your option) any later version. 
     16    it under the terms of the GNU General Public License, version 2, as  
     17    published by the Free Software Foundation. 
    1818 
    1919    This program is distributed in the hope that it will be useful, 
     
    2828 
    2929 
    30 if ( !class_exists( 'WyPiekacz' ) || ( defined( 'WP_DEBUG') && WP_DEBUG ) ) { 
     30if ( !class_exists( 'WyPiekacz' ) || ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) { 
     31 
     32/*// Include compatibility file if WP version is not current (WP 3.2.x) 
     33if ( version_compare( $wp_version, '3.2', '<' ) ) { 
     34    include( dirname( __FILE__ ) . '/compat.php' ); 
     35}*/ 
    3136 
    3237class WyPiekacz { 
     
    3742    // Link counter - used by RX for removing links 
    3843    var $link_counter = 0; 
     44    // Number of initial links to remove - used by RX for removing links 
     45    var $links_to_remove = 0; 
    3946    // List of supported post types 
    4047    var $post_types = array(); 
     48    // Flag if we are deleting orphaned posts, to avoid infinite recursion 
     49    var $deleting_orphaned_posts = false; 
    4150     
    4251    // WP versions 
     52    var $has_wp_28 = false; 
     53    var $has_wp_29 = false; 
    4354    var $has_wp_30 = false; 
     55     
     56    // True if User Locker 1.2+ is active 
     57    var $has_user_locker = false; 
    4458     
    4559    // Constructor 
    4660    function WyPiekacz() { 
    4761        global $wp_version; 
    48         $this->has_wp_30 = version_compare( $wp_version, '2.999', '>' ); 
     62        $this->has_wp_28 = version_compare( $wp_version, '2.7.999', '>' ); 
     63        $this->has_wp_29 = version_compare( $wp_version, '2.8.999', '>' ); 
     64        $this->has_wp_30 = version_compare( $wp_version, '2.9.999', '>' ); 
    4965         
    5066        // Initialise plugin 
     
    85101            // Default post template handling 
    86102            add_action( 'submitpost_box', array( &$this, 'submitpost_box' ) ); 
     103             
     104            // Unload autosave script if needed 
     105            if ( get_option( 'wypiekacz_autosave_interval' ) == 0 ) { 
     106                add_action( 'wp_print_scripts', array( &$this, 'wp_print_scripts' ) ); 
     107            } 
    87108        } 
    88109    } 
     
    91112    function init() { 
    92113        load_plugin_textdomain( 'wypiekacz', false, dirname( plugin_basename( __FILE__ ) ).'/lang' ); 
     114         
     115        // Check if User Locker 1.2+ is active 
     116        if ( function_exists( 'user_locker_lock_user' ) ) { 
     117            $this->has_user_locker = true; 
     118             
     119            // Register hooks for filters and actions which depends on User Locker 
     120            add_action( 'user_locker_unlock_user', array( &$this, 'user_locker_unlock_user' ) ); 
     121            add_action( 'user_locker_enable_user', array( &$this, 'user_locker_unlock_user' ) ); 
     122             
     123            if ( is_admin() ) { 
     124                // Add new column to the user list 
     125                add_filter( 'manage_users_columns', array( &$this, 'manage_users_columns' ) ); 
     126                add_filter( 'manage_users_custom_column', array( &$this, 'manage_users_custom_column' ), 10, 3 ); 
     127            } 
     128        } 
    93129    } 
    94130     
     
    123159        register_setting( 'wypiekacz', 'wypiekacz_min_len', array( &$this, 'sanitize_nonnegative' ) ); 
    124160        register_setting( 'wypiekacz', 'wypiekacz_min_len_words', array( &$this, 'sanitize_nonnegative' ) ); 
     161        register_setting( 'wypiekacz', 'wypiekacz_min_links', array( &$this, 'sanitize_nonnegative' ) ); 
    125162        register_setting( 'wypiekacz', 'wypiekacz_max_links', array( &$this, 'sanitize_nonnegative' ) ); 
     163        register_setting( 'wypiekacz', 'wypiekacz_link_after', array( &$this, 'sanitize_nonnegative' ) ); 
     164        register_setting( 'wypiekacz', 'wypiekacz_link_after_words', array( &$this, 'sanitize_nonnegative' ) ); 
    126165        register_setting( 'wypiekacz', 'wypiekacz_min_title_len', array( &$this, 'sanitize_nonnegative' ) ); 
    127166        register_setting( 'wypiekacz', 'wypiekacz_min_title_len_words', array( &$this, 'sanitize_nonnegative' ) ); 
     
    139178        register_setting( 'wypiekacz', 'wypiekacz_pass_reset_email', array( &$this, 'sanitize_01' ) ); 
    140179        register_setting( 'wypiekacz', 'wypiekacz_right_now_stats', array( &$this, 'sanitize_01' ) ); 
     180        register_setting( 'wypiekacz', 'wypiekacz_post_menu_links', array( &$this, 'sanitize_01' ) ); 
    141181        register_setting( 'wypiekacz', 'wypiekacz_badwords', array( &$this, 'sanitize_stringlist' ) ); 
    142182        register_setting( 'wypiekacz', 'wypiekacz_check_badwords_title', array( &$this, 'sanitize_01' ) ); 
    143183        register_setting( 'wypiekacz', 'wypiekacz_check_badwords_content', array( &$this, 'sanitize_01' ) ); 
     184        register_setting( 'wypiekacz', 'wypiekacz_check_badwords_tags', array( &$this, 'sanitize_01' ) ); 
    144185        register_setting( 'wypiekacz', 'wypiekacz_goodwords', array( &$this, 'sanitize_stringlist' ) ); 
     186        register_setting( 'wypiekacz', 'wypiekacz_post_thumbnail', array( &$this, 'sanitize_01' ) ); 
    145187        register_setting( 'wypiekacz', 'wypiekacz_enforce_links', array( &$this, 'sanitize_01' ) ); 
     188        register_setting( 'wypiekacz', 'wypiekacz_enforce_link_positions', array( &$this, 'sanitize_01' ) ); 
    146189        register_setting( 'wypiekacz', 'wypiekacz_enforce_title', array( &$this, 'sanitize_01' ) ); 
    147190        register_setting( 'wypiekacz', 'wypiekacz_enforce_add_dots', array( &$this, 'sanitize_01' ) ); 
     
    150193        register_setting( 'wypiekacz', 'wypiekacz_allow_skip_rules', array( &$this, 'sanitize_01' ) ); 
    151194        register_setting( 'wypiekacz', 'wypiekacz_post_types', array( &$this, 'sanitize_post_types' ) ); 
     195        register_setting( 'wypiekacz', 'wypiekacz_dont_save_invalid_post', array( &$this, 'sanitize_01' ) ); 
     196        register_setting( 'wypiekacz', 'wypiekacz_autosave_interval', array( &$this, 'sanitize_nonnegative' ) ); 
     197        register_setting( 'wypiekacz', 'wypiekacz_post_revisions', array( &$this, 'sanitize_nonnegative_or_minus1' ) ); 
     198        register_setting( 'wypiekacz', 'wypiekacz_empty_trash_days', array( &$this, 'sanitize_nonnegative' ) ); 
     199        register_setting( 'wypiekacz', 'wypiekacz_delete_orphaned_drafts', array( &$this, 'sanitize_nonnegative' ) ); 
     200        register_setting( 'wypiekacz', 'wypiekacz_force_delete_orphaned_drafts', array( &$this, 'sanitize_01' ) ); 
     201         
     202        // Do not register these options if User Locker is not active - otherwise WP would clear them when options are saved 
     203        if ( $this->has_user_locker ) { 
     204            register_setting( 'wypiekacz', 'wypiekacz_lock_account', array( &$this, 'sanitize_01' ) ); 
     205            register_setting( 'wypiekacz', 'wypiekacz_lock_account_after', array( &$this, 'sanitize_positive' ) ); 
     206            register_setting( 'wypiekacz', 'wypiekacz_lock_method', array( &$this, 'sanitize_01' ) ); 
     207            register_setting( 'wypiekacz', 'wypiekacz_lock_reason', 'trim' ); 
     208            register_setting( 'wypiekacz', 'wypiekacz_lock_show_details', array( &$this, 'sanitize_01' ) ); 
     209        } 
    152210    } 
    153211     
     
    163221            'WyPiekacz', 'manage_options', __FILE__, array( &$this, 'options_panel' ) ); 
    164222         
     223        // Add links to Posts menu 
     224        if ( get_option( 'wypiekacz_post_menu_links' ) ) { 
     225            $can_edit = current_user_can( 'edit_posts' ); 
     226            $can_publish = current_user_can('publish_posts'); 
     227            if ( $can_edit || $can_publish ) { 
     228                $num_posts = wp_count_posts( 'post' ); 
     229                 
     230                $drafts = $num_posts->draft; 
     231                add_submenu_page( 'edit.php', __('Drafts', 'wypiekacz'),  
     232                    sprintf( __('Drafts %s', 'wypiekacz'), "<span class='awaiting-mod count-$drafts'><span class='pending-count'>" .  
     233                        number_format_i18n( $drafts ) . "</span></span>" ), 
     234                    'edit_posts', 'edit.php?post_status=draft' ); 
     235                 
     236                $pending = $num_posts->pending; 
     237                add_submenu_page( 'edit.php', __('Pending', 'wypiekacz'),  
     238                    sprintf( __('Pending %s', 'wypiekacz'), "<span class='awaiting-mod count-$pending'><span class='pending-count'>" .  
     239                        number_format_i18n( $pending ) . "</span></span>" ), 
     240                    'edit_posts', 'edit.php?post_status=pending' ); 
     241            } 
     242        } 
     243         
    165244        // Add metabox to edit post page 
    166245        foreach ( $this->post_types as $post_type ) { 
     
    181260        global $post; 
    182261        $meta = ''; 
    183         if ( $this->post_page && is_object( $post) ) { 
     262        if ( $this->post_page && is_object( $post ) ) { 
    184263            $meta = get_post_meta($post->ID, 'WyPiekacz_msg', true); 
    185264        } 
     
    187266            // Display error message 
    188267            echo '<div id="notice" class="error"><p>', $meta,  
    189                 '<br />', __('Post Status has been changed to Draft.', 'wypiekacz'), '</p></div>', "\n"; 
     268                '<br />', __('Post Status has been changed to Draft.', 'wypiekacz'); 
     269            if ( isset( $_GET['message'] ) && ( $_GET['message'] == '85614' ) ) { 
     270                echo '<br />', __('Post was *NOT* saved.', 'wypiekacz'); 
     271            } 
     272            echo '</p></div>', "\n"; 
     273             
    190274            // Remove this message 
    191275            delete_post_meta( $post->ID, 'WyPiekacz_msg' ); 
    192             // Change WP message to 'Post saved' 
    193             if ( isset( $_GET['message'] ) ) { 
    194                 if ( '6' == $_GET['message'] ) { 
    195                     $_GET['message'] = '7'; 
     276             
     277            // redirect_post_location filter is supported starting from WP2.9 
     278            if ( !$this->has_wp_29 ) { // 2.8 and below 
     279                // Change WP message to 'Post saved' 
     280                if ( isset( $_GET['message'] ) ) { 
     281                    if ( '6' == $_GET['message'] ) { 
     282                        $_GET['message'] = '7'; 
     283                    } elseif ( '85614' == $_GET['message'] ) { 
     284                        unset( $_GET['message'] ); 
     285                    } 
    196286                } 
    197287            } 
     
    203293        if ( count( $this->errors ) > 0 ) { 
    204294            $location = remove_query_arg( 'message', $location ); 
    205             $location = add_query_arg( 'message', '10', $location ); 
     295            // When invalid post was not saved, WyPiekacz will display appropriate message 
     296            if ( get_option( 'wypiekacz_dont_save_invalid_post' ) ) { 
     297                $location = add_query_arg( 'message', '85614', $location ); 
     298            } else { 
     299                $location = add_query_arg( 'message', '10', $location ); 
     300            } 
    206301        } 
    207302        return $location; 
     
    210305    // Check submitted post data 
    211306    function wp_insert_post( $data, $post_arr ) { 
     307        // Skip post revisions and auto-drafts 
     308        if ( ( $data['post_type'] == 'revision' ) || ( $data['post_status'] == 'auto-draft' ) ) { 
     309            return $data; 
     310        } 
     311 
     312        // TODO: although it is possible to stop creation of auto drafts from here by breaking the query, 
     313        // it does not work as expected - there are many PHP warnings in debug mode, and finally 
     314        // "You are not allowed to edit this post." error on next post save attempt. 
     315        // WP Core must be fixed first in order to make this work. 
     316         
     317         
     318        // Delete orphaned post drafts 
     319        if ( $this->deleting_orphaned_posts ) { // Avoid infinite recursion 
     320            return $data; 
     321        } else { 
     322            $this->deleting_orphaned_posts = true; 
     323            $this->delete_orphaned_drafts(); 
     324            $this->deleting_orphaned_posts = false; 
     325        } 
     326         
    212327        if ( 
    213             // Check only for posts 
     328            // Check selected post types only 
    214329            in_array( $data['post_type'], $this->post_types ) 
    215             // Check only if status is Published or Pending Review 
     330            // Check only if status is Published or Pending Review or Future 
    216331            && ( in_array( $data['post_status'], array( 'publish', 'pending', 'future' ) ) ) 
    217332            // Editors (and above) can have limits too 
     
    240355             
    241356            if ( !$skip_check ) { 
     357                // Enforce some rules before checking them 
    242358                $data = $this->enforce_rules( $data ); 
    243359                 
     360                // Check rules 
    244361                $result = $this->check_precel_post( $data['post_content'], $data['post_title'], $post_arr ); 
    245362                if ( true !== $result ) { 
     
    259376                        return new WP_Error( 'edit_refused', implode( '; ', $errors ) ); 
    260377                    }*/ 
     378                     
     379                    // Do not save invalid post if user asked for this. So far the only way is to break SQL query, so register new filter to do this. 
     380                    if ( get_option( 'wypiekacz_dont_save_invalid_post' ) ) { 
     381                        // save_post hook will not be called later, so need to perform some extra steps here 
     382                         
     383                        // Save errors to post meta 
     384                        if ( isset( $post_arr['ID'] ) ) { 
     385                            delete_post_meta( $post_arr['ID'], 'WyPiekacz_msg' ); 
     386                            add_post_meta( $post_arr['ID'], 'WyPiekacz_msg', $this->pack_errors( '<br />' ), true ); 
     387                        } 
     388                         
     389                        // Lock user account if needed 
     390                        $this->lock_user_account( isset( $post_arr['ID'] ) ? $post_arr['ID'] : 0, false ); 
     391                         
     392                        // Do not want to execute any extra SQL queries - just proceeded to INSERT/UPDATE query for current post 
     393                        remove_all_actions( 'pre_post_update' ); 
     394                         
     395                        // Now we can add the filter 
     396                        add_filter( 'query', array( &$this, 'kill_sql_query' ) ); 
     397                         
     398                        // Do not execute any extra code beyond this point - return data only 
     399                        return $data; 
     400                    } 
    261401                } 
    262402            } 
     
    264404         
    265405        return $data; 
     406    } 
     407     
     408    // Replace INSERT/UPDATE query with some junk. This filter is used to prevent creating/updating invalid post 
     409    function kill_sql_query( $query ) { 
     410        if ( preg_match( '/^\s*(insert|update:?)\s/i', $query ) ) { 
     411            return 'xxx'; 
     412        } else { 
     413            return $query; 
     414        } 
     415    } 
     416     
     417    // Delete orphaned post drafts 
     418    function delete_orphaned_drafts() { 
     419        $interval = get_option( 'wypiekacz_delete_orphaned_drafts', 0 ); 
     420        if ( $interval == 0 ) { // Feature disabled 
     421            return; 
     422        } 
     423        $force = get_option( 'wypiekacz_force_delete_orphaned_drafts' ) ? true : false; 
     424         
     425        global $wpdb; 
     426        $orphaned_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'draft' AND DATE_SUB( NOW(), INTERVAL $interval DAY ) > post_date" ); 
     427        foreach ( (array) $orphaned_posts as $delete ) { 
     428            wp_delete_post( $delete, $force ); 
     429        } 
     430    } 
     431     
     432    // Lock/disable user account if needed 
     433    function lock_user_account( $post_id, $check_result ) { 
     434        if ( !$this->has_user_locker || !get_option( 'wypiekacz_lock_account' ) ) { 
     435            return; 
     436        } 
     437         
     438        if ( $post_id <= 0 ) { // Make sure post_id == -1 if it is unknown (new post) 
     439            $post_id = -1; 
     440        } 
     441         
     442        $current_user = wp_get_current_user(); 
     443        $user_id = $current_user->ID; 
     444         
     445        $last_post_id   = get_user_option( '_wypiekacz_last_post', $user_id, false ); 
     446        $bad_post_count = get_user_option( '_wypiekacz_bad_posts', $user_id, false ); 
     447        if ( empty( $last_post_id ) ) { 
     448            $last_post_id = 0; 
     449        } 
     450        if ( empty( $bad_post_count ) ) { 
     451            $bad_post_count = 0; 
     452        } 
     453         
     454        if ( $check_result ) { // Rule check succeeded 
     455            $bad_post_count = 0; // Reset bad post count 
     456        } else { // Rule check failed 
     457            // Following cases are not checked here (do nothing for them): 
     458            // id > 0, last_id == -1 : most probably last failed publish attempt was for the same post (new post), so do nothing 
     459            // id == last_id > 0 : another failed publish attempt for the same post 
     460             
     461            if ( $post_id < 0 ) { // Post ID is not known (new post) 
     462                ++$bad_post_count; 
     463            } elseif ( $last_post_id == 0 ) { // No user meta yet, treat this as a new post 
     464                ++$bad_post_count; 
     465            } elseif ( ( $last_post_id > 0 ) && ( $last_post_id != $post_id ) ) { // Post ID has changed 
     466                ++$bad_post_count; 
     467            } 
     468        } 
     469         
     470        update_user_option( $user_id, '_wypiekacz_last_post', $post_id, false ); 
     471        update_user_option( $user_id, '_wypiekacz_bad_posts', $bad_post_count, false ); 
     472         
     473        $max_count = get_option( 'wypiekacz_lock_account_after' ); 
     474        if ( $bad_post_count > $max_count ) { 
     475            $reason = get_option( 'wypiekacz_lock_reason' ); 
     476            if ( get_option( 'wypiekacz_lock_method' ) == 0 ) { 
     477                user_locker_lock_user( $user_id, $reason ); 
     478            } else { 
     479                user_locker_disable_user( $user_id, $reason ); 
     480            } 
     481             
     482            // Force logout 
     483            wp_logout(); 
     484        } 
     485    } 
     486     
     487    // User Locker plugin unlocks/enable user account 
     488    function user_locker_unlock_user( $user_id ) { 
     489        // Clear our data 
     490        update_user_option( $user_id, '_wypiekacz_last_post', 0, false ); 
     491        update_user_option( $user_id, '_wypiekacz_bad_posts', 0, false ); 
    266492    } 
    267493     
    268494    // Called after post is saved - save error messages too 
    269495    function save_post( $post_ID ) { 
     496        $post = get_post( $post_ID ); 
     497         
     498        // Skip post revisions and auto-drafts 
     499        if ( ( $post->post_type == 'revision' ) || ( $post->post_status == 'auto-draft' ) ) { 
     500            return; 
     501        } 
     502         
    270503        // Check if 'Skip rule check' option was checked 
    271         // verify this came from the our screen and with proper authorisation, 
     504        // verify this came from the our screen and with proper authorization, 
    272505        // because save_post can be triggered at other times 
    273506        if ( get_option( 'wypiekacz_allow_skip_rules' ) && isset( $_POST['wypiekacz_nonce'] ) &&  
     
    287520        if ( count( $this->errors ) > 0 ) { 
    288521            delete_post_meta( $post_ID, 'WyPiekacz_msg' ); 
    289             add_post_meta( $post_ID, 'WyPiekacz_msg', implode( '<br />', $this->errors ), true ); 
     522            add_post_meta( $post_ID, 'WyPiekacz_msg', $this->pack_errors( '<br />' ), true ); 
     523             
     524            // Lock user account if needed 
     525            $this->lock_user_account( $post_ID, false ); 
     526        } 
     527         
     528        // Lock/unlock user account if needed 
     529        $autosave = defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE; 
     530        if ( !$autosave ) { 
     531            $this->lock_user_account( $post_ID, count( $this->errors ) == 0 ); 
    290532        } 
    291533    } 
     
    310552                    _e('OK', 'wypiekacz'); 
    311553                } else { 
    312                     echo '<span style="color:red">', implode( '<br />', $this->errors ), "</span>\n"; 
     554                    echo '<span style="color:red">', $this->pack_errors( '<br />' ), "</span>\n"; 
    313555                    $this->errors = array(); 
    314556                } 
     
    319561    // Callback - remove extra links 
    320562    function rx_remove_links( $matches ) { 
     563        // Remove links placed too early first 
     564        if ( $this->links_to_remove > 0 ) { 
     565            --$this->links_to_remove; 
     566            return $matches[2]; 
     567        } 
     568         
     569        // Remove links above limit 
    321570        ++$this->link_counter; 
    322571        if ( $this->link_counter > get_option( 'wypiekacz_max_links' ) ) { 
     
    348597    // Enforce rules for posts 
    349598    function enforce_rules( $data ) { 
    350         // Enforce max link count 
    351         if ( get_option( 'wypiekacz_enforce_links' ) ) { 
     599        // Find how many initial links should be removed 
     600        $this->links_to_remove = 0; 
     601        if ( get_option( 'wypiekacz_enforce_link_positions' ) ) { 
     602            $first_link_after_chars = get_option( 'wypiekacz_link_after' ); 
     603            $first_link_after_words = get_option( 'wypiekacz_link_after_words' ); 
     604             
     605            $cnt = preg_match_all( '/<\s*a\s/i', $data['post_content'], $matches, PREG_OFFSET_CAPTURE ); 
     606            for ( $n = 0; $n < $cnt; ++$n ) { 
     607                // $matches[0][N][1] contains match offsets for links 
     608                $link_pos = $matches[0][$n][1]; 
     609                $text_before_link = substr( $data['post_content'], 0, $link_pos ); 
     610                 
     611                // Check position of link (in characters) 
     612                $text2 = preg_replace( '/\s\s+/', ' ', ltrim( wp_strip_all_tags( $text_before_link ) ) ); 
     613                $len = strlen( $text2 ); 
     614                if ( $len < $first_link_after_chars ) { 
     615                    // Need to remove this link 
     616                    ++$this->links_to_remove; 
     617                    continue; 
     618                } 
     619                 
     620                // Check position of link (in words) 
     621                $count = $this->count_words( $text_before_link ); 
     622                if ( $count < $first_link_after_words ) { 
     623                    // Need to remove this link 
     624                    ++$this->links_to_remove; 
     625                    continue; 
     626                } 
     627                 
     628                // Link is in correct place - next links will be too, so exit loop 
     629                break; 
     630            } 
     631        } 
     632         
     633        // Enforce max link count and link positions 
     634        if ( ( $this->links_to_remove > 0 ) || get_option( 'wypiekacz_enforce_links' ) ) { 
    352635            $this->link_counter = 0; 
    353636            $data['post_content'] = preg_replace_callback( '#(<a [^>]*href\s*=\s*[^>]+[^>]*>)(.*?)(</a>)#',  
     
    409692        /*if ( get_option( 'wypiekacz_enforce_tags' ) ) { 
    410693        }*/ 
     694         
     695        // Allow other plugins to enforce additional rules 
     696        $data = apply_filters( 'wypiekacz_enforce_rules', $data ); 
    411697         
    412698        return $data; 
     
    604890        // Check length (in characters) 
    605891        $min_len = get_option( 'wypiekacz_min_len' ); 
    606         $text2 = preg_replace( '/\s\s+/', ' ', trim( strip_tags( $text ) ) ); 
     892        $text2 = preg_replace( '/\s\s+/', ' ', trim( wp_strip_all_tags( $text ) ) ); 
    607893        $len = strlen( $text2 ); 
    608894        if ( $len < $min_len ) { 
    609             $this->errors[] = sprintf( __('Post is too short (minimum is %1$s chars, your post has %2$s).', 'wypiekacz'), 
    610                 $min_len, $len ); 
     895            $this->errors[] = array( 'min_len_chars', sprintf( __('Post is too short (minimum is %1$s chars, your post has %2$s).', 'wypiekacz'), 
     896                $min_len, $len ) ); 
    611897        } 
    612898         
     
    615901        $count = $this->count_words( $text ); 
    616902        if ( $count < $min_len ) { 
    617             $this->errors[] = sprintf( __('Post is too short (minimum is %1$s words, your post has %2$s).', 'wypiekacz'), 
    618                 $min_len, $count ); 
     903            $this->errors[] = array( 'min_len_words', sprintf( __('Post is too short (minimum is %1$s words, your post has %2$s).', 'wypiekacz'), 
     904                $min_len, $count ) ); 
    619905        } 
    620906         
    621907        // Check links 
     908        $min_links = get_option( 'wypiekacz_min_links' ); 
    622909        $max_links = get_option( 'wypiekacz_max_links' ); 
    623         $cnt = preg_match_all( '/<\s*a\s/i', $text, $matches ); 
    624         if ( $cnt > $max_links ) { 
    625             $this->errors[] = sprintf( __('Post contains too many links (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
    626                 $max_links, $cnt ); 
     910        $cnt = preg_match_all( '/<\s*a\s/i', $text, $matches, PREG_OFFSET_CAPTURE ); 
     911        if ( $cnt < $min_links ) { 
     912            $this->errors[] = array( 'min_links', sprintf( __('Post contains too few links (minimum is %1$s, your post has %2$s).', 'wypiekacz'), 
     913                $max_links, $cnt ) ); 
     914        } elseif ( $cnt > $max_links ) { 
     915            $this->errors[] = array( 'max_links', sprintf( __('Post contains too many links (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
     916                $max_links, $cnt ) ); 
     917        } 
     918         
     919        if ( $cnt > 0 ) { 
     920            // $matches[0][N][1] contains match offsets for links 
     921            $link_pos = $matches[0][0][1]; 
     922            $text_before_link = substr( $text, 0, $link_pos ); 
     923             
     924            // Check position of first link (in characters) 
     925            $first_link_after = get_option( 'wypiekacz_link_after' ); 
     926            $text2 = preg_replace( '/\s\s+/', ' ', ltrim( wp_strip_all_tags( $text_before_link ) ) ); 
     927            $len = strlen( $text2 ); 
     928            if ( $len < $first_link_after ) { 
     929                $this->errors[] = array( 'link_after_chars', sprintf( __('First link is too close to the beginning (minimum is after %1$s chars, your link is after %2$s).', 'wypiekacz'), 
     930                    $first_link_after, $len ) ); 
     931            } 
     932             
     933            // Check position of first link (in words) 
     934            $first_link_after = get_option( 'wypiekacz_link_after_words' ); 
     935            $count = $this->count_words( $text_before_link ); 
     936            if ( $count < $first_link_after ) { 
     937                $this->errors[] = array( 'link_after_words', sprintf( __('First link is too close to the beginning (minimum is after %1$s words, your link is after %2$s).', 'wypiekacz'), 
     938                    $first_link_after, $count ) ); 
     939            } 
    627940        } 
    628941         
     
    633946        $len = strlen( $text2 ); 
    634947        if ( $len < $min_len ) { 
    635             $this->errors[] = sprintf( __('Post Title is too short (minimum is %1$s chars, your Title has %2$s).', 'wypiekacz'), 
    636                 $min_len, $len ); 
     948            $this->errors[] = array( 'min_title_len_chars', sprintf( __('Post Title is too short (minimum is %1$s chars, your Title has %2$s).', 'wypiekacz'), 
     949                $min_len, $len ) ); 
    637950        } 
    638951        elseif ( ( $max_len > 0 ) && ( $len > $max_len ) ) { 
    639             $this->errors[] = sprintf( __('Post Title is too long (maximum is %1$s chars, your Title has %2$s).', 'wypiekacz'), 
    640                 $max_len, $len ); 
     952            $this->errors[] = array( 'max_title_len_chars', sprintf( __('Post Title is too long (maximum is %1$s chars, your Title has %2$s).', 'wypiekacz'), 
     953                $max_len, $len ) ); 
    641954        } 
    642955         
     
    646959        $count = $this->count_words( $title ); 
    647960        if ( $count < $min_len ) { 
    648             $this->errors[] = sprintf( __('Post Title is too short (minimum is %1$s words, your Title has %2$s).', 'wypiekacz'), 
    649                 $min_len, $count ); 
     961            $this->errors[] = array( 'min_title_len_words', sprintf( __('Post Title is too short (minimum is %1$s words, your Title has %2$s).', 'wypiekacz'), 
     962                $min_len, $count ) ); 
    650963        } 
    651964        elseif ( ( $max_len > 0 ) && ( $count > $max_len ) ) { 
    652             $this->errors[] = sprintf( __('Post Title is too long (maximum is %1$s words, your Title has %2$s).', 'wypiekacz'), 
    653                 $max_len, $count ); 
     965            $this->errors[] = array( 'max_title_len_words', sprintf( __('Post Title is too long (maximum is %1$s words, your Title has %2$s).', 'wypiekacz'), 
     966                $max_len, $count ) ); 
    654967        } 
    655968         
     
    6871000        $use_default_cat = get_option( 'wypiekacz_use_def_cat' ); 
    6881001        if ( !$use_default_cat && $has_default_cat ) { 
    689             $this->errors[] = sprintf( __('Cannot add posts to the default category (%s).', 'wypiekacz'),  
    690                 get_cat_name( $default_cat ) ); 
     1002            $this->errors[] = array( 'no_def_cat', sprintf( __('Cannot add posts to the default category (%s).', 'wypiekacz'),  
     1003                get_cat_name( $default_cat ) ) ); 
    6911004        } 
    6921005         
     
    7001013        $max_cats = get_option( 'wypiekacz_max_cats' ); 
    7011014        if ( $post_cat_cnt < $min_cats ) { 
    702             $this->errors[] = sprintf( __('Too few categories selected (minimum is %1$s, your post has %2$s).', 'wypiekacz'), 
    703                 $min_cats, $post_cat_cnt ); 
     1015            $this->errors[] = array( 'min_cats', sprintf( __('Too few categories selected (minimum is %1$s, your post has %2$s).', 'wypiekacz'), 
     1016                $min_cats, $post_cat_cnt ) ); 
    7041017        } else if ( $post_cat_cnt > $max_cats ) { 
    705             $this->errors[] = sprintf( __('Too many categories selected (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
    706                 $max_cats, $post_cat_cnt ); 
     1018            $this->errors[] = array( 'max_cats', sprintf( __('Too many categories selected (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
     1019                $max_cats, $post_cat_cnt ) ); 
    7071020        } 
    7081021         
    7091022        if ( is_array( $post_data ) ) { 
    710             //var_dump($post_data);die; 
    7111023            $post_tag_cnt = 0; 
    7121024            $tags = array(); 
     
    7501062         
    7511063        if ( $post_tag_cnt < $min_tags ) { 
    752             $this->errors[] = sprintf( __('Too few tags (minimum is %1$s, your post has %2$s).', 'wypiekacz'), 
    753                 $min_tags, $post_tag_cnt ); 
     1064            $this->errors[] = array( 'min_tags', sprintf( __('Too few tags (minimum is %1$s, your post has %2$s).', 'wypiekacz'), 
     1065                $min_tags, $post_tag_cnt ) ); 
    7541066        } else if ($post_tag_cnt > $max_tags) { 
    755             $this->errors[] = sprintf( __('Too many tags (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
    756                 $max_tags, $post_tag_cnt ); 
     1067            $this->errors[] = array( 'max_tags', sprintf( __('Too many tags (maximum is %1$s, your post has %2$s).', 'wypiekacz'), 
     1068                $max_tags, $post_tag_cnt ) ); 
    7571069        } 
    7581070         
     
    7611073            $words_found = $this->check_badwords( $title ); 
    7621074            if ( count( $words_found ) > 0 ) { 
    763                 $this->errors[] = sprintf( __('Forbidden word(s) in title: %s', 'wypiekacz'), 
    764                     implode( ', ', $words_found ) ); 
     1075                $this->errors[] = array( 'badwords_title', sprintf( __('Forbidden word(s) in title: %s', 'wypiekacz'), 
     1076                    implode( ', ', $words_found ) ) ); 
    7651077            } 
    7661078        } 
     
    7701082            $words_found = $this->check_badwords( $text ); 
    7711083            if ( count( $words_found ) > 0 ) { 
    772                 $this->errors[] = sprintf( __('Forbidden word(s) in content: %s', 'wypiekacz'), 
    773                     implode( ', ', $words_found ) ); 
    774             } 
    775         } 
     1084                $this->errors[] = array( 'badwords_content', sprintf( __('Forbidden word(s) in content: %s', 'wypiekacz'), 
     1085                    implode( ', ', $words_found ) ) ); 
     1086            } 
     1087        } 
     1088         
     1089        // Check forbidden words in tags 
     1090        if ( get_option( 'wypiekacz_check_badwords_tags' ) ) { 
     1091            // Convert objects to string if needed 
     1092            if ( ( count( $tags ) > 0 ) && is_object( $tags[0] ) ) { 
     1093                $tags_to_check = array(); 
     1094                foreach ( $tags as $tag ) { 
     1095                    $tags_to_check[] = $tag->name; 
     1096                } 
     1097            } else { 
     1098                $tags_to_check = $tags; 
     1099            } 
     1100             
     1101            $words_found = $this->check_badwords( implode( ',', $tags_to_check ) ); 
     1102            if ( count( $words_found ) > 0 ) { 
     1103                $this->errors[] = array( 'badwords_tags', sprintf( __('Forbidden word(s) in tags: %s', 'wypiekacz'), 
     1104                    implode( ', ', $words_found ) ) ); 
     1105            } 
     1106        } 
     1107         
     1108        // Check post thumbnail 
     1109        if ( get_option( 'wypiekacz_post_thumbnail' ) ) { 
     1110            $post_id = 0; 
     1111            if ( is_array( $post_data ) ) { 
     1112                // Get ID from POST data 
     1113                if ( isset( $post_data['ID'] ) ) { 
     1114                    $post_id = (int)$post_data['ID']; 
     1115                } 
     1116            } else { 
     1117                // Get ID from Post object 
     1118                $post_id = $post_data->ID; 
     1119            } 
     1120             
     1121            $has_thumbnail = false; 
     1122            if ( ( $post_id > 0 ) && function_exists( 'has_post_thumbnail' ) ) { 
     1123                $has_thumbnail = has_post_thumbnail( $post_id ); 
     1124            } 
     1125             
     1126            $has_thumbnail = apply_filters( 'wypiekacz_check_thumbnail', $has_thumbnail, $post_id, $post_data ); 
     1127             
     1128            if ( !$has_thumbnail ) { 
     1129                $this->errors[] = array( 'post_thumbnail', __('Post thumbnail (Featured image) is required.', 'wypiekacz') ); 
     1130            } 
     1131        } 
     1132         
     1133        // Allow other plugins to check additional rules 
     1134        $this->errors = apply_filters( 'wypiekacz_check_post', $this->errors, $text, $title, $post_data ); 
    7761135         
    7771136        return count( $this->errors ) == 0; 
     
    8511210    } 
    8521211     
     1212    // Convert error array to displayable form 
     1213    function pack_errors( $separator ) { 
     1214        $ret = ''; 
     1215        $first = true; 
     1216        foreach ( $this->errors as $error ) { 
     1217            if ( $first ) { 
     1218                $first = false; 
     1219            } else { 
     1220                $ret .= $separator; 
     1221            } 
     1222            $ret .= $error[1]; 
     1223        } 
     1224        return $ret; 
     1225    } 
     1226     
     1227    // Unload autosave script if needed 
     1228    function wp_print_scripts() { 
     1229        wp_deregister_script( 'autosave' ); 
     1230    } 
     1231     
     1232    // Add new column to the user list page 
     1233    function manage_users_columns( $columns ) { 
     1234        // This requires WP 2.8+ 
     1235        global $wp_version; 
     1236        if ( $this->has_wp_28 && $this->has_user_locker && get_option( 'wypiekacz_lock_show_details' ) ) { 
     1237            $columns['wypiekacz'] = 'WyPiekacz'; 
     1238        } 
     1239        return $columns; 
     1240    } 
     1241     
     1242    // Add column content for each user on user list 
     1243    function manage_users_custom_column( $value, $column_name, $user_id ) { 
     1244        if ( $column_name == 'wypiekacz' ) { 
     1245            $last_post_id   = get_user_option( '_wypiekacz_last_post', $user_id, false ); 
     1246            $bad_post_count = get_user_option( '_wypiekacz_bad_posts', $user_id, false ); 
     1247            if ( empty( $last_post_id ) ) { 
     1248                $last_post_id = 0; 
     1249            } 
     1250            if ( empty( $bad_post_count ) ) { 
     1251                $bad_post_count = 0; 
     1252            } 
     1253             
     1254            $value = sprintf( __('Bad Posts: %1$s, Last Post ID: %2$s', 'wypiekacz'), $bad_post_count, $last_post_id ); 
     1255        } 
     1256         
     1257        return $value; 
     1258    } 
     1259     
    8531260    // Handle options panel 
    8541261    function options_panel() { 
     
    8691276</th> 
    8701277<td> 
    871 <input type="text" maxlength="6" size="10" id="wypiekacz_min_len" name="wypiekacz_min_len" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set minimum word count to zero.', 'wypiekacz'); ?> 
     1278<input type="text" maxlength="6" size="10" id="wypiekacz_min_len" name="wypiekacz_min_len" value="<?php echo esc_attr( get_option( 'wypiekacz_min_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set minimum word count to zero.', 'wypiekacz'); ?> 
    8721279</td> 
    8731280</tr> 
     
    8781285</th> 
    8791286<td> 
    880 <input type="text" maxlength="6" size="10" id="wypiekacz_min_len_words" name="wypiekacz_min_len_words" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set minimum character count to zero.', 'wypiekacz'); ?> 
     1287<input type="text" maxlength="6" size="10" id="wypiekacz_min_len_words" name="wypiekacz_min_len_words" value="<?php echo esc_attr( get_option( 'wypiekacz_min_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set minimum character count to zero.', 'wypiekacz'); ?> 
     1288</td> 
     1289</tr> 
     1290 
     1291<tr> 
     1292<th scope="row" style="text-align:right; vertical-align:top;"> 
     1293<label for="wypiekacz_min_links"><?php _e('Minimum link count in post:', 'wypiekacz'); ?></label> 
     1294</th> 
     1295<td> 
     1296<input type="text" maxlength="4" size="10" id="wypiekacz_min_links" name="wypiekacz_min_links" value="<?php echo esc_attr( get_option( 'wypiekacz_min_links' ) ); ?>" /> 
    8811297</td> 
    8821298</tr> 
     
    8871303</th> 
    8881304<td> 
    889 <input type="text" maxlength="2" size="10" id="wypiekacz_max_links" name="wypiekacz_max_links" value="<?php echo stripcslashes( get_option( 'wypiekacz_max_links' ) ); ?>" /> 
     1305<input type="text" maxlength="4" size="10" id="wypiekacz_max_links" name="wypiekacz_max_links" value="<?php echo esc_attr( get_option( 'wypiekacz_max_links' ) ); ?>" /> 
     1306</td> 
     1307</tr> 
     1308 
     1309<tr> 
     1310<th scope="row" style="text-align:right; vertical-align:top;"> 
     1311<label for="wypiekacz_link_after"><?php _e('First link is allowed after N initial characters:', 'wypiekacz'); ?></label> 
     1312</th> 
     1313<td> 
     1314<input type="text" maxlength="6" size="10" id="wypiekacz_link_after" name="wypiekacz_link_after" value="<?php echo esc_attr( get_option( 'wypiekacz_link_after' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set minimum word count to zero.', 'wypiekacz'); ?> 
     1315</td> 
     1316</tr> 
     1317 
     1318<tr> 
     1319<th scope="row" style="text-align:right; vertical-align:top;"> 
     1320<label for="wypiekacz_link_after_words"><?php _e('First link is allowed after N initial words:', 'wypiekacz'); ?></label> 
     1321</th> 
     1322<td> 
     1323<input type="text" maxlength="6" size="10" id="wypiekacz_link_after_words" name="wypiekacz_link_after_words" value="<?php echo esc_attr( get_option( 'wypiekacz_link_after_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set minimum character count to zero.', 'wypiekacz'); ?> 
     1324</td> 
     1325</tr> 
     1326 
     1327<tr> 
     1328<th scope="row" style="text-align:right; vertical-align:top;"> 
     1329<label for="wypiekacz_post_thumbnail"><?php _e('Post thumbnail (Featured image) is required:', 'wypiekacz'); ?></label> 
     1330</th> 
     1331<td> 
     1332<input type="checkbox" id="wypiekacz_post_thumbnail" name="wypiekacz_post_thumbnail" value="yes" <?php checked( 1, get_option( 'wypiekacz_post_thumbnail' ) ); ?> /> 
     1333<br /><?php _e('Note: WyPiekacz supports WordPress Featured image by default. You can also provide your own function to check if Post thumbnail is present - see FAQ for more details.', 'wypiekacz'); ?> 
    8901334</td> 
    8911335</tr> 
     
    8981342</th> 
    8991343<td> 
    900 <input type="text" maxlength="3" size="10" id="wypiekacz_min_title_len" name="wypiekacz_min_title_len" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_title_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set minimum word count to zero.', 'wypiekacz'); ?> 
     1344<input type="text" maxlength="3" size="10" id="wypiekacz_min_title_len" name="wypiekacz_min_title_len" value="<?php echo esc_attr( get_option( 'wypiekacz_min_title_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set minimum word count to zero.', 'wypiekacz'); ?> 
    9011345</td> 
    9021346</tr> 
     
    9071351</th> 
    9081352<td> 
    909 <input type="text" maxlength="3" size="10" id="wypiekacz_min_title_len_words" name="wypiekacz_min_title_len_words" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_title_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set minimum character count to zero.', 'wypiekacz'); ?> 
     1353<input type="text" maxlength="3" size="10" id="wypiekacz_min_title_len_words" name="wypiekacz_min_title_len_words" value="<?php echo esc_attr( get_option( 'wypiekacz_min_title_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set minimum character count to zero.', 'wypiekacz'); ?> 
    9101354</td> 
    9111355</tr> 
     
    9161360</th> 
    9171361<td> 
    918 <input type="text" maxlength="3" size="10" id="wypiekacz_max_title_len" name="wypiekacz_max_title_len" value="<?php echo stripcslashes( get_option( 'wypiekacz_max_title_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set maximum word count to zero.', 'wypiekacz'); ?> 
     1362<input type="text" maxlength="3" size="10" id="wypiekacz_max_title_len" name="wypiekacz_max_title_len" value="<?php echo esc_attr( get_option( 'wypiekacz_max_title_len' ) ); ?>" /><br /><?php _e('Note: if you want to check character count only, set maximum word count to zero.', 'wypiekacz'); ?> 
    9191363</td> 
    9201364</tr> 
     
    9251369</th> 
    9261370<td> 
    927 <input type="text" maxlength="3" size="10" id="wypiekacz_max_title_len_words" name="wypiekacz_max_title_len_words" value="<?php echo stripcslashes( get_option( 'wypiekacz_max_title_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set maximum character count to zero.', 'wypiekacz'); ?> 
     1371<input type="text" maxlength="3" size="10" id="wypiekacz_max_title_len_words" name="wypiekacz_max_title_len_words" value="<?php echo esc_attr( get_option( 'wypiekacz_max_title_len_words' ) ); ?>" /><br /><?php _e('Note: if you want to check word count only, set maximum character count to zero.', 'wypiekacz'); ?> 
    9281372</td> 
    9291373</tr> 
     
    9451389</th> 
    9461390<td> 
    947 <input type="text" maxlength="3" size="10" id="wypiekacz_min_cats" name="wypiekacz_min_cats" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_cats' ) ); ?>" /> 
     1391<input type="text" maxlength="3" size="10" id="wypiekacz_min_cats" name="wypiekacz_min_cats" value="<?php echo esc_attr( get_option( 'wypiekacz_min_cats' ) ); ?>" /> 
    9481392</td> 
    9491393</tr> 
     
    9541398</th> 
    9551399<td> 
    956 <input type="text" maxlength="3" size="10" id="wypiekacz_max_cats" name="wypiekacz_max_cats" value="<?php echo stripcslashes( get_option( 'wypiekacz_max_cats' ) ); ?>" /> 
     1400<input type="text" maxlength="3" size="10" id="wypiekacz_max_cats" name="wypiekacz_max_cats" value="<?php echo esc_attr( get_option( 'wypiekacz_max_cats' ) ); ?>" /> 
    9571401</td> 
    9581402</tr> 
     
    9651409</th> 
    9661410<td> 
    967 <input type="text" maxlength="3" size="10" id="wypiekacz_min_tags" name="wypiekacz_min_tags" value="<?php echo stripcslashes( get_option( 'wypiekacz_min_tags' ) ); ?>" /> 
     1411<input type="text" maxlength="3" size="10" id="wypiekacz_min_tags" name="wypiekacz_min_tags" value="<?php echo esc_attr( get_option( 'wypiekacz_min_tags' ) ); ?>" /> 
    9681412</td> 
    9691413</tr> 
     
    9741418</th> 
    9751419<td> 
    976 <input type="text" maxlength="3" size="10" id="wypiekacz_max_tags" name="wypiekacz_max_tags" value="<?php echo stripcslashes( get_option( 'wypiekacz_max_tags' ) ); ?>" /> 
     1420<input type="text" maxlength="3" size="10" id="wypiekacz_max_tags" name="wypiekacz_max_tags" value="<?php echo esc_attr( get_option( 'wypiekacz_max_tags' ) ); ?>" /> 
    9771421</td> 
    9781422</tr> 
     
    10001444<tr> 
    10011445<th scope="row" style="text-align:right; vertical-align:top;"> 
     1446<label for="wypiekacz_check_badwords_tags"><?php _e('Check for forbidden words in tags:', 'wypiekacz'); ?></label> 
     1447</th> 
     1448<td> 
     1449<input type="checkbox" id="wypiekacz_check_badwords_tags" name="wypiekacz_check_badwords_tags" value="yes" <?php checked( 1, get_option( 'wypiekacz_check_badwords_tags' ) ); ?> /> 
     1450</td> 
     1451</tr> 
     1452 
     1453<tr> 
     1454<th scope="row" style="text-align:right; vertical-align:top;"> 
    10021455<label for="wypiekacz_badwords"><?php _e('Forbidden words list:', 'wypiekacz'); ?></label> 
    10031456</th> 
    10041457<td> 
    1005 <textarea id="wypiekacz_badwords" name="wypiekacz_badwords" rows="5" cols="30"><?php echo htmlspecialchars( implode(  
    1006 "\n", get_option( 'wypiekacz_badwords', array() ) ) ); ?></textarea><br /> 
     1458<textarea id="wypiekacz_badwords" name="wypiekacz_badwords" rows="5" cols="30"><?php echo esc_html( implode( "\n", get_option( 'wypiekacz_badwords', array() ) ) ); ?></textarea><br /> 
    10071459<?php _e('Put one word per line', 'wypiekacz'); ?> 
    10081460</td> 
     
    10141466</th> 
    10151467<td> 
    1016 <textarea id="wypiekacz_goodwords" name="wypiekacz_goodwords" rows="5" cols="30"><?php echo htmlspecialchars( implode(  
    1017 "\n", get_option( 'wypiekacz_goodwords', array() ) ) ); ?></textarea><br /> 
     1468<textarea id="wypiekacz_goodwords" name="wypiekacz_goodwords" rows="5" cols="30"><?php echo esc_html( implode( "\n", get_option( 'wypiekacz_goodwords', array() ) ) ); ?></textarea><br /> 
    10181469<?php _e('Put one word per line', 'wypiekacz'); ?><br /><?php _e('When WyPiekacz will found any word from Forbidden Word List in text, it will check if it part of any word from Allowed Words List (e.g. <b>fly</b> - forbidden, <b>butterfly</b> - allowed)', 'wypiekacz'); ?> 
    10191470</td> 
    10201471</tr> 
    10211472 
     1473<tr><th colspan="2"><h3><?php _e('Post Thumbnail:', 'wypiekacz'); ?></h3></th></tr> 
     1474 
     1475<tr> 
     1476<th scope="row" style="text-align:right; vertical-align:top;"> 
     1477<label for="wypiekacz_post_thumbnail"><?php _e('Post thumbnail (Featured image) is required:', 'wypiekacz'); ?></label> 
     1478</th> 
     1479<td> 
     1480<input type="checkbox" id="wypiekacz_post_thumbnail" name="wypiekacz_post_thumbnail" value="yes" <?php checked( 1, get_option( 'wypiekacz_post_thumbnail' ) ); ?> /> 
     1481<br /><?php _e('Note: WyPiekacz supports WordPress Featured image by default. You can also provide your own function to check if Post thumbnail is present - see FAQ for more details.', 'wypiekacz'); ?> 
     1482</td> 
     1483</tr> 
     1484 
    10221485<tr><th colspan="2"><h3><?php _e('Rule enforcement:', 'wypiekacz'); ?></h3></th></tr> 
    10231486 
     
    10331496<tr> 
    10341497<th scope="row" style="text-align:right; vertical-align:top;"> 
     1498<label for="wypiekacz_enforce_link_positions"><?php _e('Enforce link positions:', 'wypiekacz'); ?></label> 
     1499</th> 
     1500<td> 
     1501<input type="checkbox" id="wypiekacz_enforce_link_positions" name="wypiekacz_enforce_link_positions" value="yes" <?php checked( 1, get_option( 'wypiekacz_enforce_link_positions' ) ); ?> /><br /><?php _e('Links inserted too close to the beginning of post content will be automatically removed', 'wypiekacz'); ?> 
     1502</td> 
     1503</tr> 
     1504 
     1505<tr> 
     1506<th scope="row" style="text-align:right; vertical-align:top;"> 
    10351507<label for="wypiekacz_enforce_title"><?php _e('Enforce max title length:', 'wypiekacz'); ?></label> 
    10361508</th> 
     
    10671539</tr> 
    10681540 
     1541<tr><th colspan="2"><h3><?php _e('Invalid Posts:', 'wypiekacz'); ?></h3></th></tr> 
     1542 
     1543<tr> 
     1544<th scope="row" style="text-align:right; vertical-align:top;"> 
     1545<label for="wypiekacz_dont_save_invalid_post"><?php _e('Do not save posts which do not satisfy all rules:', 'wypiekacz'); ?></label> 
     1546</th> 
     1547<td> 
     1548<input type="checkbox" id="wypiekacz_dont_save_invalid_post" name="wypiekacz_dont_save_invalid_post" value="yes" <?php checked( 1, get_option( 'wypiekacz_dont_save_invalid_post' ) ); ?> /> 
     1549<br /><?php _e('When this option is enabled, posts submitted for review or publishing will not be saved if they do not satisfy all rules. This can negatively affect user experience, so enable it if you have to deal with lots of automated spam only.', 'wypiekacz'); ?> 
     1550<br /><?php _e('Note: this option does not prevent creation of auto drafts - so far there are too many dependencies in WordPress code to make it work correctly. Autosave and normal post saving as draft (without attempt to publish or send it for review) will work too.', 'wypiekacz'); ?> 
     1551</td> 
     1552</tr> 
     1553 
     1554<tr> 
     1555<th scope="row" style="text-align:right; vertical-align:top;"> 
     1556<label for="wypiekacz_delete_orphaned_drafts"><?php _e('Delete orphaned post drafts interval (days):', 'wypiekacz'); ?></label> 
     1557</th> 
     1558<td> 
     1559<input type="text" maxlength="4" size="10" id="wypiekacz_delete_orphaned_drafts" name="wypiekacz_delete_orphaned_drafts" value="<?php echo esc_attr( get_option( 'wypiekacz_delete_orphaned_drafts' ) ); ?>" /><br /><?php _e('Default is 0 days - disabled. When Trash is enabled, drafts will be moved to Trash.', 'wypiekacz'); ?> 
     1560</td> 
     1561</tr> 
     1562 
     1563<tr> 
     1564<th scope="row" style="text-align:right; vertical-align:top;"> 
     1565<label for="wypiekacz_force_delete_orphaned_drafts"><?php _e('Force deletion of orphaned post drafts:', 'wypiekacz'); ?></label> 
     1566</th> 
     1567<td> 
     1568<input type="checkbox" id="wypiekacz_force_delete_orphaned_drafts" name="wypiekacz_force_delete_orphaned_drafts" value="yes" <?php checked( 1, get_option( 'wypiekacz_force_delete_orphaned_drafts' ) ); ?> /><br /><?php _e('Do not move orphaned post drafts to Trash - delete them immediately.', 'wypiekacz'); ?> 
     1569</td> 
     1570</tr> 
     1571 
     1572<tr><th colspan="2"><h3><?php _e('Build-in WordPress functionalities:', 'wypiekacz'); ?></h3></th></tr> 
     1573 
     1574<tr> 
     1575<th scope="row" style="text-align:right; vertical-align:top;"> 
     1576<label for="wypiekacz_autosave_interval"><?php _e('Post autosave interval (seconds):', 'wypiekacz'); ?></label> 
     1577</th> 
     1578<td> 
     1579<input type="text" maxlength="4" size="10" id="wypiekacz_autosave_interval" name="wypiekacz_autosave_interval" value="<?php echo esc_attr( get_option( 'wypiekacz_autosave_interval' ) ); ?>" /><br /><?php _e('Default is 60 seconds. Enter 0 to disable autosave.', 'wypiekacz'); ?> 
     1580</td> 
     1581</tr> 
     1582 
     1583<tr> 
     1584<th scope="row" style="text-align:right; vertical-align:top;"> 
     1585<label for="wypiekacz_post_revisions"><?php _e('Maximum post revisions count:', 'wypiekacz'); ?></label> 
     1586</th> 
     1587<td> 
     1588<input type="text" maxlength="4" size="10" id="wypiekacz_post_revisions" name="wypiekacz_post_revisions" value="<?php echo esc_attr( get_option( 'wypiekacz_post_revisions' ) ); ?>" /><br /><?php _e('Default is -1 (no limit). Enter 0 to disable post revisions.', 'wypiekacz'); ?> 
     1589</td> 
     1590</tr> 
     1591 
     1592<tr> 
     1593<th scope="row" style="text-align:right; vertical-align:top;"> 
     1594<label for="wypiekacz_empty_trash_days"><?php _e('Empty trash interval (days):', 'wypiekacz'); ?></label> 
     1595</th> 
     1596<td> 
     1597<input type="text" maxlength="4" size="10" id="wypiekacz_empty_trash_days" name="wypiekacz_empty_trash_days" value="<?php echo esc_attr( get_option( 'wypiekacz_empty_trash_days' ) ); ?>" /><br /><?php _e('Default is 30 days. Enter 0 to disable Trash.', 'wypiekacz'); ?> 
     1598</td> 
     1599</tr> 
     1600 
    10691601<tr><th colspan="2"><h3><?php _e('Special:', 'wypiekacz'); ?></h3></th></tr> 
    10701602 
     
    10931625<td> 
    10941626<input type="checkbox" id="wypiekacz_right_now_stats" name="wypiekacz_right_now_stats" value="yes" <?php checked( 1, get_option( 'wypiekacz_right_now_stats' ) ); ?> /> 
     1627</td> 
     1628</tr> 
     1629 
     1630<tr> 
     1631<th scope="row" style="text-align:right; vertical-align:top;"> 
     1632<label for="wypiekacz_post_menu_links"><?php _e('Add links to Draft and Pending Posts lists to Posts menu:', 'wypiekacz'); ?></label> 
     1633</th> 
     1634<td> 
     1635<input type="checkbox" id="wypiekacz_post_menu_links" name="wypiekacz_post_menu_links" value="yes" <?php checked( 1, get_option( 'wypiekacz_post_menu_links' ) ); ?> /> 
    10951636</td> 
    10961637</tr> 
     
    11101651    foreach ( $post_types as $post_type => $post_type_label ) { 
    11111652?> 
    1112 <label><input type="checkbox" id="wypiekacz_post_types_<?php print $post_type; ?>" name="wypiekacz_post_types[]" value="<?php print $post_type; ?>" <?php checked( true, in_array( $post_type, $selected_post_types ) ); ?> /> <?php print $post_type_label; ?></label><br /> 
     1653<label><input type="checkbox" id="wypiekacz_post_types_<?php echo esc_attr( $post_type ); ?>" name="wypiekacz_post_types[]" value="<?php echo esc_attr( $post_type );  ?>" <?php checked( true, in_array( $post_type, $selected_post_types ) ); ?> /> <?php echo esc_html( $post_type_label ); ?></label><br /> 
    11131654<?php 
    11141655    } 
     
    11261667</th> 
    11271668<td> 
    1128 <input type="text" size="59" id="wypiekacz_def_title" name="wypiekacz_def_title" value="<?php echo stripcslashes( get_option( 'wypiekacz_def_title' ) ); ?>" /> 
     1669<input type="text" size="59" id="wypiekacz_def_title" name="wypiekacz_def_title" value="<?php echo esc_attr( get_option( 'wypiekacz_def_title' ) ); ?>" /> 
    11291670</td> 
    11301671</tr> 
     
    11351676</th> 
    11361677<td> 
    1137 <textarea rows="5" cols="57" id="wypiekacz_def_text" name="wypiekacz_def_text"><?php echo stripcslashes( get_option( 'wypiekacz_def_text' ) ); ?></textarea> 
     1678<textarea rows="5" cols="57" id="wypiekacz_def_text" name="wypiekacz_def_text"><?php echo esc_textarea( get_option( 'wypiekacz_def_text' ) ); ?></textarea> 
    11381679</td> 
    11391680</tr> 
     
    11581699</td> 
    11591700</tr> 
     1701 
     1702<tr><th colspan="2"><h3><?php _e('Account locking:', 'wypiekacz'); ?></h3></th></tr> 
     1703 
     1704<tr> 
     1705<th scope="row" style="text-align:right; vertical-align:top;"> 
     1706&nbsp; 
     1707</th> 
     1708<td> 
     1709<?php printf( __('Current WyPiekacz version is integrated with the <a href="%s" target="_blank">User Locker</a> plugin (version 1.2 or newer). It can lock or disable user account when he/she will send too many invalid posts for review or attempt to publish it and abandon them. Multiple failed attempts for the same post in a row are counted only once. Every successful submission resets the counter. Users will be able to unlock locked account by requesting new password or asking admin for help; disabled accounts can be enabled by admin only.', 'wypiekacz'), 'http://wordpress.org/extend/plugins/user-locker/' ); ?> 
     1710</td> 
     1711</tr> 
     1712 
     1713<tr> 
     1714<th scope="row" style="text-align:right; vertical-align:top;"> 
     1715&nbsp; 
     1716</th> 
     1717<td> 
     1718<?php  
     1719if ( $this->has_user_locker ) { 
     1720    _e('User Locker 1.2+ is installed and active.', 'wypiekacz'); 
     1721} else { 
     1722    _e('User Locker 1.2+ is not active. You need to install and activate it first.', 'wypiekacz'); 
     1723} 
     1724?> 
     1725</td> 
     1726</tr> 
     1727 
     1728<?php if ( $this->has_user_locker ): ?> 
     1729 
     1730<tr> 
     1731<th scope="row" style="text-align:right; vertical-align:top;"> 
     1732<label for="wypiekacz_lock_account"><?php _e('Enable account locking/disabling:', 'wypiekacz'); ?></label> 
     1733</th> 
     1734<td> 
     1735<input type="checkbox" id="wypiekacz_lock_account" name="wypiekacz_lock_account" value="yes" <?php checked( 1, get_option( 'wypiekacz_lock_account' ) ); ?> /> 
     1736</td> 
     1737</tr> 
     1738 
     1739<tr> 
     1740<th scope="row" style="text-align:right; vertical-align:top;"> 
     1741<label for="wypiekacz_lock_account_after"><?php _e('Maximum allowed number of abandoned invalid posts:', 'wypiekacz'); ?></label> 
     1742</th> 
     1743<td> 
     1744<input type="text" maxlength="4" size="10" id="wypiekacz_lock_account_after" name="wypiekacz_lock_account_after" value="<?php echo esc_attr( get_option( 'wypiekacz_lock_account_after' ) ); ?>" /><br /><?php _e('Multiple failed attempts for the same post in a row are counted only once. Every successful submission resets the counter.', 'wypiekacz'); ?> 
     1745</td> 
     1746</tr> 
     1747 
     1748<tr> 
     1749<th scope="row" style="text-align:right; vertical-align:top;"> 
     1750<label for="wypiekacz_lock_method"><?php _e('Account lock method:', 'wypiekacz'); ?></label> 
     1751</th> 
     1752<td> 
     1753<?php $wypiekacz_lock_method = get_option( 'wypiekacz_lock_method' ); ?> 
     1754<select id="wypiekacz_lock_method" name="wypiekacz_lock_method"> 
     1755<option value="0" <?php selected( $wypiekacz_lock_method, 0 ); ?>><?php _e('Lock', 'wypiekacz'); ?></option> 
     1756<option value="1" <?php selected( $wypiekacz_lock_method, 1 ); ?>><?php _e('Disable', 'wypiekacz'); ?></option> 
     1757</select><br /><?php _e('Users will be able to unlock locked account by requesting new password or asking admin for help. Disabled accounts can be enabled by admin only.', 'wypiekacz'); ?> 
     1758</td> 
     1759</tr> 
     1760 
     1761<tr> 
     1762<th scope="row" style="text-align:right; vertical-align:top;"> 
     1763<label for="wypiekacz_lock_reason"><?php _e('Lock/Disable reason:', 'wypiekacz'); ?></label> 
     1764</th> 
     1765<td> 
     1766<input type="text" maxlength="500" size="80" id="wypiekacz_lock_reason" name="wypiekacz_lock_reason" value="<?php echo esc_attr( get_option( 'wypiekacz_lock_reason' ) ); ?>" /> 
     1767<br /><?php _e('Reason text can be displayed after unsuccessful login attempt, and on User List. Make sure you enabled appropriate options in User Locker settings.', 'wypiekacz'); ?> 
     1768<br /><?php _e('Note: start text with \'@\' (AT sign) to keep it private.', 'wypiekacz'); ?> 
     1769</td> 
     1770</tr> 
     1771 
     1772<tr> 
     1773<th scope="row" style="text-align:right; vertical-align:top;"> 
     1774<label for="wypiekacz_lock_show_details"><?php _e('Show details on User List:', 'wypiekacz'); ?></label> 
     1775</th> 
     1776<td> 
     1777<input type="checkbox" id="wypiekacz_lock_show_details" name="wypiekacz_lock_show_details" value="yes" <?php checked( 1, get_option( 'wypiekacz_lock_show_details' ) ); ?> /> 
     1778<br /><?php _e('Enable this option to add extra column to User List with Bad Posts count and last Post ID.', 'wypiekacz'); ?> 
     1779</td> 
     1780</tr> 
     1781 
     1782<?php endif; /* if ( $this->has_user_locker ): */ ?> 
    11601783 
    11611784</table> 
     
    11931816    } 
    11941817     
     1818    function sanitize_nonnegative_or_minus1( $value ) { 
     1819        $value = (int)$value; 
     1820        if ( $value < -1 ) { 
     1821            $value = -1; 
     1822        } 
     1823        return $value; 
     1824    } 
     1825     
     1826    function sanitize_positive( $value ) { 
     1827        $value = (int)$value; 
     1828        if ( $value <= 0 ) { 
     1829            $value = 1; 
     1830        } 
     1831        return $value; 
     1832    } 
     1833     
    11951834    function sanitize_stringlist( $value ) { 
    11961835        $value = explode( "\n", (string)$value ); 
     
    12321871    // Sanitize list of post types 
    12331872    function sanitize_post_types( $types ) { 
     1873        // For pre-WP 3.0 return predefined value - otherwise WP will save empty array when 
     1874        // options are updated, causing mysterious problem after upgrade to WP 3.0+. 
     1875        if ( !$this->has_wp_30 ) { 
     1876            return array( 'post' ); 
     1877        } 
     1878         
    12341879        $post_types = $this->get_post_types(); 
    12351880        $ret = array(); 
     
    12461891add_option( 'wypiekacz_min_len', 1000 ); // Minimum post length (characters) 
    12471892add_option( 'wypiekacz_min_len_words', 0 ); // Minimum post length (words) 
     1893add_option( 'wypiekacz_min_links', 0 ); // Minimum links per post 
    12481894add_option( 'wypiekacz_max_links', 3 ); // Maximum links per post 
     1895add_option( 'wypiekacz_link_after', 0 ); // First link allowed after N characters 
     1896add_option( 'wypiekacz_link_after_words', 0 ); // First link allowed after N words 
    12491897add_option( 'wypiekacz_min_title_len', 5 ); // Minimum title length (characters) 
    12501898add_option( 'wypiekacz_min_title_len_words', 0 ); // Minimum title length (words) 
     
    12621910add_option( 'wypiekacz_pass_reset_email', 1 ); // Send notification of password resets to admin 
    12631911add_option( 'wypiekacz_right_now_stats', 1 ); // Show number of Drafts and Pending Posts in Dashboard 
     1912add_option( 'wypiekacz_post_menu_links', 1 ); // Add Drafts/Pending links to Posts menu 
    12641913add_option( 'wypiekacz_badwords', array() ); // List of forbidden words 
    12651914add_option( 'wypiekacz_check_badwords_title', 0 ); // Check for forbidden words in title 
    12661915add_option( 'wypiekacz_check_badwords_content', 0 ); // Check for forbidden words in content 
     1916add_option( 'wypiekacz_check_badwords_tags', 0 ); // Check for forbidden words in tags 
    12671917add_option( 'wypiekacz_goodwords', array() ); // List of allowed words 
     1918add_option( 'wypiekacz_post_thumbnail', 0 ); // Check post thumbnail 
    12681919add_option( 'wypiekacz_enforce_links', 0 ); // Enforce max link count 
     1920add_option( 'wypiekacz_enforce_link_positions', 0 ); // Enforce link positions 
    12691921add_option( 'wypiekacz_enforce_title', 0 ); // Enforce max title length 
    12701922add_option( 'wypiekacz_enforce_add_dots', 1 ); // Enforce max title length 
     
    12731925add_option( 'wypiekacz_allow_skip_rules', 1 ); // Allow Editors and Administrators to skip rule check 
    12741926add_option( 'wypiekacz_post_types', array( 'post' ) ); // List of post types supported by default 
     1927add_option( 'wypiekacz_dont_save_invalid_post', 0 ); // Save post even it has not passed validation 
     1928add_option( 'wypiekacz_autosave_interval', 60 ); // Post autosave interval (60 = default, 0 to disable (need special handling)) 
     1929add_option( 'wypiekacz_post_revisions', -1 ); // Post revisions count (-1/true = default, 0/false to disable) 
     1930add_option( 'wypiekacz_empty_trash_days', 30 ); // Empty trash days (30 = default, 0 to disable) 
     1931add_option( 'wypiekacz_delete_orphaned_drafts', 0 ); // Delete orphaned_drafts interval (days, 0 = disable) 
     1932add_option( 'wypiekacz_force_delete_orphaned_drafts', 0 ); // Force delete orphaned_drafts (otherwise they may end in Trash) 
     1933add_option( 'wypiekacz_lock_account', 0 ); // Lock/disable user account when user submits too many invalid posts for review/publish and do not correct them 
     1934add_option( 'wypiekacz_lock_account_after', 5 ); // How many invalid posts can be submitted before locking user 
     1935add_option( 'wypiekacz_lock_method', 0 ); // 0 - lock account, 1 - disable account 
     1936add_option( 'wypiekacz_lock_reason', '' ); // Lock reason 
     1937add_option( 'wypiekacz_lock_show_details', 1 ); // Add extra column to User List with details 
    12751938 
    12761939$wp_wypiekacz = new WyPiekacz(); 
     1940// TODO: try to call this from 'init' hook (at this point user is authenticated), and do not enforce rules if checking is skipped for user or by option 
    12771941$wp_wypiekacz->enforce_rules_POST(); 
    12781942 
     
    13091973} 
    13101974 
     1975// Define some special defines 
     1976if ( !defined( 'AUTOSAVE_INTERVAL' ) ) { // 60 = default 
     1977    $val = get_option( 'wypiekacz_autosave_interval' ); 
     1978    if ( $val == 0 ) { 
     1979        // Use some big value to make sure autosave will not start if autosave script will be loaded (should not be) 
     1980        $val = 999999999; 
     1981    } 
     1982    define( 'AUTOSAVE_INTERVAL', $val ); 
     1983} 
     1984 
     1985if ( !defined( 'WP_POST_REVISIONS' ) ) { // -1 = default 
     1986    $val = get_option( 'wypiekacz_post_revisions' ); 
     1987    define( 'WP_POST_REVISIONS', $val ); 
     1988} 
     1989 
     1990if ( !defined( 'EMPTY_TRASH_DAYS' ) ) { // 30 = default 
     1991    $val = get_option( 'wypiekacz_empty_trash_days' ); 
     1992    define( 'EMPTY_TRASH_DAYS', $val ); 
     1993} 
     1994 
     1995// Add functions from WP2.8 for previous WP versions 
     1996if ( !function_exists( 'esc_html' ) ) { 
     1997    function esc_html( $text ) { 
     1998        return wp_specialchars( $text ); 
     1999    } 
     2000} 
     2001if ( !function_exists( 'esc_attr' ) ) { 
     2002    function esc_attr( $text ) { 
     2003        return attribute_escape( $text ); 
     2004    } 
     2005} 
     2006 
     2007// Add functions from WP2.9 for previous WP versions 
     2008if ( !function_exists( 'wp_strip_all_tags' ) ) { 
     2009    function wp_strip_all_tags($string, $remove_breaks = false) { 
     2010        $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string ); 
     2011        $string = strip_tags($string); 
     2012     
     2013        if ( $remove_breaks ) 
     2014            $string = preg_replace('/[\r\n\t ]+/', ' ', $string); 
     2015     
     2016        return trim($string); 
     2017    } 
     2018} 
     2019 
     2020// Add functions from WP3.1 for previous WP versions 
     2021if ( !function_exists( 'esc_textarea' ) ) { 
     2022    function esc_textarea( $text ) { 
     2023        $safe_text = htmlspecialchars( $text, ENT_QUOTES ); 
     2024        return apply_filters( 'esc_textarea', $safe_text, $text ); 
     2025    } 
     2026} 
     2027 
    13112028} // END 
    13122029 
Note: See TracChangeset for help on using the changeset viewer.