WordPress.org

Plugin Directory

Changeset 613295


Ignore:
Timestamp:
10/16/12 17:20:43 (18 months ago)
Author:
tmoorewp
Message:

Update the grunion branch with the new version from WordPress.com.

Location:
jetpack/branches/grunion/modules/contact-form
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • jetpack/branches/grunion/modules/contact-form/admin.php

    r580939 r613295  
    11<?php 
     2 
     3function menu_alter() { 
     4    echo ' 
     5    <style> 
     6    #menu-posts-feedback .wp-menu-image img { display: none; } 
     7    #adminmenu .menu-icon-feedback:hover div.wp-menu-image, #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, #adminmenu .menu-icon-feedback.current div.wp-menu-image { background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover.png") no-repeat 7px 7px !important; } 
     8    #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu.png") no-repeat 7px 7px !important; } 
     9    .grunion-menu-button { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form.png") no-repeat; width: 13px; height: 12px; display: inline-block; ) } 
     10    @media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) { 
     11        #adminmenu .menu-icon-feedback:hover div.wp-menu-image, #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, #adminmenu .menu-icon-feedback.current div.wp-menu-image { background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover-2x.png") no-repeat 7px 7px !important; background-size: 15px 16px !important; } 
     12        #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu-2x.png") no-repeat 7px 7px !important; background-size: 15px 16px !important; } 
     13        .grunion-menu-button { background-image: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form-2x.png"); background-size: 13px 12px !important; vertical-align: bottom; } 
     14    } 
     15    </style>'; 
     16} 
     17 
     18add_action('admin_head', 'menu_alter'); 
     19 
     20/** 
     21 * Add a contact form button to the post composition screen 
     22 */ 
     23add_action( 'media_buttons', 'grunion_media_button', 999 ); 
     24function grunion_media_button( ) { 
     25    global $post_ID, $temp_ID; 
     26    $iframe_post_id = (int) (0 == $post_ID ? $temp_ID : $post_ID); 
     27    $title = esc_attr( __( 'Add a custom form', 'jetpack' ) ); 
     28    $plugin_url = esc_url( GRUNION_PLUGIN_URL ); 
     29    $site_url = esc_url( admin_url( "/admin-ajax.php?post_id={$iframe_post_id}&action=grunion_form_builder&TB_iframe=true&width=768" ) ); 
     30 
     31    echo '<a href="' . $site_url . '&id=add_form" class="thickbox" title="' . $title . '"><div class="grunion-menu-button" alt="' . $title . '"></div></a>'; 
     32} 
     33 
     34add_action( 'wp_ajax_grunion_form_builder', 'display_form_view' ); 
     35 
     36function display_form_view() { 
     37    require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php'; 
     38    exit; 
     39} 
    240 
    341// feedback specific css items 
     
    4179} 
    4280 
    43 #icon-edit { background-position: -432px -5px; } 
    44  
    45 #icon-edit, #icon-post { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big.png") no-repeat !important; } 
     81#icon-edit.icon32-posts-feedback, #icon-post.icon32-posts-feedback { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big.png") no-repeat !important; } 
    4682@media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) { 
    47     #icon-edit, #icon-post { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big-2x.png") no-repeat !important; background-size: 30px 31px !important; } 
    48 } 
     83    #icon-edit.icon32-posts-feedback, #icon-post.icon32-posts-feedback { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big-2x.png") no-repeat !important; background-size: 30px 31px !important; } 
     84} 
     85 
     86#icon-edit.icon32-posts-feedback { background-position: 2px 2px !important; } 
     87 
    4988</style> 
    5089 
     
    321360function grunion_ajax_shortcode() { 
    322361    check_ajax_referer( 'grunion_shortcode' ); 
    323      
    324     $atts = ''; 
    325     if ( trim( $_POST['subject'] ) ) 
    326         $atts .= ' subject="'.grunion_esc_attr($_POST['subject']).'"'; 
    327     if ( trim( $_POST['to'] ) ) 
    328         $atts .= ' to="'.grunion_esc_attr($_POST['to']).'"'; 
    329          
    330     $shortcode = '[contact-form'.$atts.']'; 
    331     $shortcode .= "\n"; 
     362 
     363    $attributes = array(); 
     364 
     365    foreach ( array( 'subject', 'to' ) as $attribute ) { 
     366        if ( isset( $_POST[$attribute] ) && strlen( $_POST[$attribute] ) ) { 
     367            $attributes[$attribute] = stripslashes( $_POST[$attribute] ); 
     368        } 
     369    } 
     370 
    332371    if ( is_array( $_POST['fields'] ) ) { 
    333         usort( $_POST['fields'], 'grunion_sort_objects' ); 
    334         foreach ( $_POST['fields'] as $field ) { 
    335             $req = $opts = ''; 
    336             if ( $field['required'] == 'true' ) 
    337                 $req = ' required="true"'; 
    338             if ( isset( $field['options'] ) && $field['options'] ) { 
    339                 $opts = ' options="'; 
    340                 foreach ( $field['options'] as $option ) { 
    341                     $option = wp_kses( $option, array() ); 
    342                     $option = grunion_esc_attr( $option ); 
    343  
    344                     # we need to be very specific about how we 
    345                     # encode these values 
    346                     $option = str_replace( ',', '&#x002c;', $option ); 
    347                     $option = str_replace( '"', '&#x0022;', $option ); 
    348                     $option = str_replace( "'", '&#x0027;', $option ); 
    349                     $option = str_replace( '&', '&#x0026;', $option ); 
    350  
    351                     $opts .= $option . ','; 
     372        $fields = stripslashes_deep( $_POST['fields'] ); 
     373        usort( $fields, 'grunion_sort_objects' ); 
     374 
     375        $field_shortcodes = array(); 
     376 
     377        foreach ( $fields as $field ) { 
     378            $field_attributes = array(); 
     379 
     380            if ( isset( $field['required'] ) && 'true' === $field['required'] ) { 
     381                $field_attributes['required'] = 'true'; 
     382            } 
     383 
     384            foreach ( array( 'options', 'label', 'type' ) as $attribute ) { 
     385                if ( isset( $field[$attribute] ) ) { 
     386                    $field_attributes[$attribute] = $field[$attribute]; 
    352387                } 
    353                 $opts = rtrim( $opts, ',' ) . '"'; 
    354             } 
    355  
    356             $field['label'] = wp_kses( $field['label'], array() ); 
    357             $field['label'] = str_replace( '"', '&#x0022;', $field['label'] ); 
    358  
    359             $shortcode .= '[contact-field label="'. $field['label'] .'" type="'.grunion_esc_attr($field['type']).'"' . $req . $opts .' /]'."\n"; 
     388            } 
     389 
     390            $field_shortcodes[] = new Grunion_Contact_Form_Field( $field_attributes ); 
    360391        } 
    361392    } 
    362     $shortcode .= '[/contact-form]'; 
    363      
    364     die( "\n$shortcode\n" ); 
     393 
     394    $grunion = new Grunion_Contact_Form( $attributes, $field_shortcodes ); 
     395 
     396    die( "\n$grunion\n" ); 
    365397} 
    366398 
     
    371403     
    372404    check_ajax_referer( 'grunion_shortcode_to_json' ); 
    373     if ( isset( $_POST['content'] ) && is_numeric( $_POST['post_id'] ) ) { 
    374         $content = stripslashes( $_POST['content'] ); 
    375         $post = get_post( $_POST['post_id'] ); 
    376         // does it look like a post with a [contact-form] already? 
    377         if ( strpos( $content, '[contact-form' ) !== false ) { 
    378             $out = do_shortcode($content); 
    379             global $contact_form_fields; 
    380             if ( is_array($contact_form_fields) && !empty($contact_form_fields) ) { 
    381                 foreach ( $contact_form_fields as $field_id => $field ) { 
    382                     # need to dig deeper on select field options 
    383                     if ( preg_match( "|^(.*)\-select$|", $field_id ) ) { 
    384                         foreach ( (array) $field['options'] as $opt_i => $opt ) { 
    385                             $contact_form_fields[$field_id]['options'][$opt_i] = html_entity_decode( $opt ); 
    386                         } 
    387                     } 
    388                     $contact_form_fields[$field_id]['label'] = html_entity_decode( $contact_form_fields[$field_id]['label'] ); 
    389                     $contact_form_fields[$field_id]['label'] = wp_kses( $contact_form_fields[$field_id]['label'], array() ); 
    390                 } 
    391  
    392                 $out = array( 'fields' => $contact_form_fields, 'to' => $grunion_form->to, 'subject' => $grunion_form->subject ); 
    393                 die( json_encode( $out ) ); 
    394             } 
     405 
     406    if ( !isset( $_POST['content'] ) || !is_numeric( $_POST['post_id'] ) ) { 
     407        die( '-1' ); 
     408    } 
     409 
     410    $content = stripslashes( $_POST['content'] ); 
     411 
     412    // doesn't look like a post with a [contact-form] already. 
     413    if ( false === strpos( $content, '[contact-form' ) ) { 
     414        die( '' ); 
     415    } 
     416 
     417    $post = get_post( $_POST['post_id'] ); 
     418 
     419    do_shortcode( $content ); 
     420 
     421    $grunion = Grunion_Contact_Form::$last; 
     422 
     423    $out = array( 
     424        'to'      => '', 
     425        'subject' => '', 
     426        'fields'  => array(), 
     427    ); 
     428 
     429    foreach ( $grunion->fields as $field ) { 
     430        $out['fields'][$field->get_attribute( 'id' )] = $field->attributes; 
     431    } 
     432 
     433    $to = $grunion->get_attribute( 'to' ); 
     434    $subject = $grunion->get_attribute( 'subject' ); 
     435    foreach ( array( 'to', 'subject' ) as $attribute ) { 
     436        $value = $grunion->get_attribute( $attribute ); 
     437        if ( isset( $grunion->defaults[$attribute] ) && $value == $grunion->defaults[$attribute] ) { 
     438            $value = ''; 
    395439        } 
    396         die( '' ); 
    397     } 
    398      
    399     die( -1 ); 
     440        $out[$attribute] = $value; 
     441    } 
     442 
     443    die( json_encode( $out ) ); 
    400444} 
    401445 
  • jetpack/branches/grunion/modules/contact-form/css/grunion.css

    r535805 r613295  
    1 .textwidget input[type='text'], .textwidget textarea { width: 100% !important; } 
     1.textwidget input[type='text'], .textwidget input[type='email'], .textwidget textarea { width: 100% !important; } 
    22.contact-form .clear-form { clear: both; } 
    3 .contact-form input[type='text'] { width: 300px; margin-bottom: 13px; } 
     3.contact-form input[type='text'], .contact-form input[type='email'] { width: 300px; margin-bottom: 13px; } 
    44.contact-form select { margin-bottom: 13px; } 
    55.contact-form textarea { height: 200px; width: 80%; float: none; margin-bottom: 13px; } 
     
    88.contact-form label.checkbox, .contact-form label.radio { margin-bottom: 3px; float: none; font-weight: bold; display: inline-block; } 
    99.contact-form label span { color: #AAA; margin-left: 4px; font-weight: normal; } 
     10.form-errors .form-error-message { color: red; } 
  • jetpack/branches/grunion/modules/contact-form/grunion-contact-form.php

    r588176 r613295  
    77AUthor: Automattic, Inc. 
    88Author URI: http://automattic.com/ 
    9 Version: 2.3 
     9Version: 2.4 
    1010License: GPLv2 or later 
    1111*/ 
     
    1717    require_once GRUNION_PLUGIN_DIR . '/admin.php'; 
    1818 
    19 // take the content of a contact-form shortcode and parse it into a list of field types 
    20 function contact_form_parse( $content ) { 
    21     // first parse all the contact-field shortcodes into an array 
    22     global $contact_form_fields, $grunion_form; 
    23     $contact_form_fields = array(); 
    24  
    25     if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'grunion_shortcode_to_json' ) { 
    26         wp_print_styles( 'grunion.css' ); 
    27     } 
     19/** 
     20 * Sets up various actions, filters, post types, post statuses, shortcodes. 
     21 */ 
     22class Grunion_Contact_Form_Plugin { 
     23    /** 
     24     * @var string The Widget ID of the widget currently being processed.  Used to build the unique contact-form ID for forms embedded in widgets. 
     25     */ 
     26    var $current_widget_id; 
     27 
     28    static function init() { 
     29        static $instance = false; 
     30 
     31        if ( !$instance ) { 
     32            $instance = new Grunion_Contact_Form_Plugin; 
     33        } 
     34 
     35        return $instance; 
     36    } 
     37 
     38    /** 
     39     * Strips HTML tags from input.  Output is NOT HTML safe. 
     40     * 
     41     * @param string $string 
     42     * @return string 
     43     */ 
     44    static function strip_tags( $string ) { 
     45        $string = wp_kses( $string, array() ); 
     46        return str_replace( '&amp;', '&', $string ); // undo damage done by wp_kses_normalize_entities() 
     47    } 
     48 
     49    function __construct() { 
     50        $this->add_shortcode(); 
     51 
     52        // While generating the output of a text widget with a contact-form shortcode, we need to know its widget ID. 
     53        add_action( 'dynamic_sidebar', array( $this, 'track_current_widget' ) ); 
     54 
     55        // Add a "widget" shortcode attribute to all contact-form shortcodes embedded in widgets 
     56        add_filter( 'widget_text', array( $this, 'widget_atts' ), 0 ); 
     57 
     58        // If Text Widgets don't get shortcode processed, hack ours into place. 
     59        if ( !has_filter( 'widget_text', 'do_shortcode' ) ) 
     60            add_filter( 'widget_text', array( $this, 'widget_shortcode_hack' ), 5 ); 
     61 
     62        // Akismet to the rescue 
     63        if ( function_exists( 'akismet_http_post' ) ) { 
     64            add_filter( 'contact_form_is_spam', array( $this, 'is_spam_akismet' ), 10 ); 
     65            add_action( 'contact_form_akismet', array( $this, 'akismet_submit' ), 10, 2 ); 
     66        } 
     67 
     68        add_action( 'loop_start', array( 'Grunion_Contact_Form', '_style_on' ) ); 
     69 
     70        // custom post type we'll use to keep copies of the feedback items 
     71        register_post_type( 'feedback', array( 
     72            'labels'            => array( 
     73                'name'               => __( 'Feedbacks', 'jetpack' ), 
     74                'singular_name'      => __( 'Feedback', 'jetpack' ), 
     75                'search_items'       => __( 'Search Feedback', 'jetpack' ), 
     76                'not_found'          => __( 'No feedback found', 'jetpack' ), 
     77                'not_found_in_trash' => __( 'No feedback found', 'jetpack' ) 
     78            ), 
     79            'menu_icon'         => GRUNION_PLUGIN_URL . '/images/grunion-menu.png', 
     80            'show_ui'           => TRUE, 
     81            'show_in_admin_bar' => FALSE, 
     82            'public'            => FALSE, 
     83            'rewrite'           => FALSE, 
     84            'query_var'         => FALSE, 
     85            'capability_type'   => 'page' 
     86        ) ); 
     87 
     88        // Add "spam" as a post status 
     89        register_post_status( 'spam', array( 
     90            'label'                  => 'Spam', 
     91            'public'                 => FALSE, 
     92            'exclude_from_search'    => TRUE, 
     93            'show_in_admin_all_list' => FALSE, 
     94            'label_count'            => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'jetpack' ), 
     95            'protected'              => TRUE, 
     96            '_builtin'               => FALSE 
     97        ) ); 
     98 
     99        // POST handler 
     100        if ( 
     101            'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) 
     102        && 
     103            isset( $_POST['action'] ) && 'grunion-contact-form' == $_POST['action'] 
     104        && 
     105            isset( $_POST['contact-form-id'] ) 
     106        ) { 
     107            add_action( 'template_redirect', array( $this, 'process_form_submission' ) ); 
     108        } 
     109 
     110        /* Can be dequeued by placing the following in wp-content/themes/yourtheme/functions.php 
     111         * 
     112         *  function remove_grunion_style() { 
     113         *      wp_deregister_style('grunion.css'); 
     114         *  } 
     115         *  add_action('wp_print_styles', 'remove_grunion_style'); 
     116         */ 
     117 
     118        wp_register_style( 'grunion.css', GRUNION_PLUGIN_URL . 'css/grunion.css', array(), JETPACK__VERSION ); 
     119    } 
     120 
     121    /** 
     122     * Handles all contact-form POST submissions 
     123     * 
     124     * Conditionally attached to `template_redirect` 
     125     */ 
     126    function process_form_submission() { 
     127        $id = stripslashes( $_POST['contact-form-id'] ); 
     128 
     129        check_admin_referer( "contact-form_{$id}" ); 
     130 
     131        $is_widget = 0 === strpos( $id, 'widget-' ); 
     132 
     133        $form = false; 
     134 
     135        if ( $is_widget ) { 
     136            // It's a form embedded in a text widget 
     137 
     138            $this->current_widget_id = substr( $id, 7 ); // remove "widget-" 
     139             
     140            // Is the widget active? 
     141            $sidebar = is_active_widget( false, $this->current_widget_id, 'text' ); 
     142 
     143            // This is lame - no core API for getting a widget by ID 
     144            $widget = isset( $GLOBALS['wp_registered_widgets'][$this->current_widget_id] ) ? $GLOBALS['wp_registered_widgets'][$this->current_widget_id] : false; 
     145 
     146            if ( $sidebar && $widget && isset( $widget['callback'] ) ) { 
     147                // This is lamer - no API for outputting a given widget by ID 
     148                ob_start(); 
     149                // Process the widget to populate Grunion_Contact_Form::$last 
     150                call_user_func( $widget['callback'], array(), $widget['params'][0] ); 
     151                ob_end_clean(); 
     152            } 
     153        } else { 
     154            // It's a form embedded in a post 
     155 
     156            $post = get_post( $id ); 
     157 
     158            // Process the content to populate Grunion_Contact_Form::$last 
     159            apply_filters( 'the_content', $post->post_content ); 
     160        } 
     161 
     162        $form = Grunion_Contact_Form::$last; 
     163 
     164        if ( !$form || ( is_wp_error( $form->errors ) && $form->errors->get_error_codes() ) ) { 
     165            return; 
     166        } 
     167 
     168        // Process the form 
     169        $form->process_submission(); 
     170    } 
     171 
     172    /** 
     173     * Ensure the post author is always zero for contact-form feedbacks 
     174     * Attached to `wp_insert_post_data` 
     175     * 
     176     * @see Grunion_Contact_Form::process_submission() 
     177     * 
     178     * @param array $data the data to insert 
     179     * @param array $postarr the data sent to wp_insert_post() 
     180     * @return array The filtered $data to insert 
     181     */ 
     182    function insert_feedback_filter( $data, $postarr ) { 
     183        if ( $data['post_type'] == 'feedback' && $postarr['post_type'] == 'feedback' ) { 
     184            $data['post_author'] = 0; 
     185        } 
     186 
     187        return $data; 
     188    } 
     189 
     190    /* 
     191     * Adds our contact-form shortcode 
     192     * The "child" contact-field shortcode is added as needed by the contact-form shortcode handler 
     193     */ 
     194    function add_shortcode() { 
     195        add_shortcode( 'contact-form', array( 'Grunion_Contact_Form', 'parse' ) ); 
     196    } 
     197 
     198    /** 
     199     * Tracks the widget currently being processed. 
     200     * Attached to `dynamic_sidebar` 
     201     * 
     202     * @see $current_widget_id 
     203     * 
     204     * @param array $widget The widget data 
     205     */ 
     206    function track_current_widget( $widget ) { 
     207        $this->current_widget_id = $widget['id']; 
     208    } 
     209 
     210    /** 
     211     * Adds a "widget" attribute to every contact-form embedded in a text widget. 
     212     * Used to tell the difference between post-embedded contact-forms and widget-embedded contact-forms 
     213     * Attached to `widget_text` 
     214     * 
     215     * @param string $text The widget text 
     216     * @return string The filtered widget text 
     217     */ 
     218    function widget_atts( $text ) { 
     219        Grunion_Contact_Form::style( true ); 
     220 
     221        return preg_replace( '/\[contact-form([^a-zA-Z_-])/', '[contact-form widget="' . $this->current_widget_id . '"\\1', $text ); 
     222    } 
     223 
     224    /** 
     225     * For sites where text widgets are not processed for shortcodes, we add this hack to process just our shortcode 
     226     * Attached to `widget_text` 
     227     * 
     228     * @param string $text The widget text 
     229     * @return string The contact-form filtered widget text 
     230     */ 
     231    function widget_shortcode_hack( $text ) { 
     232        if ( !preg_match( '/\[contact-form([^a-zA-Z_-])/', $text ) ) { 
     233            return $text; 
     234        } 
     235 
     236        $old = $GLOBALS['shortcode_tags']; 
     237        remove_all_shortcodes(); 
     238        $this->add_shortcode(); 
     239 
     240        $text = do_shortcode( $text ); 
     241 
     242        $GLOBALS['shortcode_tags'] = $old; 
     243 
     244        return $text; 
     245    } 
     246 
     247    /** 
     248     * Populate an array with all values necessary to submit a NEW contact-form feedback to Akismet. 
     249     * Note that this includes the current user_ip etc, so this should only be called when accepting a new item via $_POST 
     250     * 
     251     * @param array $form Contact form feedback array 
     252     * @return array feedback array with additional data ready for submission to Akismet 
     253     */ 
     254    function prepare_for_akismet( $form ) { 
     255        $form['comment_type'] = 'contact_form'; 
     256        $form['user_ip']      = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] ); 
     257        $form['user_agent']   = $_SERVER['HTTP_USER_AGENT']; 
     258        $form['referrer']     = $_SERVER['HTTP_REFERER']; 
     259        $form['blog']         = get_option( 'home' ); 
     260 
     261        $ignore = array( 'HTTP_COOKIE' ); 
     262 
     263        foreach ( $_SERVER as $k => $value ) 
     264            if ( !in_array( $k, $ignore ) && is_string( $value ) ) 
     265                $form["$k"] = $value; 
     266             
     267        return $form; 
     268    } 
     269 
     270    /** 
     271     * Submit contact-form data to Akismet to check for spam. 
     272     * If you're accepting a new item via $_POST, run it Grunion_Contact_Form_Plugin::prepare_for_akismet() first 
     273     * Attached to `contact_form_is_spam` 
     274     * 
     275     * @param array $form 
     276     * @return bool|WP_Error TRUE => spam, FALSE => not spam, WP_Error => stop processing entirely 
     277     */ 
     278    function is_spam_akismet( $form ) { 
     279        global $akismet_api_host, $akismet_api_port; 
    28280     
    29     $out = do_shortcode( $content ); 
     281        if ( !function_exists( 'akismet_http_post' ) ) 
     282            return false; 
     283 
     284        $query_string = http_build_query( $form ); 
     285 
     286        $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port ); 
     287        $result = false; 
     288        if ( 'true' == trim( $response[1] ) ) // 'true' is spam 
     289            $result = true; 
     290        return apply_filters( 'contact_form_is_spam_akismet', $result, $form ); 
     291    } 
     292 
     293    /** 
     294     * Submit a feedback as either spam or ham 
     295     * 
     296     * @param string $as Either 'spam' or 'ham'. 
     297     * @param array $form the contact-form data 
     298     */ 
     299    function akismet_submit( $as, $form ) { 
     300        global $akismet_api_host, $akismet_api_port; 
     301 
     302        if ( !in_array( $as, array( 'ham', 'spam' ) ) ) 
     303            return false; 
     304 
     305        $query_string = http_build_query( $form ); 
     306 
     307        $response = akismet_http_post( $query_string, $akismet_api_host, "/1.1/submit-{$as}", $akismet_api_port ); 
     308        return trim( $response[1] ); 
     309    } 
     310} 
     311 
     312/** 
     313 * Generic shortcode class. 
     314 * Does nothing other than store structured data and output the shortcode as a string 
     315 * 
     316 * Not very general - specific to Grunion. 
     317 */ 
     318class Crunion_Contact_Form_Shortcode { 
     319    /** 
     320     * @var string the name of the shortcode: [$shortcode_name /] 
     321     */ 
     322    var $shortcode_name; 
     323 
     324    /** 
     325     * @var array key => value pairs for the shortcode's attributes: [$shortcode_name key="value" ... /] 
     326     */ 
     327    var $attributes; 
     328 
     329    /** 
     330     * @var array key => value pair for attribute defaults 
     331     */ 
     332    var $defaults = array(); 
     333 
     334    /** 
     335     * @var null|string Null for selfclosing shortcodes.  Hhe inner content of otherwise: [$shortcode_name]$content[/$shortcode_name] 
     336     */ 
     337    var $content; 
     338 
     339    /** 
     340     * @var array Associative array of inner "child" shortcodes equivalent to the $content: [$shortcode_name][child 1/][child 2/][/$shortcode_name] 
     341     */ 
     342    var $fields; 
     343 
     344    /** 
     345     * @var null|string The HTML of the parsed inner "child" shortcodes".  Null for selfclosing shortcodes. 
     346     */ 
     347    var $body; 
     348 
     349    /** 
     350     * @param array $attributes An associative array of shortcode attributes.  @see shortcode_atts() 
     351     * @param null|string $content Null for selfclosing shortcodes.  The inner content otherwise. 
     352     */ 
     353    function __construct( $attributes, $content = null ) { 
     354        $this->attributes = $this->unesc_attr( $attributes ); 
     355        if ( is_array( $content ) ) { 
     356            $string_content = ''; 
     357            foreach ( $content as $field ) { 
     358                $string_content .= (string) $field; 
     359            } 
     360 
     361            $this->content = $string_content; 
     362        } else { 
     363            $this->content = $content; 
     364        } 
     365 
     366        $this->parse_content( $content ); 
     367    } 
     368 
     369    /** 
     370     * Processes the shortcode's inner content for "child" shortcodes 
     371     * 
     372     * @param string $content The shortcode's inner content: [shortcode]$content[/shortcode] 
     373     */ 
     374    function parse_content( $content ) { 
     375        if ( is_null( $content ) ) { 
     376            $this->body = null; 
     377        } 
     378 
     379        $this->body = do_shortcode( $content ); 
     380    } 
     381 
     382    /** 
     383     * Returns the value of the requested attribute. 
     384     * 
     385     * @param string $key The attribute to retrieve 
     386     * @return mixed 
     387     */ 
     388    function get_attribute( $key ) { 
     389        return isset( $this->attributes[$key] ) ? $this->attributes[$key] : null; 
     390    } 
     391 
     392    function esc_attr( $value ) { 
     393        if ( is_array( $value ) ) { 
     394            return array_map( array( $this, 'esc_attr' ), $value ); 
     395        } 
     396 
     397        $value = Grunion_Contact_Form_Plugin::strip_tags( $value ); 
     398        $value = _wp_specialchars( $value, ENT_QUOTES, false, true ); 
     399 
     400        // Shortcode attributes can't contain "]" 
     401        $value = str_replace( ']', '', $value ); 
     402        $value = str_replace( ',', '&#x002c;', $value ); // store commas encoded 
     403        $value = strtr( $value, array( '%' => '%25', '&' => '%26' ) ); 
     404 
     405        // shortcode_parse_atts() does stripcslashes() 
     406        $value = addslashes( $value ); 
     407        return $value; 
     408    } 
     409 
     410    function unesc_attr( $value ) { 
     411        if ( is_array( $value ) ) { 
     412            return array_map( array( $this, 'unesc_attr' ), $value ); 
     413        } 
     414 
     415        // For back-compat with old Grunion encoding 
     416        // Also, unencode commas 
     417        $value = strtr( $value, array( '%26' => '&', '%25' => '%' ) ); 
     418        $value = preg_replace( array( '/&#x0*22;/i', '/&#x0*27;/i', '/&#x0*26;/i', '/&#x0*2c;/i' ), array( '"', "'", '&', ',' ), $value ); 
     419        $value = htmlspecialchars_decode( $value, ENT_QUOTES ); 
     420        $value = Grunion_Contact_Form_Plugin::strip_tags( $value ); 
     421 
     422        return $value; 
     423    } 
     424 
     425    /** 
     426     * Generates the shortcode 
     427     */ 
     428    function __toString() { 
     429        $r = "[{$this->shortcode_name} "; 
     430 
     431        foreach ( $this->attributes as $key => $value ) { 
     432            if ( !$value ) { 
     433                continue; 
     434            } 
     435 
     436            if ( isset( $this->defaults[$key] ) && $this->defaults[$key] == $value ) { 
     437                continue; 
     438            } 
     439 
     440            if ( 'id' == $key ) { 
     441                continue; 
     442            } 
     443 
     444            $value = $this->esc_attr( $value ); 
     445 
     446            if ( is_array( $value ) ) { 
     447                $value = join( ',', $value ); 
     448            } 
     449 
     450            if ( false === strpos( $value, "'" ) ) { 
     451                $value = "'$value'"; 
     452            } elseif ( false === strpos( $value, '"' ) ) { 
     453                $value = '"' . $value . '"'; 
     454            } else { 
     455                // Shortcodes can't contain both '"' and "'".  Strip one. 
     456                $value = str_replace( "'", '', $value ); 
     457                $value = "'$value'"; 
     458            } 
     459 
     460            $r .= "{$key}={$value} "; 
     461        } 
     462 
     463        $r = rtrim( $r ); 
     464 
     465        if ( $this->fields ) { 
     466            $r .= ']'; 
     467 
     468            foreach ( $this->fields as $field ) { 
     469                $r .= (string) $field; 
     470            } 
     471 
     472            $r .= "[/{$this->shortcode_name}]"; 
     473        } else { 
     474            $r .= '/]'; 
     475        } 
     476 
     477        return $r; 
     478    } 
     479} 
     480 
     481/** 
     482 * Class for the contact-form shortcode. 
     483 * Parses shortcode to output the contact form as HTML 
     484 * Sends email and stores the contact form response (a.k.a. "feedback") 
     485 */ 
     486class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode { 
     487    var $shortcode_name = 'contact-form'; 
     488 
     489    /** 
     490     * @var WP_Error stores form submission errors 
     491     */ 
     492    var $errors; 
     493 
     494    /** 
     495     * @var Grunion_Contact_Form The most recent (inclusive) contact-form shortcode processed 
     496     */ 
     497    static $last; 
     498 
     499    /** 
     500     * @var bool Whether to print the grunion.css style when processing the contact-form shortcode 
     501     */ 
     502    static $style = false; 
     503 
     504    function __construct( $attributes, $content = null ) { 
     505        global $wp_query; 
     506 
     507        // Set up the default subject and recipient for this form 
     508        $default_to = get_option( 'admin_email' ); 
     509        $default_subject = "[" . get_option( 'blogname' ) . "]"; 
     510         
     511        if ( !empty( $attributes['widget'] ) && $attributes['widget'] ) { 
     512            $attributes['id'] = 'widget-' . $attributes['widget']; 
     513 
     514            $default_subject = sprintf( _x( '%1$s Sidebar', '%1$s = blog name', 'jetpack' ), $default_subject ); 
     515        } else { 
     516            $attributes['id'] = get_the_ID(); 
     517 
     518            $post = get_post( $attributes['id'] ); 
     519 
     520            if ( $post ) { 
     521                $default_subject = sprintf( _x( '%1$s %2$s', '%1$s = blog name, %2$s = post title' ), $default_subject, Grunion_Contact_Form_Plugin::strip_tags( $post->post_title ) ); 
     522                $post_author = get_userdata( $post->post_author ); 
     523                $default_to = $post_author->user_email; 
     524            }  
     525        } 
     526 
     527        $this->defaults = array( 
     528            'to'           => $default_to, 
     529            'subject'      => $default_subject, 
     530            'show_subject' => 'no', // only used in back-compat mode 
     531            'widget'       => 0,    // Not exposed to the user. Works with Grunion_Contact_Form_Plugin::widget_atts() 
     532            'id'           => null, // Not exposed to the user. Set above. 
     533        ); 
     534 
     535        $attributes = shortcode_atts( $this->defaults, $attributes ); 
     536 
     537        // We only add the contact-field shortcode temporarily while processing the contact-form shortcode 
     538        add_shortcode( 'contact-field', array( $this, 'parse_contact_field' ) ); 
     539 
     540        parent::__construct( $attributes, $content ); 
     541 
     542        // There were no fields in the contact form. The form was probably just [contact-form /]. Build a default form. 
     543        if ( empty( $this->fields ) ) { 
     544            // same as the original Grunion v1 form 
     545            $default_form = ' 
     546                [contact-field label="' . __( 'Name', 'jetpack' )    . '" type="name"  required="true" /] 
     547                [contact-field label="' . __( 'Email', 'jetpack' )   . '" type="email" required="true" /] 
     548                [contact-field label="' . __( 'Website', 'jetpack' ) . '" type="url" /]'; 
     549 
     550            if ( 'yes' == strtolower( $this->get_attribute( 'show_subject' ) ) ) { 
     551                $default_form .= ' 
     552                    [contact-field label="' . __( 'Subject', 'jetpack' ) . '" type="subject" /]'; 
     553            } 
     554 
     555            $default_form .= ' 
     556                [contact-field label="' . __( 'Message', 'jetpack' ) . '" type="textarea" /]'; 
     557 
     558            $this->parse_content( $default_form ); 
     559        } 
     560 
     561        // $this->body and $this->fields have been setup.  We no longer need the contact-field shortcode. 
     562        remove_shortcode( 'contact-field' ); 
     563    } 
     564 
     565    /** 
     566     * Toggle for printing the grunion.css stylesheet 
     567     * 
     568     * @param bool $style 
     569     */ 
     570    static function style( $style ) { 
     571        $previous_style = self::$style; 
     572        self::$style = (bool) $style; 
     573        return $previous_style; 
     574    } 
     575 
     576    /** 
     577     * Turn on printing of grunion.css stylesheet 
     578     * @see ::style() 
     579     * @internal 
     580     * @param bool $style 
     581     */ 
     582    static function _style_on() { 
     583        return self::style( true ); 
     584    } 
     585 
     586    /** 
     587     * The contact-form shortcode processor 
     588     * 
     589     * @param array $attributes Key => Value pairs as parsed by shortcode_parse_atts() 
     590     * @param string|null $content The shortcode's inner content: [contact-form]$content[/contact-form] 
     591     * @return string HTML for the concat form. 
     592     */ 
     593    static function parse( $attributes, $content ) { 
     594        // Create a new Grunion_Contact_Form object (this class) 
     595        $form = new Grunion_Contact_Form( $attributes, $content ); 
     596 
     597        $id = $form->get_attribute( 'id' ); 
     598 
     599        if ( !$id ) { // something terrible has happened 
     600            return '[contact-form]'; 
     601        } 
     602 
     603        if ( apply_filters( 'jetpack_bail_on_shortcode', false, 'contact-form' ) || is_feed() ) { 
     604            return '[contact-form]'; 
     605        } 
     606 
     607        // Only allow one contact form per post/widget 
     608        if ( self::$last && $id == self::$last->get_attribute( 'id' ) ) { 
     609            // We're processing the same post 
     610 
     611            if ( self::$last->attributes != $form->attributes || self::$last->content != $form->content ) { 
     612                // And we're processing a different shortcode; 
     613                return ''; 
     614            } // else, we're processing the same shortcode - probably a separate run of do_shortcode() - let it through 
     615 
     616        } else { 
     617            self::$last = $form; 
     618        } 
     619 
     620        // Output the grunion.css stylesheet if self::$style allows it 
     621        if ( self::$style && ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'grunion_shortcode_to_json' ) ) { 
     622            ob_start(); 
     623            wp_print_styles( 'grunion.css' ); // wp_print_styles() will only ever print grunion.css once, regaurdless of how many times it is called. 
     624            $r = ob_get_clean(); 
     625        } else { 
     626            $r = ''; 
     627        } 
     628 
     629        $r .= "<div id='contact-form-$id'>\n"; 
    30630     
    31     if ( empty($contact_form_fields) || !is_array($contact_form_fields) ) { 
    32         // default form: same as the original Grunion form 
    33         $default_form = ' 
    34         [contact-field label="'.__( 'Name', 'jetpack' ).'" type="name" required="true" /] 
    35         [contact-field label="'.__( 'Email', 'jetpack' ).'" type="email" required="true" /] 
    36         [contact-field label="'.__( 'Website', 'jetpack' ).'" type="url" /]'; 
    37         if ( 'yes' == strtolower($grunion_form->show_subject) ) { 
    38             $default_form .= ' 
    39             [contact-field label="'.__( 'Subject', 'jetpack' ).'" type="subject" /]'; 
    40         } 
    41         $default_form .= ' 
    42         [contact-field label="'.__( 'Message', 'jetpack' ).'" type="textarea" /]'; 
    43  
    44         $out = do_shortcode( $default_form ); 
    45     } 
    46  
    47     return $out; 
     631        if ( is_wp_error( $form->errors ) && $form->errors->get_error_codes() ) { 
     632            // There are errors.  Display them 
     633            $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<ul class='form-errors'>\n"; 
     634            foreach ( $form->errors->get_error_messages() as $message ) 
     635                $r .= "\t<li class='form-error-message'>" . esc_html( $message ) . "</li>\n"; 
     636            $r .= "</ul>\n</div>\n\n"; 
     637        } 
     638 
     639        if ( isset( $_GET['contact-form-id'] ) && $_GET['contact-form-id'] == self::$last->get_attribute( 'id' ) && isset( $_GET['contact-form-sent'] ) ) { 
     640            // The contact form was submitted.  Show the success message/results 
     641 
     642            $feedback_id = (int) $_GET['contact-form-sent']; 
     643 
     644            $back_url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', '_wpnonce' ) ); 
     645 
     646            $r_success_message = 
     647                "<h3>" . __( 'Message Sent', 'jetpack' ) . 
     648                ' (<a href="' . esc_url( $back_url ) . '">' . esc_html__( 'go back', 'jetpack' ) . '</a>)' . 
     649                "</h3>\n\n"; 
     650 
     651            // Don't show the feedback details unless the nonce matches 
     652            if ( $feedback_id && wp_verify_nonce( stripslashes( $_GET['_wpnonce'] ), "contact-form-sent-{$feedback_id}" ) ) { 
     653                $feedback = get_post( $feedback_id ); 
     654 
     655                $field_ids = $form->get_field_ids(); 
     656 
     657                // Maps field_ids to post_meta keys 
     658                $field_value_map = array( 
     659                    'name'     => 'author', 
     660                    'email'    => 'author_email', 
     661                    'url'      => 'author_url', 
     662                    'subject'  => 'subject', 
     663                    'textarea' => false, // not a post_meta key.  This is stored in post_content 
     664                ); 
     665 
     666                $contact_form_message = "<blockquote>\n"; 
     667 
     668                // "Standard" field whitelist 
     669                foreach ( $field_value_map as $type => $meta_key ) { 
     670                    if ( isset( $field_ids[$type] ) ) { 
     671                        $field = $form->fields[$field_ids[$type]]; 
     672 
     673                        if ( $meta_key ) { 
     674                            $value = get_post_meta( $feedback_id, "_feedback_{$meta_key}", true ); 
     675                        } else { 
     676                            // The feedback content is stored as the first "half" of post_content 
     677                            $value = $feedback->post_content; 
     678                            list( $value ) = explode( '<!--more-->', $value ); 
     679                            $value = trim( $value ); 
     680                        } 
     681 
     682                        $contact_form_message .= sprintf( 
     683                            _x( '%1$s: %2$s', '%1$s = form field label, %2$s = form field value', 'jetpack' ), 
     684                            wp_kses( $field->get_attribute( 'label' ), array() ), 
     685                            wp_kses( $value, array() ) 
     686                        ) . '<br />'; 
     687                    } 
     688                } 
     689 
     690                // "Non-standard" fields 
     691                if ( $field_ids['extra'] ) { 
     692                    // array indexed by field label (not field id) 
     693                    $extra_fields = get_post_meta( $feedback_id, '_feedback_extra_fields', true ); 
     694 
     695                    foreach ( $field_ids['extra'] as $field_id ) { 
     696                        $field = $form->fields[$field_id]; 
     697                        $label = $field->get_attribute( 'label' ); 
     698                        $contact_form_message .= sprintf( 
     699                            _x( '%1$s: %2$s', '%1$s = form field label, %2$s = form field value', 'jetpack' ), 
     700                            wp_kses( $label, array() ), 
     701                            wp_kses( $extra_fields[$label], array() ) 
     702                        ) . '<br />'; 
     703                    } 
     704                } 
     705 
     706                $contact_form_message .= "</blockquote><br /><br />"; 
     707 
     708                $r_success_message .= wp_kses( $contact_form_message, array( 'br' => array(), 'blockquote' => array() ) ); 
     709            } 
     710 
     711            $r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message ); 
     712        } else { 
     713            // Nothing special - show the normal contact form 
     714 
     715            if ( $form->get_attribute( 'widget' ) ) { 
     716                // Submit form to the current URL 
     717                $url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', 'action', '_wpnonce' ) ); 
     718            } else { 
     719                // Submit form to the post permalink 
     720                $url = get_permalink( $feedback_id ); 
     721            } 
     722 
     723            // May eventually want to send this to admin-post.php... 
     724            $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id ); 
     725 
     726            $r .= "<form action='" . esc_url( $url ) . "' method='post' class='contact-form commentsblock'>\n"; 
     727            $r .= $form->body; 
     728            $r .= "\t<p class='contact-submit'>\n"; 
     729            $r .= "\t\t<input type='submit' value='" . esc_attr__( 'Submit &#187;', 'jetpack' ) . "' class='pushbutton-wide'/>\n"; 
     730            $r .= "\t\t" . wp_nonce_field( 'contact-form_' . $id, '_wpnonce', true, false ) . "\n"; // nonce and referer 
     731            $r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n"; 
     732            $r .= "\t\t<input type='hidden' name='action' value='grunion-contact-form' />\n"; 
     733            $r .= "\t</p>\n"; 
     734            $r .= "</form>\n"; 
     735        } 
     736 
     737        $r .= "</div>"; 
     738 
     739        return $r; 
     740    } 
     741 
     742    /** 
     743     * The contact-field shortcode processor 
     744     * We use an object method here instead of a static Grunion_Contact_Form_Field class method to parse contact-field shortcodes so that we can tie them to the contact-form object. 
     745     * 
     746     * @param array $attributes Key => Value pairs as parsed by shortcode_parse_atts() 
     747     * @param string|null $content The shortcode's inner content: [contact-field]$content[/contact-field] 
     748     * @return HTML for the contact form field 
     749     */ 
     750    function parse_contact_field( $attributes, $content ) { 
     751        $field = new Grunion_Contact_Form_Field( $attributes, $content, $this ); 
     752 
     753        $field_id = $field->get_attribute( 'id' ); 
     754        if ( $field_id ) { 
     755            $this->fields[$field_id] = $field; 
     756        } else { 
     757            $this->fields[] = $field; 
     758        } 
     759 
     760        if ( 
     761            isset( $_POST['action'] ) && 'grunion-contact-form' === $_POST['action'] 
     762        && 
     763            isset( $_POST['contact-form-id'] ) && $this->get_attribute( 'id' ) == $_POST['contact-form-id'] 
     764        ) { 
     765            // If we're processing a POST submission for this contact form, validate the field value so we can show errors as necessary. 
     766            $field->validate(); 
     767        } 
     768 
     769        // Output HTML 
     770        return $field->render(); 
     771    } 
     772 
     773    /** 
     774     * Loops through $this->fields to generate a (structured) list of field IDs 
     775     * @return array 
     776     */ 
     777    function get_field_ids() { 
     778        $field_ids = array( 
     779            'all'   => array(), // array of all field_ids 
     780            'extra' => array(), // array of all non-whitelisted field IDs 
     781 
     782            // Whitelisted "standard" field IDs: 
     783            // 'email'    => field_id, 
     784            // 'name'     => field_id, 
     785            // 'url'      => field_id, 
     786            // 'subject'  => field_id, 
     787            // 'textarea' => field_id, 
     788        ); 
     789 
     790        foreach ( $this->fields as $id => $field ) { 
     791            $field_ids['all'][] = $id; 
     792 
     793            $type = $field->get_attribute( 'type' ); 
     794            if ( isset( $field_ids[$type] ) ) { 
     795                // This type of field is already present in our whitelist of "standard" fields for this form 
     796                // Put it in extra 
     797                $field_ids['extra'][] = $id; 
     798                continue; 
     799            } 
     800 
     801            switch ( $type ) { 
     802            case 'email' : 
     803            case 'name' : 
     804            case 'url' : 
     805            case 'subject' : 
     806            case 'textarea' : 
     807                $field_ids[$type] = $id; 
     808                break; 
     809            default : 
     810                // Put everything else in extra 
     811                $field_ids['extra'][] = $id; 
     812            } 
     813        } 
     814 
     815        return $field_ids; 
     816    } 
     817 
     818    /** 
     819     * Process the contact form's POST submission 
     820     * Stores feedback.  Sends email. 
     821     */ 
     822    function process_submission() { 
     823        global $post; 
     824 
     825        $plugin = Grunion_Contact_Form_Plugin::init(); 
     826 
     827        $id     = $this->get_attribute( 'id' ); 
     828        $to     = $this->get_attribute( 'to' ); 
     829        $widget = $this->get_attribute( 'widget' ); 
     830 
     831        $contact_form_subject = $this->get_attribute( 'subject' ); 
     832 
     833        $to = str_replace( ' ', '', $to ); 
     834        $emails = explode( ',', $to ); 
     835 
     836        $valid_emails = array(); 
     837 
     838        foreach ( (array) $emails as $email ) { 
     839            if ( !is_email( $email ) ) { 
     840                continue; 
     841            } 
     842 
     843            if ( function_exists( 'is_email_address_unsafe' ) && is_email_address_unsafe( $email ) ) { 
     844                continue; 
     845            } 
     846 
     847            $valid_emails[] = $email; 
     848        } 
     849 
     850        // No one to send it to :( 
     851        if ( !$valid_emails ) { 
     852            return; 
     853        } 
     854 
     855        $to = $valid_emails; 
     856 
     857        // Make sure we're processing the form we think we're processing... probably a redundant check. 
     858        if ( $widget ) { 
     859            if ( 'widget-' . $widget != $_POST['contact-form-id'] ) { 
     860                return; 
     861            } 
     862        } else { 
     863            if ( $post->ID != $_POST['contact-form-id'] ) { 
     864                return; 
     865            } 
     866        } 
     867 
     868        $field_ids = $this->get_field_ids(); 
     869 
     870        // Initialize all these "standard" fields to null 
     871        $comment_author_email = $comment_author_email_label = // v 
     872        $comment_author       = $comment_author_label       = // v 
     873        $comment_author_url   = $comment_author_url_label   = // v 
     874        $comment_content      = $comment_content_label      = null; 
     875 
     876        // For each of the "standard" fields, grab their field label and value. 
     877 
     878        if ( isset( $field_ids['name'] ) ) { 
     879            $field = $this->fields[$field_ids['name']]; 
     880            $comment_author = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_name', addslashes( $field->value ) ) ) ); 
     881            $comment_author_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) ); 
     882        } 
     883 
     884        if ( isset( $field_ids['email'] ) ) { 
     885            $field = $this->fields[$field_ids['email']]; 
     886            $comment_author_email = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_email', addslashes( $field->value ) ) ) ); 
     887            $comment_author_email_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) ); 
     888        } 
     889 
     890        if ( isset( $field_ids['url'] ) ) { 
     891            $field = $this->fields[$field_ids['url']]; 
     892            $comment_author_url = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_url', addslashes( $field->value ) ) ) ); 
     893            if ( 'http://' == $comment_author_url ) { 
     894                $comment_author_url = ''; 
     895            } 
     896            $comment_author_url_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) ); 
     897        } 
     898 
     899        if ( isset( $field_ids['textarea'] ) ) { 
     900            $field = $this->fields[$field_ids['textarea']]; 
     901            $comment_content = trim( Grunion_Contact_Form_Plugin::strip_tags( $field->value ) ); 
     902            $comment_content_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) ); 
     903        } 
     904 
     905        if ( isset( $field_ids['subject'] ) ) { 
     906            $field = $this->fields[$field_ids['subject']]; 
     907            if ( $field->value ) { 
     908                $contact_form_subject = Grunion_Contact_Form_Plugin::strip_tags( $field->value ); 
     909            } 
     910        } 
     911 
     912        $all_values = $extra_values = array(); 
     913 
     914        // For all fields, grab label and value 
     915        foreach ( $field_ids['all'] as $field_id ) { 
     916            $field = $this->fields[$field_id]; 
     917            $label = $field->get_attribute( 'label' ); 
     918            $value = $field->value; 
     919            $all_values[$label] = $value; 
     920        } 
     921 
     922        // For the "non-standard" fields, grab label and value 
     923        foreach ( $field_ids['extra'] as $field_id ) { 
     924            $field = $this->fields[$field_id]; 
     925            $label = $field->get_attribute( 'label' ); 
     926            $value = $field->value; 
     927            $extra_values[$label] = $value; 
     928        } 
     929 
     930        $contact_form_subject = trim( $contact_form_subject ); 
     931 
     932        $comment_author_IP = Grunion_Contact_Form_Plugin::strip_tags( $_SERVER['REMOTE_ADDR'] ); 
     933 
     934        $vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' ); 
     935        foreach ( $vars as $var ) 
     936            $$var = str_replace( array( "\n", "\r" ), '', $$var ); 
     937        $vars[] = 'comment_content'; 
     938 
     939        $spam = ''; 
     940        $akismet_values = $plugin->prepare_for_akismet( compact( $vars ) ); 
     941 
     942        // Is it spam? 
     943        $is_spam = apply_filters( 'contact_form_is_spam', $akismet_values ); 
     944        if ( is_wp_error( $is_spam ) ) // WP_Error to abort 
     945            return; // abort 
     946        else if ( $is_spam === TRUE )  // TRUE to flag a spam 
     947            $spam = '***SPAM*** '; 
     948 
     949        if ( !$comment_author ) 
     950            $comment_author = $comment_author_email; 
     951 
     952        $to = (array) apply_filters( 'contact_form_to', $to ); 
     953        foreach ( $to as $to_key => $to_value ) { 
     954            $to[$to_key] = Grunion_Contact_Form_Plugin::strip_tags( $to_value ); 
     955        } 
     956 
     957        $from_email_addr = $to[0]; 
     958        if ( !empty( $comment_author_email ) ) { 
     959            $from_email_addr = $comment_author_email; 
     960        } 
     961 
     962        $headers = 'From: ' . $comment_author  . 
     963            ' <' . $from_email_addr  . ">\r\n" . 
     964            'Reply-To: ' . $from_email_addr  . "\r\n" . 
     965            "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"";  
     966 
     967        $subject = apply_filters( 'contact_form_subject', $contact_form_subject ); 
     968 
     969        $time = date_i18n( __( 'l F j, Y \a\t g:i a', 'jetpack' ), current_time( 'timestamp' ) ); 
     970     
     971        $extra_content = ''; 
     972     
     973        foreach ( $extra_values as $label => $value ) { 
     974            $extra_content .= $label . ': ' . trim( $value ) . "\n"; 
     975        } 
     976 
     977        $message = "$comment_author_label: $comment_author\n"; 
     978        if ( !empty( $comment_author_email ) ) { 
     979            $message .= "$comment_author_email_label: $comment_author_email\n"; 
     980        } 
     981        if ( !empty( $comment_author_url ) ) { 
     982            $message .= "$comment_author_url_label: $comment_author_url\n"; 
     983        } 
     984        if ( !empty( $comment_content_label ) ) { 
     985            $message .= "$comment_content_label: $comment_content\n"; 
     986        } 
     987        $message .= $extra_content . "\n"; 
     988 
     989        $message .= __( 'Time:', 'jetpack' ) . ' ' . $time . "\n"; 
     990        $message .= __( 'IP Address:', 'jetpack' ) . ' ' . $comment_author_IP . "\n"; 
     991        $message .= __( 'Contact Form URL:', 'jetpack' ) . ' ' . get_permalink( $post->ID ) . "\n"; 
     992 
     993 
     994        if ( is_user_logged_in() ) { 
     995            $message .= "\n"; 
     996            $message .= sprintf( 
     997                __( 'Sent by a verified %s user.', 'jetpack' ), 
     998                isset( $GLOBALS['current_site']->site_name ) && $GLOBALS['current_site']->site_name ? $GLOBALS['current_site']->site_name : '"' . get_option( 'blogname' ) . '"' 
     999            ); 
     1000        } else { 
     1001            $message .= __( 'Sent by an unverified visitor to your site.', 'jetpack' ); 
     1002        } 
     1003 
     1004        $message = apply_filters( 'contact_form_message', $message ); 
     1005        $message = Grunion_Contact_Form_Plugin::strip_tags( $message ); 
     1006 
     1007        // keep a copy of the feedback as a custom post type 
     1008        $feedback_mysql_time = current_time( 'mysql' ); 
     1009        $feedback_title = "{$comment_author} - {$feedback_mysql_time}"; 
     1010        $feedback_status = 'publish'; 
     1011        if ( $is_spam === TRUE ) 
     1012            $feedback_status = 'spam'; 
     1013 
     1014        foreach ( (array) $akismet_values as $av_key => $av_value ) { 
     1015            $akismet_values[$av_key] = Grunion_Contact_Form_Plugin::strip_tags( $av_value ); 
     1016        } 
     1017 
     1018        foreach ( (array) $all_values as $all_key => $all_value ) { 
     1019            $all_values[$all_key] = Grunion_Contact_Form_Plugin::strip_tags( $all_value ); 
     1020        } 
     1021 
     1022        foreach ( (array) $extra_values as $ev_key => $ev_value ) { 
     1023            $extra_values[$ev_key] = Grunion_Contact_Form_Plugin::strip_tags( $ev_value ); 
     1024        } 
     1025 
     1026        /* We need to make sure that the post author is always zero for contact 
     1027         * form submissions.  This prevents export/import from trying to create 
     1028         * new users based on form submissions from people who were logged in 
     1029         * at the time. 
     1030         * 
     1031         * Unfortunately wp_insert_post() tries very hard to make sure the post 
     1032         * author gets the currently logged in user id.  That is how we ended up 
     1033         * with this work around. */ 
     1034        add_filter( 'wp_insert_post_data', array( $plugin, 'insert_feedback_filter' ), 10, 2 ); 
     1035 
     1036        $post_id = wp_insert_post( array( 
     1037            'post_date'    => addslashes( $feedback_mysql_time ), 
     1038            'post_type'    => 'feedback', 
     1039            'post_status'  => addslashes( $feedback_status ), 
     1040            'post_parent'  => (int) $post->ID, 
     1041            'post_title'   => addslashes( wp_kses( $feedback_title, array() ) ), 
     1042            'post_content' => addslashes( wp_kses( $comment_content . "\n<!--more-->\n" . "AUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$contact_form_subject}\nIP: {$comment_author_IP}\n" . print_r( $all_values, TRUE ), array() ) ), // so that search will pick up this data 
     1043            'post_name'    => md5( $feedback_title ), 
     1044        ) ); 
     1045 
     1046        // once insert has finished we don't need this filter any more 
     1047        remove_filter( 'wp_insert_post_data', array( $plugin, 'insert_feedback_filter' ), 10, 2 ); 
     1048 
     1049        update_post_meta( $post_id, '_feedback_author', addslashes( $comment_author ) ); 
     1050        update_post_meta( $post_id, '_feedback_author_email', addslashes( $comment_author_email ) ); 
     1051        update_post_meta( $post_id, '_feedback_author_url', addslashes( $comment_author_url ) ); 
     1052        update_post_meta( $post_id, '_feedback_subject', addslashes( $contact_form_subject ) ); 
     1053        update_post_meta( $post_id, '_feedback_ip', addslashes( $comment_author_IP ) ); 
     1054        update_post_meta( $post_id, '_feedback_contact_form_url', addslashes( get_permalink( $post->ID ) ) ); 
     1055        update_post_meta( $post_id, '_feedback_all_fields', $this->addslashes_deep( $all_values ) ); 
     1056        update_post_meta( $post_id, '_feedback_extra_fields', $this->addslashes_deep( $extra_values ) ); 
     1057        update_post_meta( $post_id, '_feedback_akismet_values', $this->addslashes_deep( $akismet_values ) ); 
     1058        update_post_meta( $post_id, '_feedback_email', $this->addslashes_deep( array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers ) ) ); 
     1059 
     1060        do_action( 'grunion_pre_message_sent', $post_id, $all_values, $extra_values ); 
     1061 
     1062        // schedule deletes of old spam feedbacks 
     1063        if ( !wp_next_scheduled( 'grunion_scheduled_delete' ) ) { 
     1064            wp_schedule_event( time() + 250, 'daily', 'grunion_scheduled_delete' ); 
     1065        } 
     1066 
     1067        if ( $is_spam !== TRUE ) 
     1068            wp_mail( $to, "{$spam}{$subject}", $message, $headers ); 
     1069        elseif ( apply_filters( 'grunion_still_email_spam', FALSE ) == TRUE ) // don't send spam by default.  Filterable. 
     1070            wp_mail( $to, "{$spam}{$subject}", $message, $headers ); 
     1071 
     1072        $redirect = wp_get_referer(); 
     1073        if ( !$redirect ) { // wp_get_referer() returns false if the referer is the same as the current page 
     1074            $redirect = $_SERVER['REQUEST_URI']; 
     1075        } 
     1076 
     1077        $redirect = add_query_arg( urlencode_deep( array( 
     1078            'contact-form-id'   => $id, 
     1079            'contact-form-sent' => $post_id, 
     1080            '_wpnonce'          => wp_create_nonce( "contact-form-sent-{$post_id}" ), // wp_nonce_url HTMLencodes :( 
     1081        ) ), $redirect ); 
     1082 
     1083        $redirect = apply_filters( 'grunion_contact_form_redirect_url', $redirect, $id, $post_id ); 
     1084 
     1085        wp_safe_redirect( $redirect ); 
     1086        exit; 
     1087    } 
     1088 
     1089    function addslashes_deep( $value ) { 
     1090        if ( is_array( $value ) ) { 
     1091            return array_map( array( $this, 'addslashes_deep' ), $value ); 
     1092        } elseif ( is_object( $value ) ) { 
     1093            $vars = get_object_vars( $value ); 
     1094            foreach ( $vars as $key => $data ) { 
     1095                $value->{$key} = $this->addslashes_deep( $data ); 
     1096            } 
     1097            return $value; 
     1098        } 
     1099 
     1100        return addslashes( $value ); 
     1101    } 
    481102} 
    491103 
    50 function contact_form_render_field( $field ) { 
    51     global $contact_form_last_id, $contact_form_errors, $contact_form_fields, $current_user, $user_identity; 
    52      
    53     $r = ''; 
    54      
    55     $field_id = $field['id']; 
    56     if ( isset($_POST[ $field_id ]) ) { 
    57         $field_value = stripslashes( $_POST[ $field_id ] ); 
    58     } elseif ( is_user_logged_in() ) { 
    59         // Special defaults for logged-in users 
    60         if ( $field['type'] == 'email' ) 
    61             $field_value = $current_user->data->user_email; 
    62         elseif ( $field['type'] == 'name' ) 
    63             $field_value = $user_identity; 
    64         elseif ( $field['type'] == 'url' ) 
    65             $field_value = $current_user->data->user_url; 
     1104/** 
     1105 * Class for the contact-field shortcode. 
     1106 * Parses shortcode to output the contact form field as HTML. 
     1107 * Validates input. 
     1108 */ 
     1109class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode { 
     1110    var $shortcode_name = 'contact-field'; 
     1111 
     1112    /** 
     1113     * @var Grunion_Contact_Form parent form 
     1114     */ 
     1115    var $form; 
     1116 
     1117    /** 
     1118     * @var string default or POSTed value 
     1119     */ 
     1120    var $value; 
     1121 
     1122    /** 
     1123     * @var bool Is the input invalid? 
     1124     */ 
     1125    var $error = false; 
     1126 
     1127    /** 
     1128     * @param array $attributes An associative array of shortcode attributes.  @see shortcode_atts() 
     1129     * @param null|string $content Null for selfclosing shortcodes.  The inner content otherwise. 
     1130     * @param Grunion_Contact_Form $form The parent form 
     1131     */ 
     1132    function __construct( $attributes, $content = null, $form = null ) { 
     1133        $attributes = shortcode_atts( array( 
     1134            'label'    => null, 
     1135            'type'     => 'text', 
     1136            'required' => false, 
     1137            'options'  => array(), 
     1138            'id'       => null, 
     1139            'default'  => null, 
     1140        ), $attributes ); 
     1141 
     1142        // special default for subject field 
     1143        if ( 'subject' == $attributes['type'] && is_null( $attributes['default'] ) && !is_null( $form ) ) { 
     1144            $attributes['default'] = $form->get_attribute( 'subject' ); 
     1145        } 
     1146 
     1147        // allow required=1 or required=true 
     1148        if ( '1' == $attributes['required'] || 'true' == strtolower( $attributes['required'] ) ) 
     1149            $attributes['required'] = true; 
    661150        else 
    67             $field_value = $field['default']; 
    68     } else { 
    69         $field_value = $field['default']; 
    70     } 
    71      
    72     $field_value = wp_kses($field_value, array()); 
    73  
    74     $field['label'] = html_entity_decode( $field['label'] ); 
    75     $field['label'] = wp_kses( $field['label'], array() ); 
    76  
    77     if ( $field['type'] == 'email' ) { 
    78         $r .= "\n<div>\n"; 
    79         $r .= "\t\t<label for='".esc_attr($field_id)."' class='grunion-field-label ".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    80         $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n"; 
    81         $r .= "\t</div>\n"; 
    82     } elseif ( $field['type'] == 'textarea' ) { 
    83         $r .= "\n<div>\n"; 
    84         $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "' for='contact-form-comment-" . esc_attr( $field_id ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    85         $r .= "\t\t<textarea name='".esc_attr($field_id)."' id='contact-form-comment-".esc_attr($field_id)."' rows='20'>".htmlspecialchars($field_value)."</textarea>\n"; 
    86         $r .= "\t</div>\n"; 
    87     } elseif ( $field['type'] == 'radio' ) { 
    88         $r .= "\t<div><label class='". ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    89         foreach ( $field['options'] as $option ) { 
    90             $r .= "\t\t<label class='" . esc_attr( $field['type'] ) . ( contact_form_is_error( $field_id ) ? ' form-error' : '' ) . "'>"; 
    91             $r .= "<input type='radio' name='".esc_attr($field_id)."' value='".esc_attr($option)."' class='".esc_attr($field['type'])."' ".( $option == $field_value ? "checked='checked' " : "")." /> "; 
    92             $r .= htmlspecialchars( $option ) . "</label>\n"; 
     1151            $attributes['required'] = false; 
     1152 
     1153        // parse out comma-separated options list (for selects and radios) 
     1154        if ( !empty( $attributes['options'] ) && is_string( $attributes['options'] ) ) { 
     1155            $attributes['options'] = array_map( 'trim', explode( ',', $attributes['options'] ) ); 
     1156        } 
     1157 
     1158        if ( $form ) { 
     1159            // make a unique field ID based on the label, with an incrementing number if needed to avoid clashes 
     1160            $form_id = $form->get_attribute( 'id' ); 
     1161            $id = isset( $attributes['id'] ) ? $attributes['id'] : false; 
     1162 
     1163            $unescaped_label = $this->unesc_attr( $attributes['label'] ); 
     1164            $unescaped_label = str_replace( '%', '-', $unescaped_label ); // jQuery doesn't like % in IDs? 
     1165 
     1166            if ( empty( $id ) ) { 
     1167                $id = sanitize_title_with_dashes( $form_id . '-' . $unescaped_label ); 
     1168                $i = 0; 
     1169                $max_tries = 12; 
     1170                while ( isset( $form->fields[$id] ) ) { 
     1171                    $i++; 
     1172                    $id = sanitize_title_with_dashes( $form_id . '-' . $unescaped_label . '-' . $i ); 
     1173 
     1174                    if ( $i > $max_tries ) { 
     1175                        break; 
     1176                    } 
     1177                } 
     1178            } 
     1179 
     1180            $attributes['id'] = $id; 
     1181        } 
     1182 
     1183        parent::__construct( $attributes, $content ); 
     1184 
     1185        // Store parent form 
     1186        $this->form = $form; 
     1187    } 
     1188 
     1189    /** 
     1190     * This field's input is invalid.  Flag as invalid and add an error to the parent form 
     1191     * 
     1192     * @param string $message The error message to display on the form. 
     1193     */ 
     1194    function add_error( $message ) { 
     1195        $this->is_error = true; 
     1196 
     1197        if ( !is_wp_error( $this->form->errors ) ) { 
     1198            $this->form->errors = new WP_Error; 
     1199        } 
     1200 
     1201        $this->form->errors->add( $this->get_attribute( 'id' ), $message ); 
     1202    } 
     1203 
     1204    /** 
     1205     * Is the field input invalid? 
     1206     * 
     1207     * @see $error 
     1208     * 
     1209     * @return bool 
     1210     */ 
     1211    function is_error() { 
     1212        return $this->error; 
     1213    } 
     1214 
     1215    /** 
     1216     * Validates the form input 
     1217     */ 
     1218    function validate() { 
     1219        // If it's not required, there's nothing to validate 
     1220        if ( !$this->get_attribute( 'required' ) ) { 
     1221            return; 
     1222        } 
     1223 
     1224        $field_id    = $this->get_attribute( 'id' ); 
     1225        $field_type  = $this->get_attribute( 'type' ); 
     1226        $field_label = $this->get_attribute( 'label' ); 
     1227 
     1228        $field_value = isset( $_POST[$field_id] ) ? stripslashes( $_POST[$field_id] ) : ''; 
     1229 
     1230        switch ( $field_type ) { 
     1231        case 'email' : 
     1232            // Make sure the email address is valid 
     1233            if ( !is_email( $field_value ) ) { 
     1234                $this->add_error( sprintf( __( '%s requires a valid email address', 'jetpack' ), $field_label ) ); 
     1235            } 
     1236            break; 
     1237        default : 
     1238            // Just check for presence of any text 
     1239            if ( !strlen( trim( $field_value ) ) ) { 
     1240                $this->add_error( sprintf( __( '%s is required', 'jetpack' ), $field_label ) ); 
     1241            } 
     1242        } 
     1243    } 
     1244 
     1245    /** 
     1246     * Outputs the HTML for this form field 
     1247     * 
     1248     * @return string HTML 
     1249     */ 
     1250    function render() { 
     1251        global $current_user, $user_identity; 
     1252 
     1253        $r = ''; 
     1254 
     1255        $field_id        = $this->get_attribute( 'id' ); 
     1256        $field_type      = $this->get_attribute( 'type' ); 
     1257        $field_label     = $this->get_attribute( 'label' ); 
     1258        $field_required  = $this->get_attribute( 'required' ); 
     1259 
     1260        if ( isset( $_POST[$field_id] ) ) { 
     1261            $this->value = stripslashes( (string) $_POST[$field_id] ); 
     1262        } elseif ( is_user_logged_in() ) { 
     1263            // Special defaults for logged-in users 
     1264            switch ( $this->get_attribute( 'type' ) ) { 
     1265            case 'email'; 
     1266                $this->value = $current_user->data->user_email; 
     1267                break; 
     1268            case 'name' : 
     1269                $this->value = $user_identity; 
     1270                break; 
     1271            case 'url' : 
     1272                $this->value = $current_user->data->user_url; 
     1273                break; 
     1274            default : 
     1275                $this->value = $this->get_attribute( 'default' ); 
     1276            } 
     1277        } else { 
     1278            $this->value = $this->get_attribute( 'default' ); 
     1279        } 
     1280 
     1281        $field_value = Grunion_Contact_Form_Plugin::strip_tags( $this->value ); 
     1282        $field_label = Grunion_Contact_Form_Plugin::strip_tags( $field_label ); 
     1283 
     1284        switch ( $field_type ) { 
     1285        case 'email' : 
     1286            $r .= "\n<div>\n"; 
     1287            $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label email" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
     1288            $r .= "\t\t<input type='email' name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' value='" . esc_attr( $field_value ) . "' class='email' />\n"; 
     1289            $r .= "\t</div>\n"; 
     1290            break; 
     1291        case 'textarea' : 
     1292            $r .= "\n<div>\n"; 
     1293            $r .= "\t\t<label for='contact-form-comment-" . esc_attr( $field_id ) . "' class='grunion-field-label textarea" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
     1294            $r .= "\t\t<textarea name='" . esc_attr( $field_id ) . "' id='contact-form-comment-" . esc_attr( $field_id ) . "' rows='20'>" . esc_textarea( $field_value ) . "</textarea>\n"; 
     1295            $r .= "\t</div>\n"; 
     1296            break; 
     1297        case 'radio' : 
     1298            $r .= "\t<div><label class='grunion-field-label" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
     1299            foreach ( $this->get_attribute( 'options' ) as $option ) { 
     1300                $option = Grunion_Contact_Form_Plugin::strip_tags( $option ); 
     1301                $r .= "\t\t<label class='grunion-radio-label radio" . ( $this->is_error() ? ' form-error' : '' ) . "'>"; 
     1302                $r .= "<input type='radio' name='" . esc_attr( $field_id ) . "' value='" . esc_attr( $option ) . "' class='radio' " . checked( $option, $field_value, false ) . " /> "; 
     1303                $r .= esc_html( $option ) . "</label>\n"; 
     1304                $r .= "\t\t<div class='clear-form'></div>\n"; 
     1305            } 
     1306            $r .= "\t\t</div>\n"; 
     1307            break; 
     1308        case 'checkbox' : 
     1309            $r .= "\t<div>\n"; 
     1310            $r .= "\t\t<label class='grunion-field-label checkbox" . ( $this->is_error() ? ' form-error' : '' ) . "'>\n"; 
     1311            $r .= "\t\t<input type='checkbox' name='" . esc_attr( $field_id ) . "' value='" . esc_attr__( 'Yes', 'jetpack' ) . "' class='checkbox' " . checked( (bool) $field_value, true, false ) . " /> \n"; 
     1312            $r .= "\t\t" . esc_html( $field_label ) . ( $field_required ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    931313            $r .= "\t\t<div class='clear-form'></div>\n"; 
    94         } 
    95         $r .= "\t\t</div>\n"; 
    96     } elseif ( $field['type'] == 'checkbox' ) { 
    97         $r .= "\t<div>\n"; 
    98         $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>\n"; 
    99         $r .= "\t\t<input type='checkbox' name='".esc_attr($field_id)."' value='".__( 'Yes', 'jetpack' )."' class='".esc_attr($field['type'])."' ".( $field_value ? "checked='checked' " : "")." /> \n"; 
    100         $r .= "\t\t". htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    101         $r .= "\t\t<div class='clear-form'></div>\n"; 
    102         $r .= "\t</div>\n"; 
    103     } elseif ( $field['type'] == 'select' ) { 
    104         $r .= "\n<div>\n"; 
    105         $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    106         $r .= "\t<select name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n"; 
    107         foreach ( $field['options'] as $option ) { 
    108             $option = html_entity_decode( $option ); 
    109             $option = wp_kses( $option, array() ); 
    110             $r .= "\t\t<option".( $option == $field_value ? " selected='selected'" : "").">". esc_html( $option ) ."</option>\n"; 
    111         } 
    112         $r .= "\t</select>\n"; 
    113         $r .= "\t</div>\n"; 
    114     } else { 
    115         // default: text field 
    116         // note that any unknown types will produce a text input, so we can use arbitrary type names to handle 
    117         // input fields like name, email, url that require special validation or handling at POST 
    118         $r .= "\n<div>\n"; 
    119         $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
    120         $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n"; 
    121         $r .= "\t</div>\n"; 
    122     } 
    123      
    124     return $r; 
     1314            $r .= "\t</div>\n"; 
     1315            break; 
     1316        case 'select' : 
     1317            $r .= "\n<div>\n"; 
     1318            $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label select" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
     1319            $r .= "\t<select name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' class='select' />\n"; 
     1320            foreach ( $this->get_attribute( 'options' ) as $option ) { 
     1321                $option = Grunion_Contact_Form_Plugin::strip_tags( $option ); 
     1322                $r .= "\t\t<option" . selected( $option, $field_value, false ) . ">" . esc_html( $option ) . "</option>\n"; 
     1323            } 
     1324            $r .= "\t</select>\n"; 
     1325            $r .= "\t</div>\n"; 
     1326            break; 
     1327        default : // text field 
     1328            // note that any unknown types will produce a text input, so we can use arbitrary type names to handle 
     1329            // input fields like name, email, url that require special validation or handling at POST 
     1330            $r .= "\n<div>\n"; 
     1331            $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label " . esc_attr( $field_type ) . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n"; 
     1332            $r .= "\t\t<input type='text' name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' value='" . esc_attr( $field_value ) . "' class='" . esc_attr( $field_type ) . "'/>\n"; 
     1333            $r .= "\t</div>\n"; 
     1334        } 
     1335 
     1336        return $r; 
     1337    } 
    1251338} 
    1261339 
    127 function contact_form_validate_field( $field ) { 
    128     global $contact_form_last_id, $contact_form_errors, $contact_form_values; 
    129  
    130     $field_id = $field['id']; 
    131     $field_value = isset($_POST[ $field_id ]) ? stripslashes($_POST[ $field_id ]) : ''; 
    132  
    133     # pay special attention to required email fields 
    134     if ( $field['required'] && $field['type'] == 'email' ) { 
    135         if ( !is_email( $field_value ) ) { 
    136             if ( !is_wp_error( $contact_form_errors ) ) { 
    137                 $contact_form_errors = new WP_Error(); 
    138             } 
    139  
    140             $contact_form_errors->add( $field_id, sprintf( __( '%s requires a valid email address', 'jetpack' ), $field['label'] ) ); 
    141         } 
    142     } elseif ( $field['required'] && !trim($field_value) ) { 
    143         if ( !is_wp_error($contact_form_errors) ) { 
    144             $contact_form_errors = new WP_Error(); 
    145         } 
    146  
    147         $contact_form_errors->add( $field_id, sprintf( __( '%s is required', 'jetpack' ), $field['label'] ) ); 
    148     } 
    149      
    150     $contact_form_values[ $field_id ] = $field_value; 
    151 } 
    152  
    153 function contact_form_is_error( $field_id ) { 
    154     global $contact_form_errors; 
    155      
    156     return ( is_wp_error( $contact_form_errors ) && $contact_form_errors->get_error_message( $field_id ) ); 
    157 } 
    158  
    159 // generic shortcode that handles all of the major input types 
    160 // this parses the field attributes into an array that is used by other functions for rendering, validation etc 
    161 function contact_form_field( $atts, $content, $tag ) { 
    162     global $contact_form_fields, $contact_form_last_id, $grunion_form; 
    163      
    164     $field = shortcode_atts( array( 
    165         'label' => null, 
    166         'type' => 'text', 
    167         'required' => false, 
    168         'options' => array(), 
    169         'id' => null, 
    170         'default' => null, 
    171     ), $atts); 
    172      
    173     // special default for subject field 
    174     if ( $field['type'] == 'subject' && is_null($field['default']) ) 
    175         $field['default'] = $grunion_form->subject; 
    176      
    177     // allow required=1 or required=true 
    178     if ( $field['required'] == '1' || strtolower($field['required']) == 'true' ) 
    179         $field['required'] = true; 
    180     else 
    181         $field['required'] = false; 
    182          
    183     // parse out comma-separated options list 
    184     if ( !empty($field['options']) && is_string($field['options']) ) 
    185         $field['options'] = array_map('trim', explode(',', $field['options'])); 
    186  
    187     // make a unique field ID based on the label, with an incrementing number if needed to avoid clashes 
    188     $id = $field['id']; 
    189     if ( empty($id) ) { 
    190         $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] ); 
    191         $i = 0; 
    192         $max_tries = 12; 
    193         while ( isset( $contact_form_fields[ $id ] ) ) { 
    194             $i++; 
    195             $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] . '-' . $i ); 
    196  
    197             if ( $i > $max_tries ) { 
    198                 break; 
    199             } 
    200         } 
    201         $field['id'] = $id; 
    202     } 
    203      
    204     $contact_form_fields[ $id ] = $field; 
    205      
    206     if ( isset( $_POST['contact-form-id'] ) && $_POST['contact-form-id'] == $contact_form_last_id ) 
    207         contact_form_validate_field( $field ); 
    208      
    209     return contact_form_render_field( $field ); 
    210 } 
    211  
    212 add_shortcode('contact-field', 'contact_form_field'); 
    213  
    214  
    215 function contact_form_shortcode( $atts, $content ) { 
    216     global $post; 
    217  
    218     $default_to = get_option( 'admin_email' ); 
    219     $default_subject = "[" . get_option( 'blogname' ) . "]"; 
    220  
    221     if ( !empty( $atts['widget'] ) && $atts['widget'] ) { 
    222         $default_subject .=  " Sidebar"; 
    223     } elseif ( $post->ID ) { 
    224         $default_subject .= " ". wp_kses( $post->post_title, array() ); 
    225         $post_author = get_userdata( $post->post_author ); 
    226         $default_to = $post_author->user_email; 
    227     } 
    228  
    229     extract( shortcode_atts( array( 
    230         'to' => $default_to, 
    231         'subject' => $default_subject, 
    232         'show_subject' => 'no', // only used in back-compat mode 
    233         'widget' => 0 //This is not exposed to the user. Works with contact_form_widget_atts 
    234     ), $atts ) ); 
    235  
    236     $widget = esc_attr( $widget ); 
    237  
    238     if ( ( function_exists( 'faux_faux' ) && faux_faux() ) || is_feed() ) 
    239         return '[contact-form]'; 
    240  
    241     global $wp_query, $grunion_form, $contact_form_errors, $contact_form_values, $user_identity, $contact_form_last_id, $contact_form_message; 
    242      
    243     // used to store attributes, configuration etc for access by contact-field shortcodes 
    244     $grunion_form = new stdClass(); 
    245     $grunion_form->to = $to; 
    246     $grunion_form->subject = $subject; 
    247     $grunion_form->show_subject = $show_subject; 
    248  
    249     if ( $widget ) 
    250         $id = 'widget-' . $widget; 
    251     elseif ( is_singular() ) 
    252         $id = $wp_query->get_queried_object_id(); 
    253     else 
    254         $id = $GLOBALS['post']->ID; 
    255     if ( !$id ) // something terrible has happened 
    256         return '[contact-form]'; 
    257  
    258     if ( $id == $contact_form_last_id ) 
    259         return; 
    260     else 
    261         $contact_form_last_id = $id; 
    262  
    263     if ( is_user_logged_in() ) { 
    264         ob_start(); 
    265             wp_nonce_field( 'contact-form_' . $id ); 
    266             $nonce = ob_get_contents(); 
    267             $nonce = "\t\t$nonce\n"; 
    268         ob_end_clean(); 
    269     } else { 
    270         $nonce = ''; 
    271     } 
    272  
    273  
    274     $body = contact_form_parse( $content ); 
    275  
    276     $r = "<div id='contact-form-$id'>\n"; 
    277      
    278     $errors = array(); 
    279     if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) { 
    280         $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<ul class='form-errors'>\n"; 
    281         foreach ( $contact_form_errors->get_error_messages() as $message ) 
    282             $r .= "\t<li class='form-error-message' style='color: red;'>$message</li>\n"; 
    283         $r .= "</ul>\n</div>\n\n"; 
    284     } 
    285      
    286     $action = apply_filters( 'grunion_contact_form_form_action', get_permalink( $post->ID ) . "#contact-form-$id", $post, $id ); 
    287     $r .= "<form action='" . esc_url( $action ) . "' method='post' class='contact-form commentsblock'>\n"; 
    288     $r .= $body; 
    289     $r .= "\t<p class='contact-submit'>\n"; 
    290     $r .= "\t\t<input type='submit' value='" . __( "Submit &#187;", 'jetpack' ) . "' class='pushbutton-wide'/>\n"; 
    291     $r .= $nonce; 
    292     $r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n"; 
    293     $r .= "\t</p>\n"; 
    294     $r .= "</form>\n</div>"; 
    295      
    296     if ( !isset( $_POST['contact-form-id'] ) || $_POST['contact-form-id'] != $contact_form_last_id ) 
    297         return $r; 
    298  
    299  
    300     if ( is_wp_error($contact_form_errors) ) 
    301         return $r; 
    302  
    303      
    304     $emails = str_replace( ' ', '', $to ); 
    305     $emails = explode( ',', $emails ); 
    306     foreach ( (array) $emails as $email ) { 
    307         if ( is_email( $email ) && ( !function_exists( 'is_email_address_unsafe' ) || !is_email_address_unsafe( $email ) ) ) 
    308             $valid_emails[] = $email; 
    309     } 
    310  
    311     $to = ( $valid_emails ) ? $valid_emails : $default_to; 
    312  
    313     $message_sent = contact_form_send_message( $to, $subject, $widget ); 
    314  
    315     if ( is_array( $contact_form_values ) ) 
    316         extract( $contact_form_values ); 
    317  
    318     if ( !isset( $comment_content ) ) 
    319         $comment_content = ''; 
    320     else 
    321         $comment_content = wp_kses( $comment_content, array() ); 
    322  
    323  
    324     $r = "<div id='contact-form-$id'>\n"; 
    325  
    326     $errors = array(); 
    327     if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) : 
    328         $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<p>\n"; 
    329         foreach ( $contact_form_errors->get_error_messages() as $message ) 
    330             $r .= "\t$message<br />\n"; 
    331         $r .= "</p>\n</div>\n\n"; 
    332     else : 
    333         $r_success_message = "<h3>" . __( 'Message Sent', 'jetpack' ) . "</h3>\n\n"; 
    334         $r_success_message .= wp_kses($contact_form_message, array('br' => array(), 'blockquote' => array())); 
    335  
    336         $r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message ); 
    337  
    338         $r .= "</div>"; 
    339          
    340         // Reset for multiple contact forms. Hacky 
    341         $contact_form_values['comment_content'] = ''; 
    342  
    343         return $r; 
    344     endif; 
    345  
    346     return $r; 
    347 } 
    348 add_shortcode( 'contact-form', 'contact_form_shortcode' ); 
    349  
    350 function contact_form_send_message( $to, $subject, $widget ) { 
    351     global $post; 
    352      
    353     if ( !isset( $_POST['contact-form-id'] ) ) 
    354         return; 
    355          
    356     if ( ( $widget && 'widget-' . $widget != $_POST['contact-form-id'] ) || ( !$widget && $post->ID != $_POST['contact-form-id'] ) ) 
    357         return; 
    358  
    359     if ( is_user_logged_in() ) { 
    360         if ( $widget ) 
    361             check_admin_referer( 'contact-form_widget-' . $widget ); 
    362         else 
    363             check_admin_referer( 'contact-form_' . $post->ID ); 
    364     } 
    365  
    366     global $contact_form_values, $contact_form_errors, $current_user, $user_identity; 
    367     global $contact_form_fields, $contact_form_message; 
    368      
    369     // compact the fields and values into an array of Label => Value pairs 
    370     // also find values for comment_author_email and other significant fields 
    371     $all_values = $extra_values = array(); 
    372      
    373     foreach ( $contact_form_fields as $id => $field ) { 
    374         if ( $field['type'] == 'email' && !isset( $comment_author_email ) ) { 
    375             $comment_author_email = $contact_form_values[ $id ]; 
    376             $comment_author_email_label = $field['label']; 
    377         } elseif  ( $field['type'] == 'name' && !isset( $comment_author ) ) { 
    378             $comment_author = $contact_form_values[ $id ]; 
    379             $comment_author_label = $field['label']; 
    380         } elseif ( $field['type'] == 'url' && !isset( $comment_author_url ) ) { 
    381             $comment_author_url = $contact_form_values[ $id ]; 
    382             $comment_author_url_label = $field['label']; 
    383     } elseif ( $field['type'] == 'subject' && !isset( $contact_form_subject ) ) { 
    384             $contact_form_subject = $contact_form_values[$id]; 
    385             $contact_form_subject_label = $field['label']; 
    386         } elseif ( $field['type'] == 'textarea' && !isset( $comment_content ) ) { 
    387             $comment_content = $contact_form_values[ $id ]; 
    388             $comment_content_label = $field['label']; 
    389         } else { 
    390             $extra_values[ $field['label'] ] = $contact_form_values[ $id ]; 
    391         } 
    392          
    393         $all_values[ $field['label'] ] = $contact_form_values[ $id ]; 
    394     } 
    395  
    396 /* 
    397     $contact_form_values = array(); 
    398     $contact_form_errors = new WP_Error(); 
    399  
    400     list($comment_author, $comment_author_email, $comment_author_url) = is_user_logged_in() ? 
    401         add_magic_quotes( array( $user_identity, $current_user->data->user_email, $current_user->data->user_url ) ) : 
    402         array( $_POST['comment_author'], $_POST['comment_author_email'], $_POST['comment_author_url'] ); 
    403 */ 
    404  
    405     $comment_author = stripslashes( apply_filters( 'pre_comment_author_name', $comment_author ) ); 
    406  
    407     if ( !empty( $comment_author_email ) ) { 
    408         $comment_author_email = stripslashes( apply_filters( 'pre_comment_author_email', $comment_author_email ) ); 
    409     } else { 
    410         $comment_author_email = ''; 
    411         $comment_author_email_label = ''; 
    412     } 
    413  
    414     if ( !empty( $comment_author_url ) ) { 
    415         $comment_author_url = stripslashes( apply_filters( 'pre_comment_author_url', $comment_author_url ) ); 
    416         if ( 'http://' == $comment_author_url ) { 
    417             $comment_author_url = ''; 
    418         } 
    419     } else { 
    420         $comment_author_url = ''; 
    421         $comment_author_url_label = ''; 
    422     } 
    423  
    424     $comment_content = stripslashes( $comment_content ); 
    425     $comment_content = trim( wp_kses( $comment_content, array() ) ); 
    426  
    427     if ( empty( $contact_form_subject ) ) 
    428         $contact_form_subject = trim( wp_kses( $subject, array() ) ); 
    429     else 
    430         $contact_form_subject = trim( wp_kses( $contact_form_subject, array() ) ); 
    431          
    432     $comment_author_IP = $_SERVER['REMOTE_ADDR']; 
    433  
    434     $vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' ); 
    435     foreach ( $vars as $var ) 
    436         $$var = str_replace( array("\n", "\r" ), '', $$var ); // I don't know if it's possible to inject this 
    437     $vars[] = 'comment_content'; 
    438  
    439     $contact_form_values = compact( $vars ); 
    440  
    441     $spam = ''; 
    442     $akismet_values = contact_form_prepare_for_akismet( $contact_form_values ); 
    443     $is_spam = apply_filters( 'contact_form_is_spam', $akismet_values ); 
    444     if ( is_wp_error( $is_spam ) ) 
    445         return; // abort 
    446     else if ( $is_spam === TRUE ) 
    447         $spam = '***SPAM*** '; 
    448  
    449     if ( !$comment_author ) 
    450         $comment_author = $comment_author_email; 
    451  
    452     $to = apply_filters( 'contact_form_to', $to ); 
    453     foreach ( (array) $to as $to_key => $to_value ) { 
    454         $to[$to_key] = wp_kses( $to_value, array() ); 
    455     } 
    456  
    457     $from_email_addr = $to[0]; 
    458     if ( !empty( $comment_author_email ) ) { 
    459         $from_email_addr = $comment_author_email; 
    460     } 
    461  
    462     $headers = 'From: ' . wp_kses( $comment_author, array() ) . 
    463         ' <' . wp_kses( $from_email_addr, array() ) . ">\r\n" . 
    464         'Reply-To: ' . wp_kses( $from_email_addr, array() ) . "\r\n" . 
    465         "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"";  
    466     $subject = apply_filters( 'contact_form_subject', $contact_form_subject ); 
    467     $subject = wp_kses( $subject, array() ); 
    468  
    469     $time = date_i18n( __( 'l F j, Y \a\t g:i a', 'jetpack' ), current_time( 'timestamp' ) ); 
    470      
    471     $extra_content = ''; 
    472     $extra_content_br = ''; 
    473      
    474     foreach ( $extra_values as $label => $value ) { 
    475         $extra_content .= $label . ': ' . trim($value) . "\n"; 
    476         $extra_content_br .= wp_kses( $label, array() ) . ': ' . wp_kses( trim($value), array() ) . "<br />"; 
    477     } 
    478  
    479     $message = "$comment_author_label: $comment_author\n"; 
    480     if ( !empty( $comment_author_email ) ) { 
    481         $message .= "$comment_author_email_label: $comment_author_email\n"; 
    482     } 
    483     if ( !empty( $comment_author_url ) ) { 
    484         $message .= "$comment_author_url_label: $comment_author_url\n"; 
    485     } 
    486     $message .= "$comment_content_label: $comment_content\n"; 
    487     $message .= $extra_content . "\n"; 
    488  
    489     $message .= __( "Time:", 'jetpack' ) . " " . $time . "\n"; 
    490     $message .= __( "IP Address:", 'jetpack' ) . " " . $comment_author_IP . "\n"; 
    491     $message .= __( "Contact Form URL:", 'jetpack' ) . " " . get_permalink( $post->ID ) . "\n"; 
    492  
    493  
    494     // Construct message that is returned to user 
    495     $contact_form_message = "<blockquote>"; 
    496     if (isset($comment_author_label)) 
    497         $contact_form_message .= wp_kses( $comment_author_label, array() ) . ": " . wp_kses( $comment_author, array() ) . "<br />"; 
    498     if ( !empty( $comment_author_email ) ) 
    499         $contact_form_message .= wp_kses( $comment_author_email_label, array() ) . ": " . wp_kses( $comment_author_email, array() ) . "<br />";  
    500     if ( !empty( $comment_author_url ) ) 
    501         $contact_form_message .= wp_kses( $comment_author_url_label, array() ) . ": " . wp_kses( $comment_author_url, array() ) . "<br />"; 
    502     if ( !empty( $contact_form_subject_label ) ) { 
    503         $contact_form_message .= wp_kses( $contact_form_subject_label, array() ) . ": " . wp_kses( $contact_form_subject, array() ) . "<br />"; 
    504     } 
    505     if (isset($comment_content_label)) 
    506         $contact_form_message .= wp_kses( $comment_content_label, array() ) . ": " . wp_kses( $comment_content, array() ) . "<br />"; 
    507     if (isset($extra_content_br)) 
    508         $contact_form_message .= $extra_content_br; 
    509     $contact_form_message .= "</blockquote><br /><br />"; 
    510  
    511     if ( is_user_logged_in() ) { 
    512         $message .= "\n"; 
    513         $message .= sprintf( 
    514             __( 'Sent by a verified %s user.', 'jetpack' ), 
    515             isset( $GLOBALS['current_site']->site_name ) && $GLOBALS['current_site']->site_name ? $GLOBALS['current_site']->site_name : '"' . get_option( 'blogname' ) . '"' 
    516         ); 
    517     } else { 
    518         $message .= __( "Sent by an unverified visitor to your site.", 'jetpack' ); 
    519     } 
    520  
    521     $message = apply_filters( 'contact_form_message', $message ); 
    522     $message = wp_kses( $message, array() ); 
    523  
    524     // keep a copy of the feedback as a custom post type 
    525     $feedback_mysql_time = current_time( 'mysql' ); 
    526     $feedback_title = "{$comment_author} - {$feedback_mysql_time}"; 
    527     $feedback_status = 'publish'; 
    528     if ( $is_spam === TRUE ) 
    529         $feedback_status = 'spam'; 
    530  
    531     foreach ( (array) $akismet_values as $av_key => $av_value ) { 
    532         $akismet_values[$av_key] = wp_kses( $av_value, array() ); 
    533     } 
    534  
    535     foreach ( (array) $all_values as $all_key => $all_value ) { 
    536         $all_values[$all_key] = wp_kses( $all_value, array() ); 
    537     } 
    538  
    539     foreach ( (array) $extra_values as $ev_key => $ev_value ) { 
    540         $ev_values[$ev_key] = wp_kses( $ev_value, array() ); 
    541     } 
    542  
    543     # We need to make sure that the post author is always zero for contact 
    544     # form submissions.  This prevents export/import from trying to create 
    545     # new users based on form submissions from people who were logged in 
    546     # at the time. 
    547     # 
    548     # Unfortunately wp_insert_post() tries very hard to make sure the post 
    549     # author gets the currently logged in user id.  That is how we ended up 
    550     # with this work around. 
    551     global $do_grunion_insert; 
    552     $do_grunion_insert = TRUE; 
    553     add_filter( 'wp_insert_post_data', 'grunion_insert_filter', 10, 2 ); 
    554  
    555     $post_id = wp_insert_post( array( 
    556         'post_date'    => $feedback_mysql_time, 
    557         'post_type'    => 'feedback', 
    558         'post_status'  => $feedback_status, 
    559         'post_parent'  => $post->ID, 
    560         'post_title'   => wp_kses( $feedback_title, array() ), 
    561         'post_content' => wp_kses($comment_content . "\n<!--more-->\n" . "AUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$contact_form_subject}\nIP: {$comment_author_IP}\n" . print_r( $all_values, TRUE ), array()), // so that search will pick up this data 
    562         'post_name'    => md5( $feedback_title ) 
    563     ) ); 
    564  
    565     # once insert has finished we don't need this filter any more 
    566     remove_filter( 'wp_insert_post_data', 'grunion_insert_filter' ); 
    567     $do_grunion_insert = FALSE; 
    568  
    569     update_post_meta( $post_id, '_feedback_author', wp_kses( $comment_author, array() ) ); 
    570     update_post_meta( $post_id, '_feedback_author_email', wp_kses( $comment_author_email, array() ) ); 
    571     update_post_meta( $post_id, '_feedback_author_url', wp_kses( $comment_author_url, array() ) ); 
    572     update_post_meta( $post_id, '_feedback_subject', wp_kses( $contact_form_subject, array() ) ); 
    573     update_post_meta( $post_id, '_feedback_ip', wp_kses( $comment_author_IP, array() ) ); 
    574     update_post_meta( $post_id, '_feedback_contact_form_url', wp_kses( get_permalink( $post->ID ), array() ) ); 
    575     update_post_meta( $post_id, '_feedback_all_fields', $all_values ); 
    576     update_post_meta( $post_id, '_feedback_extra_fields', $extra_values ); 
    577     update_post_meta( $post_id, '_feedback_akismet_values', $akismet_values ); 
    578     update_post_meta( $post_id, '_feedback_email', array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers ) ); 
    579  
    580     do_action( 'grunion_pre_message_sent', $post_id, $all_values, $extra_values ); 
    581  
    582     # schedule deletes of old spam feedbacks 
    583     if ( !wp_next_scheduled( 'grunion_scheduled_delete' ) ) { 
    584         wp_schedule_event( time() + 250, 'daily', 'grunion_scheduled_delete' ); 
    585     } 
    586  
    587     if ( $is_spam !== TRUE ) 
    588         return wp_mail( $to, "{$spam}{$subject}", $message, $headers ); 
    589     elseif ( apply_filters( 'grunion_still_email_spam', FALSE ) == TRUE ) 
    590         return wp_mail( $to, "{$spam}{$subject}", $message, $headers ); 
    591  
    592 } 
    593  
    594 // populate an array with all values necessary to submit a NEW comment to Akismet 
    595 // note that this includes the current user_ip etc, so this should only be called when accepting a new item via $_POST 
    596 function contact_form_prepare_for_akismet( $form ) { 
    597  
    598     $form['comment_type'] = 'contact_form'; 
    599     $form['user_ip']      = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] ); 
    600     $form['user_agent']   = $_SERVER['HTTP_USER_AGENT']; 
    601     $form['referrer']     = $_SERVER['HTTP_REFERER']; 
    602     $form['blog']         = get_option( 'home' ); 
    603  
    604     $ignore = array( 'HTTP_COOKIE' ); 
    605  
    606     foreach ( $_SERVER as $k => $value ) 
    607         if ( !in_array( $k, $ignore ) && is_string( $value ) ) 
    608             $form["$k"] = $value; 
    609              
    610     return $form; 
    611 } 
    612  
    613 // submit an array to Akismet. If you're accepting a new item via $_POST, run it through contact_form_prepare_for_akismet() first 
    614 function contact_form_is_spam_akismet( $form ) { 
    615     if ( !function_exists( 'akismet_http_post' ) ) 
    616         return false; 
    617          
    618     global $akismet_api_host, $akismet_api_port; 
    619  
    620     $query_string = ''; 
    621     foreach ( array_keys( $form ) as $k ) 
    622         $query_string .= $k . '=' . urlencode( $form[$k] ) . '&'; 
    623  
    624     $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port ); 
    625     $result = false; 
    626     if ( 'true' == trim( $response[1] ) ) // 'true' is spam 
    627         $result = true; 
    628     return apply_filters( 'contact_form_is_spam_akismet', $result, $form ); 
    629 } 
    630  
    631 // submit a comment as either spam or ham 
    632 // $as should be a string (either 'spam' or 'ham'), $form should be the comment array 
    633 function contact_form_akismet_submit( $as, $form ) { 
    634     global $akismet_api_host, $akismet_api_port; 
    635      
    636     if ( !in_array( $as, array( 'ham', 'spam' ) ) ) 
    637         return false; 
    638  
    639     $query_string = ''; 
    640     foreach ( array_keys( $form ) as $k ) 
    641         $query_string .= $k . '=' . urlencode( $form[$k] ) . '&'; 
    642  
    643     $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/submit-'.$as, $akismet_api_port ); 
    644     return trim( $response[1] ); 
    645 } 
    646  
    647 function contact_form_widget_atts( $text ) { 
    648     static $widget = 0; 
    649      
    650     $widget++; 
    651  
    652     return preg_replace( '/\[contact-form([^a-zA-Z_-])/', '[contact-form widget="' . $widget . '"\\1', $text ); 
    653 } 
    654 add_filter( 'widget_text', 'contact_form_widget_atts', 0 ); 
    655  
    656 function contact_form_widget_shortcode_hack( $text ) { 
    657     if ( !preg_match( '/\[contact-form([^a-zA-Z_-])/', $text ) ) { 
    658         return $text; 
    659     } 
    660  
    661     $old = $GLOBALS['shortcode_tags']; 
    662     remove_all_shortcodes(); 
    663     add_shortcode( 'contact-form', 'contact_form_shortcode' ); 
    664     add_shortcode( 'contact-field', 'contact_form_field' ); 
    665     $text = do_shortcode( $text ); 
    666     $GLOBALS['shortcode_tags'] = $old; 
    667     return $text; 
    668 } 
    669  
    670 function contact_form_init() { 
    671     if ( function_exists( 'akismet_http_post' ) ) { 
    672         add_filter( 'contact_form_is_spam', 'contact_form_is_spam_akismet', 10 ); 
    673         add_action( 'contact_form_akismet', 'contact_form_akismet_submit', 10, 2 ); 
    674     } 
    675     if ( !has_filter( 'widget_text', 'do_shortcode' ) ) 
    676         add_filter( 'widget_text', 'contact_form_widget_shortcode_hack', 5 ); 
    677  
    678     // custom post type we'll use to keep copies of the feedback items 
    679     register_post_type( 'feedback', array( 
    680         'labels'            => array( 
    681             'name'               => __( 'Feedbacks', 'jetpack' ), 
    682             'singular_name'      => __( 'Feedback', 'jetpack' ), 
    683             'search_items'       => __( 'Search Feedback', 'jetpack' ), 
    684             'not_found'          => __( 'No feedback found', 'jetpack' ), 
    685             'not_found_in_trash' => __( 'No feedback found', 'jetpack' ) 
    686         ), 
    687         'menu_icon'         => GRUNION_PLUGIN_URL . 'images/grunion-menu.png', 
    688         'show_ui'           => TRUE, 
    689         'show_in_admin_bar' => FALSE, 
    690         'public'            => FALSE, 
    691         'rewrite'           => FALSE, 
    692         'query_var'         => FALSE, 
    693         'capability_type'   => 'page' 
    694     ) ); 
    695  
    696     register_post_status( 'spam', array( 
    697         'label'                  => 'Spam', 
    698         'public'                 => FALSE, 
    699         'exclude_from_search'    => TRUE, 
    700         'show_in_admin_all_list' => FALSE, 
    701         'label_count'            => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'jetpack' ), 
    702         'protected'              => TRUE, 
    703         '_builtin'               => FALSE 
    704     ) ); 
    705      
    706     /* Can be dequeued by placing the following in wp-content/themes/yourtheme/functions.php 
    707      * 
    708      *  function remove_grunion_style() { 
    709      *      wp_deregister_style('grunion.css'); 
    710      *  } 
    711      *  add_action('wp_print_styles', 'remove_grunion_style'); 
    712      */ 
    713      
    714     wp_register_style('grunion.css', GRUNION_PLUGIN_URL . 'css/grunion.css'); 
    715 } 
    716 add_action( 'init', 'contact_form_init' ); 
     1340add_action( 'init', array( 'Grunion_Contact_Form_Plugin', 'init' ) ); 
     1341 
     1342add_action( 'grunion_scheduled_delete', 'grunion_delete_old_spam' ); 
    7171343 
    7181344/** 
    719  * Add a contact form button to the post composition screen 
     1345 * Deletes old spam feedbacks to keep the posts table size under control 
    7201346 */ 
    721 add_action( 'media_buttons', 'grunion_media_button', 999 ); 
    722 function grunion_media_button( ) { 
    723     global $post_ID, $temp_ID; 
    724     $iframe_post_id = (int) (0 == $post_ID ? $temp_ID : $post_ID); 
    725     $title = esc_attr( __( 'Add a custom form', 'jetpack' ) ); 
    726     $plugin_url = esc_url( GRUNION_PLUGIN_URL ); 
    727     $site_url = admin_url( "/admin-ajax.php?post_id=$iframe_post_id&amp;grunion=form-builder&amp;action=grunion_form_builder&amp;TB_iframe=true&amp;width=768" ); 
    728  
    729     echo '<a href="' . $site_url . '&id=add_form" class="thickbox" title="' . $title . '"><div class="grunion-menu-button" alt="' . $title . '"></div></a>'; 
    730 } 
    731  
    732  
    733 if ( !empty( $_GET['grunion'] ) && $_GET['grunion'] == 'form-builder' ) { 
    734     add_action( 'parse_request', 'parse_wp_request' ); 
    735     add_action( 'wp_ajax_grunion_form_builder', 'parse_wp_request' ); 
    736 } 
    737  
    738 function parse_wp_request( $wp ) { 
    739     display_form_view( ); 
    740     exit; 
    741 } 
    742  
    743 function display_form_view( ) { 
    744     require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php'; 
    745 } 
    746  
    747 function menu_alter() { 
    748     echo ' 
    749     <style> 
    750     #menu-posts-feedback .wp-menu-image img { display: none; } 
    751     #adminmenu .menu-icon-feedback:hover div.wp-menu-image, 
    752     #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, 
    753     #adminmenu .menu-icon-feedback.current div.wp-menu-image { 
    754         background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover.png") no-repeat 6px 7px; 
    755     } 
    756     #adminmenu .menu-icon-feedback div.wp-menu-image { 
    757         background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu.png") no-repeat 6px 7px; 
    758     } 
    759     .grunion-menu-button { 
    760         background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form.png") no-repeat; 
    761         width: 13px; 
    762         height: 12px; 
    763         display: inline-block; 
    764     } 
    765     @media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) { 
    766         #adminmenu .menu-icon-feedback:hover div.wp-menu-image, 
    767         #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, 
    768         #adminmenu .menu-icon-feedback.current div.wp-menu-image { 
    769             background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover-2x.png") no-repeat 6px 7px; 
    770             background-size: 15px 16px; 
    771         } 
    772         #adminmenu .menu-icon-feedback div.wp-menu-image { 
    773             background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu-2x.png") no-repeat 6px 7px; 
    774             background-size: 15px 16px; 
    775         } 
    776         .grunion-menu-button { 
    777             background-image: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form-2x.png"); 
    778             background-size: 13px 12px; 
    779             vertical-align: bottom; 
    780         } 
    781     } 
    782     </style>'; 
    783 } 
    784  
    785 add_action('admin_head', 'menu_alter'); 
    786  
    787 function grunion_insert_filter( $data, $postarr ) { 
    788     global $do_grunion_insert; 
    789  
    790     if ( $do_grunion_insert === TRUE ) { 
    791         if ( $data['post_type'] == 'feedback' ) { 
    792             if ( $postarr['post_type'] == 'feedback' ) { 
    793                 $data['post_author'] = 0; 
    794             } 
    795         } 
    796     } 
    797  
    798     return $data; 
    799 } 
    800  
    801 add_action( 'grunion_scheduled_delete', 'grunion_delete_old_spam' ); 
    8021347function grunion_delete_old_spam() { 
    8031348    global $wpdb; 
  • jetpack/branches/grunion/modules/contact-form/js/grunion.js

    r579557 r613295  
    2222 
    2323GrunionFB_i18n.moveInstructions = GrunionFB_i18n.moveInstructions.replace( "\n", '<br />' ); 
     24 
     25FB.span = jQuery( '<span>' ); 
     26FB.esc_html = function( string ) { 
     27    return FB.span.text( string ).html(); 
     28}; 
     29 
     30FB.esc_attr = function( string ) { 
     31    string = FB.esc_html( string ); 
     32    return string.replace( '"', '&quot;' ).replace( "'", '&#039;' ); 
     33}; 
    2434 
    2535FB.ContactForm = function() { 
     
    158168                if (optionsCache[id].options[i] !== undefined) { 
    159169                    if (thisType === "radio") { 
    160                         thisOptions = thisOptions + '<div id="fb-radio-' + id + '-' + i + '"><input type="radio" id="fb-field' + id + '" name="radio-' + id + '" /><span>' + optionsCache[id].options[i] + '</span><div class="clear"></div></div>'; 
     170                        thisOptions = thisOptions + '<div id="fb-radio-' + id + '-' + i + '"><input type="radio" id="fb-field' + id + '" name="radio-' + id + '" /><span>' + FB.esc_html( optionsCache[id].options[i] ) + '</span><div class="clear"></div></div>'; 
    161171                    } else { 
    162                         thisOptions = thisOptions + '<option id="fb-' + id + '-' + i + '" value="' + id + '-' + i + '">' + optionsCache[id].options[i] + '</option>'; 
     172                        thisOptions = thisOptions + '<option id="fb-' + id + '-' + i + '" value="' + id + '-' + i + '">' + FB.esc_html( optionsCache[id].options[i] ) + '</option>'; 
    163173                    } 
    164174                } 
     
    278288                    if (thisOptions[i] !== undefined) { 
    279289                        if (thisType === "radio") { 
    280                             jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>'); 
     290                            jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + FB.esc_attr( fbForm.fields[id].options[i] ) + '" class="fb-options" /><div>'); 
    281291                        } else { 
    282                             jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>'); 
     292                            jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + FB.esc_attr( fbForm.fields[id].options[i] ) + '" class="fb-options" /><div>'); 
    283293                        } 
    284294                    } 
     
    527537            var thisId = jQuery('#fb-field-id').val(); 
    528538            if (!thisType) { var thisType = jQuery('#fb-new-type').val(); } 
    529             if (!thisLabelText) { var thisLabelText = jQuery('#fb-new-field' + thisId + ' label').html(); } 
     539            if (!thisLabelText) { var thisLabelText = jQuery('#fb-new-field' + thisId + ' .label-text').html(); } 
    530540            var isRequired = (thisRequired) ? '<span class="label-required">' + GrunionFB_i18n.requiredLabel + '</span>' : ''; 
    531             var thisLabel = '<label fieldid="' + thisId + '" for="fb-field' +  thisId + '"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>'; 
     541            var thisLabel = '<label fieldid="' + thisId + '" for="fb-field' +  thisId + '"><span class="label-text">' + FB.esc_html( thisLabelText ) + '</span>' + isRequired + '</label>'; 
    532542            var thisRadio = '<input type="radio" name="radio-' + thisId + '" id="fb-field' + thisId + ' "disabled="disabled" />'; 
    533             var thisRadioLabel = '<label fieldid="' + thisId + '" for="fb-field' +  thisId + '" class="fb-radio-label"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>'; 
     543            var thisRadioLabel = '<label fieldid="' + thisId + '" for="fb-field' +  thisId + '" class="fb-radio-label"><span class="label-text">' + FB.esc_html( thisLabelText ) + '</span>' + isRequired + '</label>'; 
    534544            var thisRadioRemove = '<div class="fb-remove fb-remove-small" id="' +  thisId + '"></div>'; 
    535545            var thisRemove = '<div class="fb-remove" id="' +  thisId + '"></div>'; 
Note: See TracChangeset for help on using the changeset viewer.