6
\$\begingroup\$

This is my first Wordpress plugin. I would love some input from the WP/PHP/JavaScript gurus out there. It's a simple plugin that saves a post using ctrl-Q and opens a preview in a new or current window.

<?php
    add_action('admin_menu', 'plugin_admin_add_page');
    add_action('admin_init', 'plugin_admin_init');
    add_action('admin_init', 'pass_option_value');
    wp_register_script('quick_preview',plugins_url('/quick-preview/quick-preview.js'),array('jquery'));
    wp_enqueue_script('quick_preview');


    function pass_option_value(){      //Gets the preview option and passes it to the    quick_preview script
        $options = get_option('quick_preview_options');
        wp_localize_script( 'quick_preview', 'quickPreviewOption', $options['window_preference'] );
    }

    function plugin_admin_add_page() {
        add_options_page('Quick Preview', 'Quick Preview', 'manage_options', 'quick_preview', 'qp_options_page');
    }

    function plugin_admin_init(){
        register_setting( 'quick_preview_options', 'quick_preview_options',    'qp_options_validate');
        add_settings_section('plugin_main', 'Main Settings', 'plugin_section_text',  'quick_preview');
        add_settings_field('plugin_window_preference', 'Window Preference',   'plugin_setting_string', 'quick_preview', 'plugin_main');
    }

    function plugin_section_text() {
        echo '<p>Choose whether you would like the post preview to open in a new window or    the current window.</p>';
    }

    function plugin_setting_string() {
        $options = get_option('quick_preview_options');?>
        <p><input id='plugin_window_preference' name='quick_preview_options[window_preference]' size='40' type='radio' value='current' <?  php if($options['window_preference'] === "current"){echo ("checked='checked'");}?> />  Current Window </p>
        <input id='plugin_window_preference' name='quick_preview_options[window_preference]' size='40' type='radio' value='new' <?php if($options['window_preference'] === "new"){echo ("checked='checked'");}?> /> New Window<p class="description">If popups are disabled you will need to add your site to the exception list in your broswer. </p> <?php }

    function qp_options_validate($input) {
        $newinput = array('window_preference' => trim($input['window_preference']));
        if( $newinput['window_preference'] != "new" && $newinput['window_preference'] != "current" ) {
        $newinput[ 'window_preference' ] = "";
        }
        return $newinput;
    }

    function qp_options_page() { ?>
        <div class="wrap">
            <div id="icon-options-general" class="icon32">
                <br />
            </div>
            <h2>Quick Preview Options</h2>
            <form action="options.php" method="post">
                <?php settings_fields('quick_preview_options'); ?>
                <?php do_settings_sections('quick_preview'); ?>
                <p class="submit">
                <input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e('Save Changes'); ?>" />
                </p>
            </form>
        </div>
<?php } ?>

JavaScript:

jQuery(document).ready(function(){

if (document.cookie.indexOf("previewCookie") >= 0){  
    //expires added for IE
    document.cookie="previewCookie=true; max-age=0;expires=0;path=/wp-admin/";             

    //quickPreviewOption is set in quick-preview.php  
    var previewURL = document.getElementById('post-preview');
    if(quickPreviewOption === 'current'){                                             
        window.location = previewURL;
    }
    if(quickPreviewOption === 'new'){
        window.open(previewURL,"wp_PostPreview","","true");
    }
}

jQuery(document).keydown(function(e){
    if((e.ctrlKey || e.metaKey) && e.which == 81){

        //Find #save post if it's a draft. If post is published, #save-post doesn't exist.
        if(jQuery('#save-post').length){
            jQuery('#save-post').click();    
        }
        else if(jQuery('publish').length){
            jQuery('#publish').click();
        }

        //Sets a cookie to open the preview on page refresh. Saving a post auotmatically refreshes the page.
        document.cookie = "previewCookie = true;max-age = 60;path=/wp-admin/";          
    }
});    
});
\$\endgroup\$
5
\$\begingroup\$

Well, I can't help you with the WordPress specifics, or the jquery, which means not much at all really. But here are a few things that might help you with your PHP.

Foremost, I would definitely consider separating your HTML from your logic. Or at the very least separate your escapes from PHP so that your PHP does not run into your HTML. It makes for much cleaner code that is also much easier to adapt and reuse. You can also take a look at heredocs and includes as another possible solution.

//proper escaping
?>
HTML
<?php//notice not on the same line as the HTML

//heredoc
echo <<<HTML
    Long
    bit
    of
    HTML
    maybe even with some $php_variables though no statements
HTML;//must not be indented, even if current line is 5 tabs in, this must be all the way to the left

You should also make sure you declare a variable before using it. $newinput came out of nowhere. Is it a global? Assuming for now that its not, you should declare $newinput as an array before manipulating it. This insures that $newinput definitely doesn't have any previous values, whether from a global state or some other fluke. This means no accidental appending onto another array, and no out of bounds warnings from accidentally treating a string as an array. So:

$newinput = array(
    'window_preference' => trim( $input[ 'window_preference' ] )
);

Since we are looking at this anyways, here's a couple of minor things. First, the absolute equality comparison === is usually only used when type is a factor. For example, TRUE === TRUE but "TRUE" !== TRUE. If we use the loose comparison ==, it accomplishes the same thing and allows for the type conversion, meaning that second comparison is TRUE as well. With strings this usually isn't a factor unless comparing a false string (empty, 0, "null", "false"). Second, I believe you have comparisons confused with assignments. The body of your if and else statements don't accomplish anything. If we were to read them, it would read like so, "if( TRUE ) { TRUE; } else { TRUE; }". Which makes no sense. If instead you meant to assign the value of$newinput[ 'window_preference' ]` to itself, you needn't bother, that's redundant. If all you want to do is remove the value from the array element given the opposite of that if statement, then you can alter the if statement slightly and use an actual assignment operator like so.

if( $newinput[ 'window_preference' ] != 'new' && $newinput[ 'window_preference' ] != 'current' ) {
    $newinput[ 'window_preference' ] = '';
}

But then, a better way to write this would be to make this the default value and only assign the new one if necessary. This makes the code lighter and much easier to read.

$newinput = array(
    'window_preference' => ''
);

$preference = trim( $input[ 'window_preference' ] );
if( $preference == 'new' || $preference == 'current' ) {
    $newinput[ 'window_preference' ] = $preference;
}

Hopefully this helps, sorry I couldn't answer any more specific concerns.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the PHP suggestions. I didn't realize how backward my validation function was! I'll also look into separating out the html and php. I've updated the code and up voted your answer. If no one else can provide a little more help with the WordPress specifics and js then I'll happily accept your answer. \$\endgroup\$ – FajitaNachos Sep 20 '12 at 22:38
5
\$\begingroup\$

Here are some tips for the JavaScript side:

1) Use $ instead of jQuery.

2) Use constant variables in place of numeric literals.

Old Code:

if ((e.ctrlKey || e.metaKey) && e.which == 81) {

New Code:

var CHAR_CODE_q = 81;
if ((e.ctrlKey || e.metaKey) && e.which == CHAR_CODE_q ) {

3) The id attribute value must be unique for all elements on a page. The opposite is true for the class attribute.

Be direct with a selection involving an id.

Old Code:

jQuery('input[type="submit"]#save-post').click();

New Code:

jQuery('#save-post').click();

4) window.location and the first argument of windown.open() should be set to an string that is a URL.

document.getElementById() returns a node element. The string value of a node element is the type of node element and not the HTML contained within the node.

Old Code:

window.location = document.getElementById('post-preview');

New Code:

// assuming there is a url within the html of element #post-preview.
var previewURL = document.getElementById('post-preview').innerHTML;
window.location = previewURL;

Final Code:

if (document.cookie.indexOf("previewCookie") >= 0) {
    document.cookie = "previewCookie=true; max-age=0;expires=0;path=/wp-admin/";

    var previewURL = $('#post-preview').text();
    if (quickPreviewOption === 'current') {
        window.location = previewURL;
    }
    if (quickPreviewOption === 'new') {
        window.open(previewURL, "wp_PostPreview", "", "true");
    }
}
$(document.body).keydown(function (e) {
    var CHAR_CODE_q = 81;
    if ((e.ctrlKey || e.metaKey) && e.which == CHAR_CODE_q) {
        if ($('#save-post').length) {
            $('#save-post').click();
        } else if ($('#publish').length) {
            $('#publish').click();
        }
        document.cookie = "previewCookie=true;max-age=60;path=/wp-admin/";
    }
});
\$\endgroup\$
  • \$\begingroup\$ Thanks for the javascript tips. I changed most of them and up voted your answer. \$\endgroup\$ – FajitaNachos Sep 21 '12 at 1:46
  • \$\begingroup\$ @FajitaNachos What does var previewURL = document.getElementById('post-preview'); mean? You should check the value of previewURL. Great job on your site by the way. \$\endgroup\$ – Larry Battle Sep 21 '12 at 3:55
  • \$\begingroup\$ When I print it to the console it shows the URL of the post preview. Something like www.yourblog.com/?p=266&preview=true . I tried using innerHTML like you suggested but it gave me the html of the button which was "preview". Thanks for the help. I'm just getting started on the site but it's fun :) \$\endgroup\$ – FajitaNachos Sep 21 '12 at 4:38
  • 2
    \$\begingroup\$ One note: Don’t use $ instead of jQuery in WordPress. \$\endgroup\$ – fuxia Apr 15 '13 at 17:36
1
\$\begingroup\$

I'll fill up with the missing part. mseancole covered the PHP and Larry Battle the JavaScript, now let's see WP Plugins perspective.

First of all, it's recommended to dispatch all our plugin action in a safe point, i.e., the hook plugins_loaded. And also encapsulate the functionality inside a class, so we don't have to worry with function prefixes and collisions. As boilerplate, I use Plugin Class Demo, from one of WordPress Developers moderators. And here's another one, collaborative maintained, at GitHub.

The register and enqueue functions have to be inside a proper hook, admin_print_scripts for the backend and wp_enqueue_scripts for the frontend. And, most important, they should only be enqueued in your plugin page, not everywhere. Note that register->enqueue->localize must be together and admin_init is not the place for them.

We target the specific plugin page just after add_menu_page:

# PHP 5.3+ anonymous function
# we could chain all this sequence of actions like this, albeit may be a bit confusing
add_action( 'plugins_loaded', function() 
{
    add_action( 'admin_menu', 'my_plugin_admin_add_page' );
});

function my_plugin_admin_add_page() 
{
    $page_hook = add_options_page(
        'Quick Preview', 
        'Quick Preview', 
        'manage_options', 
        'quick_preview', 
        'qp_options_page'
    );
    add_action( "admin_print_scripts-$page_hook", 'my_plugin_enqueue' );
}

function my_plugin_enqueue()
{
    $options = get_option('quick_preview_options');
    wp_register_script( 
        'quick_preview', 
        plugins_url('/quick-preview/quick-preview.js'), 
        array('jquery') 
    );
    wp_enqueue_script( 'quick_preview' );
    wp_localize_script( 
        'quick_preview', 
        'quickPreviewOption', 
        array(
            'window_pref' = > $options['window_preference']
        )
    );
}

Finally, a small observation regarding $ and jQuery with WordPress:

jQuery(document).ready(function( $ ) // <------- $
{
   $('#now-its-safe-to-use-dollar-sign').show()
});
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.