Category

Category: Wordpress

AJAX Multi File Upload in WordPress Front-End

Step 1: Create a custom page in WordPress

  • Go to your Dashboard > Pages > Add New
  • Name the page anything you want, ex: Ajax Uploader
  • In your Dashboard > Settings > Permalinks, make sure Common Settings is set to Post Name
  • In the new page, copy the page slag: if you used “Ajax Uploader” as your page name, the slag would be ajax-uploader
  • In your WordPress theme create a file called page-ajax-uploader.php  Notice how we attached the slag to the “page-“, this will allow us to add custom scripts that will only apply to this specific page.
  • Go to your browser and navigate to your new page. ex. http://example.com/ajax-uploader It should show you a blank white page, if not then you did something incorrectly.

Step 2: Working on our page

  • Building the HTML Form
1
2
3
4
5
6
7
8
9
10
<div class = "col-md-6 upload-form">
    <div class= "upload-response"></div>
    <div class = "form-group">
        <label><?php __('Select Files:', 'cvf-upload'); ?></label>
        <input type = "file" name = "files[]" accept = "image/*" class = "files-data form-control" multiple />
    </div>
    <div class = "form-group">
        <input type = "submit" value = "Upload" class = "btn btn-primary btn-upload" />
    </div>
</div>

Javascript

  • Setup our form data to be sent via AJAX:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$(document).ready(function() {
    // When the Upload button is clicked...
    $('body').on('click', '.upload-form .btn-upload', function(e){
        e.preventDefault;

        var fd = new FormData();
        var files_data = $('.upload-form .files-data'); // The <input type="file" /> field
       
        // Loop through each data and create an array file[] containing our files data.
        $.each($(files_data), function(i, obj) {
            $.each(obj.files,function(j,file){
                fd.append('files[' + j + ']', file);
            })
        });
       
        // our AJAX identifier
        fd.append('action', 'cvf_upload_files');  
       
        // Remove this code if you do not want to associate your uploads to the current page.
        fd.append('post_id', <?php echo $post->ID; ?>);

        $.ajax({
            type: 'POST',
            url: '<?php echo admin_url( 'admin-ajax.php' ); ?>',
            data: fd,
            contentType: false,
            processData: false,
            success: function(response){
                $('.upload-response').html(response); // Append Server Response
            }
        });
    });
});
  • This is how your page-ajax-uploader.php template should look like:
  • Make sure you have the latest jQuery in your header.php
    1
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php get_header(); ?>

    <section class = "inner-page-wrapper">
        <section class = "container">
            <section class = "row content">
                <?php if ( have_posts() ) while ( have_posts() ) : the_post(); ?>
                    <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                        <h1><?php the_title(); ?></h1>
                        <article class="entry-content">
                            <?php the_content(); ?>
                           
                            <div class = "col-md-6 upload-form">
                                <div class= "upload-response"></div>
                                <div class = "form-group">
                                    <label><?php __('Select Files:', 'cvf-upload'); ?></label>
                                    <input type = "file" name = "files[]" accept = "image/*" class = "files-data form-control" multiple />
                                </div>
                                <div class = "form-group">
                                    <input type = "submit" value = "Upload" class = "btn btn-primary btn-upload" />
                                </div>
                            </div>
                                                                                   
                            <script type = "text/javascript">
                            $(document).ready(function() {
                                // When the Upload button is clicked...
                                $('body').on('click', '.upload-form .btn-upload', function(e){
                                    e.preventDefault;

                                    var fd = new FormData();
                                    var files_data = $('.upload-form .files-data'); // The <input type="file" /> field
                                   
                                    // Loop through each data and create an array file[] containing our files data.
                                    $.each($(files_data), function(i, obj) {
                                        $.each(obj.files,function(j,file){
                                            fd.append('files[' + j + ']', file);
                                        })
                                    });
                                   
                                    // our AJAX identifier
                                    fd.append('action', 'cvf_upload_files');  
                                   
                                    // uncomment this code if you do not want to associate your uploads to the current page.
                                    fd.append('post_id', <?php echo $post->ID; ?>);

                                    $.ajax({
                                        type: 'POST',
                                        url: '<?php echo admin_url( 'admin-ajax.php' ); ?>',
                                        data: fd,
                                        contentType: false,
                                        processData: false,
                                        success: function(response){
                                            $('.upload-response').html(response); // Append Server Response
                                        }
                                    });
                                });
                            });                    
                            </script>
                           
                           
                        </article>
                    </article>
                <?php endwhile; ?>
            </section>
        </section>
    </section>
   
<?php get_footer(); ?>

Server Side Processing

  • Add the code bellow to your functions.php
  • Don’t forget to read thru the comments found within the codes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
add_action('wp_ajax_cvf_upload_files', 'cvf_upload_files');
add_action('wp_ajax_nopriv_cvf_upload_files', 'cvf_upload_files'); // Allow front-end submission

function cvf_upload_files(){
   
    $parent_post_id = isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0;  // The parent ID of our attachments
    $valid_formats = array("jpg", "png", "gif", "bmp", "jpeg"); // Supported file types
    $max_file_size = 1024 * 500; // in kb
    $max_image_upload = 10; // Define how many images can be uploaded to the current post
    $wp_upload_dir = wp_upload_dir();
    $path = $wp_upload_dir['path'] . '/';
    $count = 0;

    $attachments = get_posts( array(
        'post_type'         => 'attachment',
        'posts_per_page'    => -1,
        'post_parent'       => $parent_post_id,
        'exclude'           => get_post_thumbnail_id() // Exclude post thumbnail to the attachment count
    ) );

    // Image upload handler
    if( $_SERVER['REQUEST_METHOD'] == "POST" ){
       
        // Check if user is trying to upload more than the allowed number of images for the current post
        if( ( count( $attachments ) + count( $_FILES['files']['name'] ) ) > $max_image_upload ) {
            $upload_message[] = "Sorry you can only upload " . $max_image_upload . " images for each Ad";
        } else {
           
            foreach ( $_FILES['files']['name'] as $f => $name ) {
                $extension = pathinfo( $name, PATHINFO_EXTENSION );
                // Generate a randon code for each file name
                $new_filename = cvf_td_generate_random_code( 20 )  . '.' . $extension;
               
                if ( $_FILES['files']['error'][$f] == 4 ) {
                    continue;
                }
               
                if ( $_FILES['files']['error'][$f] == 0 ) {
                    // Check if image size is larger than the allowed file size
                    if ( $_FILES['files']['size'][$f] > $max_file_size ) {
                        $upload_message[] = "$name is too large!.";
                        continue;
                   
                    // Check if the file being uploaded is in the allowed file types
                    } elseif( ! in_array( strtolower( $extension ), $valid_formats ) ){
                        $upload_message[] = "$name is not a valid format";
                        continue;
                   
                    } else{
                        // If no errors, upload the file...
                        if( move_uploaded_file( $_FILES["files"]["tmp_name"][$f], $path.$new_filename ) ) {
                           
                            $count++;

                            $filename = $path.$new_filename;
                            $filetype = wp_check_filetype( basename( $filename ), null );
                            $wp_upload_dir = wp_upload_dir();
                            $attachment = array(
                                'guid'           => $wp_upload_dir['url'] . '/' . basename( $filename ),
                                'post_mime_type' => $filetype['type'],
                                'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
                                'post_content'   => '',
                                'post_status'    => 'inherit'
                            );
                            // Insert attachment to the database
                            $attach_id = wp_insert_attachment( $attachment, $filename, $parent_post_id );

                            require_once( ABSPATH . 'wp-admin/includes/image.php' );
                           
                            // Generate meta data
                            $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
                            wp_update_attachment_metadata( $attach_id, $attach_data );
                           
                        }
                    }
                }
            }
        }
    }
    // Loop through each error then output it to the screen
    if ( isset( $upload_message ) ) :
        foreach ( $upload_message as $msg ){       
            printf( __('<p class="bg-danger">%s</p>', 'wp-trade'), $msg );
        }
    endif;
   
    // If no error, show success message
    if( $count != 0 ){
        printf( __('<p class = "bg-success">%d files added successfully!</p>', 'wp-trade'), $count );  
    }
   
    exit();
}

// Random code generator used for file names.
function cvf_td_generate_random_code($length=10) {
 
   $string = '';
   $characters = "23456789ABCDEFHJKLMNPRTVWXYZabcdefghijklmnopqrstuvwxyz";
 
   for ($p = 0; $p < $length; $p++) {
       $string .= $characters[mt_rand(0, strlen($characters)-1)];
   }
 
   return $string;
 
}

WordPress Front-end AJAX Pagination with Search and Sort

I posted a tutorial a year ago about creating an AJAX Pagination in your WordPress front-end, in this tutorial I would like to show how you can integrate a Search Filter and Sorting functionality. This tutorial also covers how you can go back to previous page state (pagination number and applied sorting filter) after visiting a post then clicking the back button in your browser.

wordpress-front-end-ajax-pagination-with-search-and-sort-screenshot
Demo

Step 1: Create a custom page in WordPress

  • Go to your Dashboard > Pages > Add New
  • Name the page anything you want, ex: My Posts
  • In your Dashboard > Settings > Permalinks, make sure Common Settings is set to Post Name
  • In your newly created page, copy the page slag: if you used “My Posts” as your page name, the slag would be my-posts
  • In your WordPress theme create a file called page-my-posts.php  Notice how we attached the slag to the “page-“, this will allow us to add custom scripts that will only apply to this specific page.
  • Go to your browser and navigate to your new page. ex. http://example.com/my-posts It should show you a blank white page, if not then you did something incorrectly.

Step 2: Working on our page

  • Open the new page and paste the following code template:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
<?php get_header(); ?>
    <div class  ="col-md-12 content">
        <div class = "content">
            <form class = "post-list">
                <input type = "hidden" value = "" />
            </form>
            <?php if ( have_posts() ) while ( have_posts() ) : the_post(); ?>
                <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                    <h1 class="entry-title"><?php the_title(); ?></h1>                                                                                 
                    <hr />
                    <article class="entry-content clear">
                        <?php the_content(); ?>
                    </article>
                </article>
            <?php endwhile; ?> 
           
            <article class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control post_search_text" placeholder="Enter a keyword">
                </div>
                <input type = "submit" value = "Search" class = "btn btn-success post_search_submit" />
            </article>
           
            <br class = "clear" />
           
            <script type="text/javascript">
            var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
           
            function cvf_load_all_posts(page, th_name, th_sort){
                $(".cvf_universal_container").html('<p><img src = "<?php bloginfo('template_url'); ?>/images/loading.gif" class = "loader" /></p>');
               
                var post_data = {
                    page: page,
                    search: $('.post_search_text').val(),
                    th_name: th_name,
                    th_sort: th_sort
                };
               
                $('form.post-list input').val(JSON.stringify(post_data));
               
                var data = {
                    action: "demo_load_my_posts",
                    data: JSON.parse($('form.post-list input').val())
                };
               
                $.post(ajaxurl, data, function(response) {
                    if($(".cvf_universal_container").html(response)){
                        $('.table-post-list th').each(function() {
                            // Append the button indicator
                            $(this).find('span.glyphicon').remove();   
                            if($(this).hasClass('active')){
                                if(JSON.parse($('form.post-list input').val()).th_sort == 'DESC'){
                                    $(this).append(' <span class="glyphicon glyphicon-chevron-down"></span>');
                                } else {
                                    $(this).append(' <span class="glyphicon glyphicon-chevron-up"></span>');
                                }
                            }
                        });
                    }
                });
            }
           
            jQuery(document).ready(function($) {                                                               
               
                // Initialize default item to sort and it's sort order
               
                // Check if our hidden form input is not empty, meaning it's not the first time viewing the page.
                if($('form.post-list input').val()){
                    // Submit hidden form input value to load previous page number
                    data = JSON.parse($('form.post-list input').val());
                    cvf_load_all_posts(data.page, data.th_name, data.th_sort);
                } else {
                    // Load first page
                    cvf_load_all_posts(1, 'post_title', 'ASC');
                }
               
                var th_active = $('.table-post-list th.active');
                var th_name = $(th_active).attr('id');
                var th_sort = $(th_active).hasClass('DESC') ? 'ASC': 'DESC';
                           
                // Search
                $('body').on('click', '.post_search_submit', function(){
                    cvf_load_all_posts(1, th_name, th_sort);
                });
                // Search when Enter Key is triggered
                $(".post_search_text").keyup(function (e) {
                    if (e.keyCode == 13) {
                        cvf_load_all_posts(1, th_name, th_sort);
                    }
                });
               
                // Pagination Clicks                   
                $('.cvf_universal_container .cvf-universal-pagination li.active').live('click',function(){
                    var page = $(this).attr('p');
                    var current_sort = $(th_active).hasClass('DESC') ? 'DESC': 'ASC';
                    cvf_load_all_posts(page, th_name, current_sort);               
                });

                // Sorting Clicks
                $('body').on('click', '.table-post-list th', function(e) {
                    e.preventDefault();                            
                    var th_name = $(this).attr('id');
                                                       
                    if(th_name){
                        // Remove all TH tags with an "active" class
                        if($('.table-post-list th').removeClass('active')) {
                            // Set "active" class to the clicked TH tag
                            $(this).addClass('active');
                        }
                        if(!$(this).hasClass('DESC')){
                            cvf_load_all_posts(1, th_name, 'DESC');
                            $(this).addClass('DESC');
                        } else {
                            cvf_load_all_posts(1, th_name, 'ASC');
                            $(this).removeClass('DESC');
                        }
                    }
                })
            });
            </script>
           
            <table class = "table table-striped table-post-list no-margin">
                <tr>
                    <th width = "25%" class = "active" id = "post_title"><u><a href = "#">Post Name</a></u></th>
                    <th width = "60%">Description</th>
                    <th width = "15%" id = "post_date"><u><a href = "#">Post Date</a></u></th>
                </tr>
            </table>
           
            <div class = "cvf_pag_loading no-padding">
                <div class = "cvf_universal_container">
                    <div class="cvf-universal-content"></div>
                </div>
            </div>
        </div>
    </div>
   
<?php get_footer(); ?>
  • Explanations are on the codes
  • This tutorial make use of bootstrap classes, so I suggest including bootstrap on the header part of your website:
1
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />

Step 3: Add the following PHP Code to your functions.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
add_action( 'wp_ajax_demo_load_my_posts', 'demo_load_my_posts' );
add_action( 'wp_ajax_nopriv_demo_load_my_posts', 'demo_load_my_posts' );
function demo_load_my_posts() {
       
    global $wpdb;
   
    $msg = '';
   
    if( isset( $_POST['data']['page'] ) ){
        // Always sanitize the posted fields to avoid SQL injections
        $page = sanitize_text_field($_POST['data']['page']); // The page we are currently at
        $name = sanitize_text_field($_POST['data']['th_name']); // The name of the column name we want to sort
        $sort = sanitize_text_field($_POST['data']['th_sort']); // The order of our sort (DESC or ASC)
        $cur_page = $page;
        $page -= 1;
        $per_page = 15; // Number of items to display per page
        $previous_btn = true;
        $next_btn = true;
        $first_btn = true;
        $last_btn = true;
        $start = $page * $per_page;
       
        // The table we are querying from  
        $posts = $wpdb->prefix . "posts";
       
        $where_search = '';
       
        // Check if there is a string inputted on the search box
        if( ! empty( $_POST['data']['search']) ){
            // If a string is inputted, include an additional query logic to our main query to filter the results
            $where_search = ' AND (post_title LIKE "%%' . $_POST['data']['search'] . '%%" OR post_content LIKE "%%' . $_POST['data']['search'] . '%%") ';
        }
       
        // Retrieve all the posts
        $all_posts = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM $posts WHERE post_type = 'post' AND post_status = 'publish' $where_search
            ORDER BY $name $sort LIMIT %d, %d"
, $start, $per_page ) );
       
        $count = $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(ID) FROM "
. $posts . " WHERE post_type = 'post' AND post_status = 'publish' $where_search", array() ) );
       
        // Check if our query returns anything.
        if( $all_posts ):
            $msg .= '<table class = "table table-striped table-hover table-file-list">';
           
            // Iterate thru each item
            foreach( $all_posts as $key => $post ):
                $msg .= '
                <tr>
                    <td width = "25%"><a href = "'
. get_permalink( $post->ID ) . '">' . $post->post_title . '</a></td>
                    <td width = "60%">'
. $post->post_excerpt . '</td>
                    <td width = "15%">'
. $post->post_date . '</td>
                </tr>'
;        
            endforeach;
           
            $msg .= '</table>';
       
        // If the query returns nothing, we throw an error message
        else:
            $msg .= '<p class = "bg-danger">No posts matching your search criteria were found.</p>';
           
        endif;

        $msg = "<div class='cvf-universal-content'>" . $msg . "</div><br class = 'clear' />";
       
        $no_of_paginations = ceil($count / $per_page);

        if ($cur_page >= 7) {
            $start_loop = $cur_page - 3;
            if ($no_of_paginations > $cur_page + 3)
                $end_loop = $cur_page + 3;
            else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
                $start_loop = $no_of_paginations - 6;
                $end_loop = $no_of_paginations;
            } else {
                $end_loop = $no_of_paginations;
            }
        } else {
            $start_loop = 1;
            if ($no_of_paginations > 7)
                $end_loop = 7;
            else
                $end_loop = $no_of_paginations;
        }
         
        $pag_container .= "
        <div class='cvf-universal-pagination'>
            <ul>"
;

        if ($first_btn && $cur_page > 1) {
            $pag_container .= "<li p='1' class='active'>First</li>";
        } else if ($first_btn) {
            $pag_container .= "<li p='1' class='inactive'>First</li>";
        }

        if ($previous_btn && $cur_page > 1) {
            $pre = $cur_page - 1;
            $pag_container .= "<li p='$pre' class='active'>Previous</li>";
        } else if ($previous_btn) {
            $pag_container .= "<li class='inactive'>Previous</li>";
        }
        for ($i = $start_loop; $i <= $end_loop; $i++) {

            if ($cur_page == $i)
                $pag_container .= "<li p='$i' class = 'selected' >{$i}</li>";
            else
                $pag_container .= "<li p='$i' class='active'>{$i}</li>";
        }
       
        if ($next_btn && $cur_page < $no_of_paginations) {
            $nex = $cur_page + 1;
            $pag_container .= "<li p='$nex' class='active'>Next</li>";
        } else if ($next_btn) {
            $pag_container .= "<li class='inactive'>Next</li>";
        }

        if ($last_btn && $cur_page < $no_of_paginations) {
            $pag_container .= "<li p='$no_of_paginations' class='active'>Last</li>";
        } else if ($last_btn) {
            $pag_container .= "<li p='$no_of_paginations' class='inactive'>Last</li>";
        }

        $pag_container = $pag_container . "
            </ul>
        </div>"
;
       
        echo
        '<div class = "cvf-pagination-content">' . $msg . '</div>' .
        '<div class = "cvf-pagination-nav">' . $pag_container . '</div>';
       
    }
   
    exit();
   
}

Step 4: Add some CSS styling to our Pagination navigation:

  • Include the following code in your style.css:
1
2
3
4
5
6
.cvf_pag_loading {padding: 20px; }
.cvf-universal-pagination ul {margin: 0; padding: 0;}
.cvf-universal-pagination ul li {display: inline; margin: 3px; padding: 4px 8px; background: #FFF; color: black; }
.cvf-universal-pagination ul li.active:hover {cursor: pointer; background: #1E8CBE; color: white; }
.cvf-universal-pagination ul li.inactive {background: #7E7E7E;}
.cvf-universal-pagination ul li.selected {background: #1E8CBE; color: white;}

Loading wp_editor via AJAX

I was loading the wp_editor thru AJAX, the editor shows up but the buttons for both Visual and Text Tab are missing. After some research I found an easy fix to this problem

The idea:

  1. Create a hidden div containing the wp_editor (place this code bellow the container where you are loading your ajax response)
  2. On AJAX success, get the contents of the hidden editor then append it to your AJAX response div container
  3. Reinitialize the editor: Remove the editor then add it back

Create a hidden div containing the wp_editor:

1
2
3
<div class="hidden-editor-container" style = "display: none;">
    <?php wp_editor('Default Content', 'tdmessagereply'); ?>
</div>

Server Side Structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static function cvf_td_veiw_frontend_message() {
   
    if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'veiw_frontend_message' ) {
       
        $display .= '
        <article>
            <div class = "form-group">
                <label>'
. __('Reply', 'my-text-domain') . ':</label><br />
                <div class = "bg-primary hidden-reply-editor"></div>
            </div> 
            <input type = "submit" value = "'
. __('Send', 'my-text-domain') . '" class = "btn btn-primary reply_to_conversation" />
        </article>'
;
       
        echo $display;
    }
    exit();
}
add_action('wp_ajax_cvf_td_veiw_frontend_message', 'cvf_td_veiw_frontend_message') );
add_action('wp_ajax_nopriv_cvf_td_veiw_frontend_message', 'cvf_td_veiw_frontend_message') );

AJAX Request Structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var data = {
    'action': 'cvf_td_veiw_frontend_message',
    'cvf_action': 'veiw_frontend_message'
};

$.post(global.ajax_url, data, function(response) {
   
    // After the response has been appended to the our front-end...
    if($(".cvf_td_conversation_messages").html(response)){
       
        // Get the contents of the hidden wp_editor
        reply_editor = $('.hidden-editor-container').contents();
       
        // Append the contents of the hidden wp_editor to div container
        $('.hidden-reply-editor').append( reply_editor );
       
        // Reinitialize the editor: Remove the editor then add it back
        tinymce.execCommand( 'mceRemoveEditor', false, 'tdmessagereply' );
        tinymce.execCommand( 'mceAddEditor', false, 'tdmessagereply' );
    }
});

Hope this tutorial makes a better day for someone 🙂

Customizing WP Editor in WordPress

I was recently working on a freelance project and one of the tasks is to replace all the textareas in the front-end to the default WYSIWYG editor of WordPress. They also wanted to use a limited number of buttons for both Visual and Text tab. After some research, I was able to come up with my own customized editor that works perfectly in both front-end and back-end:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php      
$content = 'The content to be loaded';
$editor_id = 'my_editor_id';
$settings =   array(
    'wpautop' => true, // enable auto paragraph?
    'media_buttons' => false, // show media buttons?
    'textarea_name' => $editor_id, // id of the target textarea
    'textarea_rows' => get_option('default_post_edit_rows', 10), // This is equivalent to rows="" in HTML
    'tabindex' => '',
    'editor_css' => '', //  additional styles for Visual and Text editor,
    'editor_class' => '', // sdditional classes to be added to the editor
    'teeny' => true, // show minimal editor
    'dfw' => false, // replace the default fullscreen with DFW
    'tinymce' => array(
        // Items for the Visual Tab
        'toolbar1'=> 'bold,italic,underline,bullist,numlist,link,unlink,forecolor,undo,redo,',
    ),
    'quicktags' => array(
        // Items for the Text Tab
        'buttons' => 'strong,em,underline,ul,ol,li,link,code'
    )
);
wp_editor( $content, $editor_id, $settings );                      
?>

For the complete list of quicktags and tinymce buttons, please refer to the following links:
https://codex.wordpress.org/Quicktags_API
http://www.tinymce.com/wiki.php/TinyMCE3x:Buttons/controls

Function based:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function cvf_content_editor( $textarea_id, $default_content ) {
   
    $content = 'The content to be loaded';
    $editor_id = 'my_editor_id';
    $settings =   array(
        'wpautop' => true, // enable auto paragraph?
        'media_buttons' => false, // show media buttons?
        'textarea_name' => $editor_id, // id of the target textarea
        'textarea_rows' => get_option('default_post_edit_rows', 10), // This is equivalent to rows="" in HTML
        'tabindex' => '',
        'editor_css' => '', //  additional styles for Visual and Text editor,
        'editor_class' => '', // sdditional classes to be added to the editor
        'teeny' => true, // show minimal editor
        'dfw' => false, // replace the default fullscreen with DFW
        'tinymce' => array(
            // Items for the Visual Tab
            'toolbar1'=> 'bold,italic,underline,bullist,numlist,link,unlink,forecolor,undo,redo,',
        ),
        'quicktags' => array(
            // Items for the Text Tab
            'buttons' => 'strong,em,underline,ul,ol,li,link,code'
        )
    );
   
    wp_editor( $content, $editor_id, $settings );
   
}

Create Posts from WordPress Front-End

In this tutorial I am going to discuss how to insert posts via the front end of your WordPress blog. This tutorial will also cover some validation techniques, setting the post as “pending”, and email notification when a new post is created. So let’s begin!

First, you would want to create a new page, let’s call it “Add Topic” with a slug of “add-topic”. Make sure that your permalink settings it set to “/%postname%/”

Next is to Log on to your FTP then navigate to your current theme’s folder and create a PHP file: page-add-topic.php. Test this page, it should show a blank white screen.

Now we are ready to add some code to this page, open page-add-topic.php using your favorite code editor. Create the header and the footer functions:

1
2
3
<?php get_header(); ?>
// HTML code goes here
<?php get_footer(); ?>

Create the form

Within the get_header and get_footer function, add the code bellow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class = "content">
    <div class = "topic-submit">
        <?php echo $msg ? $msg : ''; ?>
    </div>
    <form method = "post">
        <div class = "form-group">
            <label class = "" for = "topic_title"><?php _e('Topic Title'); ?>*</label>
            <input type = "text" name = "topic_title" class = "form-control" value = "<?php echo $_POST['topic_title'] ? $_POST['topic_title'] : ''; ?>" />
        </div>
        <div class = "form-group">
            <label class = "" for = "topic_description"><?php _e('Topic Description'); ?>*</label>
            <textarea class = "form-control" name = "topic_description" rows= "15" ><?php echo $_POST['topic_description'] ? $_POST['topic_description'] : ''; ?></textarea>
        </div>
        <div class = "form-group">
            <label class = "" for = "topic_email"><?php _e('Your Email'); ?>*</label>
            <input type = "text" name = "topic_email" class = "form-control" value = "<?php echo $_POST['topic_email'] ? $_POST['topic_email'] : ''; ?>" />
        </div>
        <br class = "clearfix" />
        <input type = "submit" name = "topic_submit" class = "btn btn-default" />
    </form>
</div>

The “topic-submit” div container is where we will be outputting any error or success messages

Ok now that we have our form, we need to code the server side. Just before the get_header(); function, insert the following PHP code:

1
2
3
4
if (isset($_POST['topic_submit'])) {
// Sanitize the form fields here
// Create validation here
}

Sanitizing the Form Fields

To be sure that our visitors are submitting valid data and not some script or anything else that can harm our site, we need to sanitize the form fields:

1
2
3
4
// Sanitize the form fields here
$topic_title = sanitize_text_field($_POST['topic_title']);
$topic_description = wp_strip_all_tags($_POST['topic_description']);
$topic_email = sanitize_text_field($_POST['topic_email']);

We are simply passing all posted data to the sanitize_text_field function. The wp_strip_all_tags function sanitizes and strip all html tags from any input data that is why we no longer have to use sanitize_text_field with it.

Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create validation here
if( empty( $topic_title ) ) {
    $msg .= '<p class = "post-error">Please enter a title</p>';
} else if ( empty( $topic_description )) {
    $msg .= '<p class = "post-error">Please enter a description</p>';
} else if ( empty( $topic_email )) {
    $msg .= '<p class = "post-error">Please enter your email address</p>';
} else if ( ! is_email( $topic_email )) {
    $msg .= '<p class = "post-error">Please enter a valid email</p>';
} else {
    // Insert the page to the database
   
    // Save a copy of the user's email to our database
   
    // Send an email notification to the admin email
}

In the code above, we are simply validating the input fields if they are empty and if the email address is of the right format.

You can test the form right now by navigating to the page ( ex. http://example.com/add-topic ) to see the form and the validation in action.

Inserting to the Database

Add the following code on the ELSE block:

1
2
3
4
5
6
7
8
9
10
// Insert the page to the database
$topic_data = array(
    'post_title'    =>  wp_strip_all_tags( $topic_title ),
    'post_content'  =>  implode( "\n", array_map( 'sanitize_text_field', explode( "\n", $topic_description ) ) ),
    'post_status'   =>  'pending',
    'post_author'   =>  1,
    'post_type'     => 'post'
);

$post_id = wp_insert_post( $topic_data );

Notice that we are setting post_status to “pending”, that way the admin can review the post before it appears on the site. Change the “post_type” value if you want to associate this new post with another post type. Make sure to replace “post_author” value with the admin user ID. If you are dealing with logged in users then it’s best to use the global $current_user variable then set the post author value to: “$current_user->ID” without the double quotes.

We then add the codes that will save the user’s email to our database for later use and also notify the admin email about the new post that is awaiting his/her approval:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if( $post_id ){
    // Save a copy of the user's email to our database, you can remove this if you are dealing with logged-in users.
    update_post_meta( $post_id, 'topic_author', $topic_email );
   
    // Send an email notification to the admin email
    $from = 'info@example.com';
    $headers = 'From: Example.com <"' . $from . '">';
    $subject = "New topic awaiting approval";
   
    ob_start();
    ?>
   
    <p>A new topic is awaiting your approval, <a href = "<?php echo get_permalink( $post_id ); ?>">click here to review the topic</a></p>
    <p><br /></p>
    <p><a href = "<?php echo home_url(); ?>">carlofontanos.com</a></p>
   
    <?php

    $message = ob_get_contents();
   
    ob_end_clean();

    wp_mail(get_option('admin_email'), $subject, $message, $headers);
   
    $msg = '<p class = "post-success">Your topic has been submitted and is now awaiting for approval.</p>';
}

Feel Free to change and design the HTML contents above.

Test the Form

You now have a front-end form that includes validation and email notification, go ahead and test it!. You can style the error or success colors to have green and red background, your choice!.

Challange

Use JavaScript to validate the form fields then use AJAX to insert the data to the database.

Simple Posts Pagination in WordPress

If ypu have been following my WordPress Tutorials, I made two posts where I discussed how you can setup a pagination that loads the data from the server using AJAX. In this tutorial we are going to focus on creating a very simple pagination without AJAX and by just relying on PHP and WordPress functions. So let’s get started!

This simple pagination includes buttons for “Next”, “Prev”, “First”, and “Last”. You can also configure how many number bullets to show on screen.

Here’s the function that will setup the posts in a paginated view:

Comments are included so read on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
function display_posts_pagination() {
   
    global $wpdb, $post;

    // Set default variables
    $pagination_content = '';
    $current_page_url = get_permalink( $post->ID );
    $page_number = isset( $_GET[ 'pageno' ] ) ? $_GET[ 'pageno' ] : 1;
   
    // Sanitize the received page  
    $page = sanitize_text_field( $page_number );
    $cur_page = $page;
    $page -= 1;
    // Set the number of results to display
    $per_page = 5;
    $previous_btn = true;
    $next_btn = true;
    $first_btn = true;
    $last_btn = true;
    $start = $page * $per_page;

    // Set the table where we will be querying data
    $table_name = $wpdb->prefix . 'posts';

    // Query the necessary posts
    $all_blog_posts = $wpdb->get_results( $wpdb->prepare( '
            SELECT * FROM '
. $table_name . ' WHERE post_type = "post" AND post_status = "publish" ORDER BY post_date DESC LIMIT %d, %d', $start, $per_page ) );

    // At the same time, count the number of queried posts
    $count = $wpdb->get_var( $wpdb->prepare( '
            SELECT COUNT(ID) FROM '
. $table_name . ' WHERE post_type = "post" AND post_status = "publish"', array() ) );

    /**
     * Use WP_Query:
     *
      $all_blog_posts = new WP_Query(
      array(
      'post_type'         => 'post',
      'post_status '      => 'publish',
      'orderby'           => 'post_date',
      'order'             => 'DESC',
      'posts_per_page'    => $per_page,
      'offset'            => $start
      )
      );

      $count = new WP_Query(
      array(
      'post_type'         => 'post',
      'post_status '      => 'publish',
      'posts_per_page'    => -1
      )
      );
     */

     
    // Loop into all the posts
    foreach ( $all_blog_posts as $key => $post ):

        // Set the desired output into a variable
        $pagination_content .= '
            <div class = "col-md-12">      
                <h2><a href="'
. get_permalink( $post->ID ) . '">' . $post->post_title . '</a></h2>
                <p>'
. $post->post_excerpt . '</p>
            </div>'
;

    endforeach;

    // Optional, wrap the output into a container
    $pagination_content = '<div class="cvf-universal-content">' . $pagination_content . '</div><br class = "clear" />';

    // This is where the magic happens
    $no_of_paginations = ceil( $count / $per_page );

    if ( $cur_page >= 7 ) {
        $start_loop = $cur_page - 3;
        if ( $no_of_paginations > $cur_page + 3 )
            $end_loop = $cur_page + 3;
        else if ( $cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6 ) {
            $start_loop = $no_of_paginations - 6;
            $end_loop = $no_of_paginations;
        } else {
            $end_loop = $no_of_paginations;
        }
    } else {
        $start_loop = 1;
        if ( $no_of_paginations > 7 )
            $end_loop = 7;
        else
            $end_loop = $no_of_paginations;
    }

    // Pagination Buttons logic    
    $pagination_nav .= '
        <div class="pagination-container">
            <ul>'
;

    if ( $first_btn && $cur_page > 1 ) {
        $pagination_nav .= '<li class="active"><a href = "' . $current_page_url . '?pageno=1">First</a></li>';
    } else if ( $first_btn ) {
        $pagination_nav .= '<li class="inactive">First</li>';
    }

    if ( $previous_btn && $cur_page > 1 ) {
        $pre = $cur_page - 1;
        $pagination_nav .= '<li class="active"><a href = "' . $current_page_url . '?pageno=' . $pre . '">Previous</a></li>';
    } else if ( $previous_btn ) {
        $pagination_nav .= '<li class="inactive">Previous</li>';
    }
    for ( $i = $start_loop; $i <= $end_loop; $i ++  ) {

        if ( $cur_page == $i )
            $pagination_nav .= '<li class = "selected">' . $i . '</li>';
        else
            $pagination_nav .= '<li class="active"><a href = "' . $current_page_url . '?pageno=' . $i . '">' . $i . '</a></li>';
    }

    if ( $next_btn && $cur_page < $no_of_paginations ) {
        $nex = $cur_page + 1;
        $pagination_nav .= '<li class="active"><a href = "' . $current_page_url . '?pageno=' . $nex . '">Next</a></li>';
    } else if ( $next_btn ) {
        $pagination_nav .= '<li class="inactive">Next</li>';
    }

    if ( $last_btn && $cur_page < $no_of_paginations ) {
        $pagination_nav .= '<li class="active"><a href = "' . $current_page_url . '?pageno=' . $no_of_paginations . '">Last</a></li>';
    } else if ( $last_btn ) {
        $pagination_nav .= '<li class="inactive">Last</li>';
    }

    $pagination_nav = $pagination_nav . '
            </ul>
        </div>'
;
   
    $output = '
        <div>'
. $pagination_content . '</div>
        <div>'
. $pagination_nav . '</div>
    '
;
   
    return $output;
}

Simply copy the above code into your functions.php

Then in your page, simply call the function:

1
2
3
<div class = "content">
    <?php echo display_posts_pagination(); ?>
</div>

The function does not accept any parameters, but feel free to add some as you wish!

Styling

Here’s a ready made style for your navigation buttons:

1
2
3
4
5
6
7
.pagination-container  { margin-top: 15px; }
.pagination-container ul { margin: 0; padding: 0; }
.pagination-container ul li { display: inline; background: #FFF; color: black; }
.pagination-container ul li.active a:hover { background: #1E8CBE; color: white; text-decoration: none; }
.pagination-container ul li.inactive { background: #7E7E7E; }
.pagination-container ul li.selected { background: #1E8CBE; color: white; }
.pagination-container ul li a, .pagination-container ul li.inactive, .pagination-container ul li.selected { margin: 3px; padding: 4px 8px; }

Overriding WooCommerce archive-product.php

Recently I was working on a WooCommerce project and was having problems customizing the Products Page, I can’t seem to override the archive-product.php. Later I found out that the problem is due to the function woocommerce_content() outputting the wrong page for archive content.

To get around it, replace

1
<?php woocommerce_content() ?>

in woocommerce.php with:

1
2
3
4
5
<?php if ( is_singular( 'product' ) ): ?>
    <?php woocommerce_content(); ?>
<?php else: ?>
    <?php woocommerce_get_template( 'archive-product.php' ); ?>
<?php endif; ?>

Disable Update Notifications in WordPress Admin

Sometimes WordPress Update Notifications are annoying, they usually show up in red circles or a notice message on the header part of your Admin Panel. They usually do not disappear not until your click the update button, annoying? – Let’s fix that.

The use of the bellow codes is not advisable as it prevents maintenance on plugins, themes, and core codes of WordPress therefore making your website vulnerable to hackers.

Disable Plugin Notifications:

1
2
remove_action('load-update-core.php','wp_update_plugins');
add_filter('pre_site_transient_update_plugins','__return_null');

 

Disable all WordPress Notifications(Core, Themes and Plugins):

1
2
3
4
5
6
7
8
9
10
11
function cvf_remove_wp_core_updates(){
    global $wp_version;
    return(object) array('last_checked' => time(),'version_checked' => $wp_version);
}

// Core Notifications
add_filter('pre_site_transient_update_core','cvf_remove_wp_core_updates');
// Plugin Notifications
add_filter('pre_site_transient_update_plugins','cvf_remove_wp_core_updates');
// Theme Notifications
add_filter('pre_site_transient_update_themes','cvf_remove_wp_core_updates');

To completely disable all types of automatic updates, core or otherwise, add the following to your wp-config.php file:

1
define( 'AUTOMATIC_UPDATER_DISABLED', true );

Creating a Facebook Thumbnail Field in your WordPress Post or Page

When sharing a post or page – one of the reasons why you get no thumbnail, or unrelated thumbnail is because facebook’s script is not smart enough. On each page load, it searches for all image tags and display the ones that it finds useful. Sometimes the script times out without even loading the right image. To get around this issue, facebook recommends developers to tell the script exactly which image to pick by adding a meta tag in the header. The meta tag looks something like this:

1
<meta property="og:image" content="https://carlofontanos.com/wp-content/uploads/2015/01/access-denied.jpg"/>

This works perfectly when you are running a static site, but for a dynamic platform like WordPress, we need to tweak the code a little bit to make it work.

In this tutorial, I am going to show you how to create a field where you can enter a custom image that you wish to use as your cover image when sharing your posts/pages on Social Sites such as Facebook. This tutorial will also cover how you can generate a meta tag for each post/page in WordPress.

Let’s Start by creating a New Post in your WordPress admin.

Then click on “Screen Options” button found on the top right hand corner in your New Post screen.

Check the “Custom Fields” to enable it within your editing page.

facebook-meta-image1

Just bellow the Excerpt field or Content field, a new field called “Custom Fields” will appear.

On the “Name” field, enter:

1
social_media_thumbnail_preview

then for the “Value” field, enter the link of the image that you want to appear as a thumbnail when you share your post on facebook. Example:

1
https://carlofontanos.com/wp-content/uploads/2015/01/access-denied.jpg

Just to be sure that you are doing it all correctly, kindly refer to the screenshot bellow:

facebook-meta-image2

What we did above is we simply created a new custom field with a meta_key “social_media_thumbnail_preview” and a meta_value containing the value of the image link that you just entered. Later on, we are going to query the value of this field and programatically create a new meta tag which will be identified by facebook as the post thumbnail.

Next is to hit the “Publish” button.

The next time you want to create a thumbnail for a post or page – there is no need to re-enter the “Name” field, it was already registered by WordPress as a default field. Refer to the screenshot bellow:

facebook-meta-image3

Next step is we need to create a function that will register an image meta tag and will then be scraped by facebook.

Open your functions.php and simply add the block of code bellow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
add_action('wp_head', 'cvf_social_media_thumbnail_preview');   
function cvf_social_media_thumbnail_preview() {
   
    global $post;
   
    $social_media_thumbnail = get_post_meta($post->ID, 'social_media_thumbnail_preview', true);
   
    if($social_media_thumbnail){
        echo '
        <meta property="og:image" content="'
. $social_media_thumbnail . '"/>
        <meta property="og:image:width" content="1024">'
;  
    }
   
}

What the above code does is it gets the ID of the post that is currently being viewed, then gets the meta_value of the meta_key “social_media_thumbnail_preview” from the database. If an Image URL exists, it will then generate the meta tags on the header part of the webpage.

If you try to view the source code of your web page, you should be able to locate the code similar to the one bellow

1
2
<meta property="og:image" content="https://carlofontanos.com/wp-content/uploads/2015/01/access-denied.jpg"/>
<meta property="og:image:width" content="1024">

You can also define your own image width and height in your meta tags:

Width:

1
<meta property="og:image:width" content="1024">

Height:

1
<meta property="og:image:height" content="1024">

When you share your post/page on facebook, it should show the image you selected as its cover image or banner.

facebook-meta-image5

If the thumbnail is not showing the correct image or not showing any thumbnail at all, you can tell facebook to rebuild your link by using a simple tool “Object Debugger” developed by facebook: https://developers.facebook.com/tools/debug/og/object/ then enter the link of the post/page you wish to be re-generated:

facebook-meta-image4

Refresh your page, it should now show you the correct cover image when you share your post/page

Build Your Own Ajax Contact Form in WordPress

Don’t have much background in PHP, AJAX and Javascript? that’s ok, you can still use this tutorial.

Today I am going to show you how you can build an AJAX Contact Form for your WordPress blog.

Start by creating a PAGE in your WP backend, name the page “contact”. Then in your theme directory create a file with similar contents to your page.php, name the file: page-contact.php . In your Settings->Permalinks, make sure that the “Post name” is selected.

Now open page-contact.php then add the following code: (it should be within the loop)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="contact_form">
   
    <div id="result"></div>
   
    <div class="form-group">
        <label for = "name">Name</label>
        <input type="text" name = "name" class = "form-control input-name" placeholder="Enter Your Name" />
    </div>
    <div class="form-group">
        <label for = "email">Email</label>
        <input type="email" name = "email" class = "form-control input-email" placeholder="Enter Your Email" />
    </div> 
    <div class="form-group">
        <label for = "message">Message</label> 
        <textarea name = "message" class = "form-control input-message" rows="4" placeholder="Enter Your Message"></textarea><br />
    </div>
   
    <button class = "btn btn-primary submit">Submit</button>
   
</div>

Just bellow the above code, add the javascript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script type="text/javascript" >
function cvf_form_validate(element) {
    $('html, body').animate({scrollTop: $(element).offset().top-100}, 150);
    element.effect("highlight", { color: "#F2DEDE" }, 1500);
    element.parent().effect('shake');
}
   
jQuery(document).ready(function($) {

    $('body').on('click', '.submit', function() {
       
        if($('.input-name').val() === '') {
            cvf_form_validate($('.input-name'));
           
        } else if($('.input-email').val() === '') {            
            cvf_form_validate($('.input-email'));
           
        } else if($('.input-message').val() === '') {              
            cvf_form_validate($('.input-message'));
           
        } else {
            var data = {
                'action': 'cvf_send_message',
                'name': $('.input-name').val(),
                'email': $('.input-email').val(),
                'message': $('.input-message').val()
            };
           
            var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
            $.post(ajaxurl, data, function(response) {
                if(response === 'success'){
                    alert('Message Sent Successfully!');
                    $('.input-name').val(''); $('.input-email').val(''); $('.input-message').val('');
                }
            });
        }
    });
});
</script>

In our code above, we are simply sending the POST request to the admin-ajax.php file of WordPress. It parses your data by looking for the ‘action’ variable which in our case is: ‘cvf_send_message’. The data is passed to the PHP function: ‘wp_ajax_cvf_send_message’. So next step is we need to create the call back function that will handle the AJAX post request.

In your functions.php, add the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * @ Author: Carl Victor Fontanos.
 * @ Class: CVF_Posts
 *
 */

 
add_action('wp_ajax_cvf_send_message', array('CVF_Posts', 'cvf_send_message') );
add_action('wp_ajax_nopriv_cvf_send_message', array('CVF_Posts', 'cvf_send_message') );
add_filter('wp_mail_content_type', array('CVF_Posts', 'cvf_mail_content_type') );

class CVF_Posts {
   
    public static function cvf_send_message() {
       
        if (isset($_POST['message'])) {
           
            $to = get_option('admin_email');
            $headers = 'From: ' . $_POST['name'] . ' <"' . $_POST['email'] . '">';
            $subject = "carlofontanos.com | New Message from " . $_POST['name'];
           
            ob_start();
           
            echo '
                <h2>Message:</h2>'
.
                wpautop($_POST['message']) . '
                <br />
                --
                <p><a href = "'
. home_url() . '">www.carlofontanos.com</a></p>
            '
;
           
            $message = ob_get_contents();
           
            ob_end_clean();

            $mail = wp_mail($to, $subject, $message, $headers);
           
            if($mail){
                echo 'success';
            }
        }
       
        exit();
       
    }
       
    public static function cvf_mail_content_type() {
        return "text/html";
    }
}

In the above code we used ob_start(); , ob_get_contents(); and ob_end_clean(); to allow our email message to read HTML elements instead of printing them as is.

About styling the email message: There is no way you can get your email message to read external styles, so what you can only do is to use inline CSS. Ex:

1
<td style = "background: red; color: white; width: 50%">Text</td>

To learn more about styling emails, I created a separate tutorial: Generate Emails with HTML & CSS in WordPress

The tutorial code should work out of the box. If you encountered any problems, there is comment section bellow where you can post your issues or concerns.

Featured Image Shortcode [Plugin]

featured-image-shortcode

Usage:

1
[featured-image size = "large" class = "featured-image" title = "something" alt = "image"]

Available attributes:
1. class
2. title
3. alt

Available sizes:
1. thumbnail
2. medium
3. large
4. full

Give away code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
add_shortcode('featured-image', array('Featured_image_shortcode', 'cvf_post_thumbnail_shortcode') );

class Featured_image_shortcode {

    public static function cvf_post_thumbnail_shortcode($atts, $content = '') {
       
        $img_attr = '';
        $atts['class'] = '';
       
        $img_attr = array(
            'class' => $atts['class'],
            'alt'   => trim(strip_tags($atts['alt'])),
            'title' => trim(strip_tags($atts['title']))
        );
       
        if(!$atts['size']){
            $atts['size'] = 'thumbnail';
        }
       
        return get_the_post_thumbnail(null, $atts['size'], $img_attr);
    }
}

Overriding Woocommerce CSS

Add the the block of code bellow into your functions.php

1
2
3
4
5
6
7
8
9
add_action('wp_enqueue_scripts', 'cvf_st_load_custom_css_before_main') );

function cvf_st_load_custom_css_before_main() {
         
    if (class_exists('woocommerce')){
        wp_enqueue_style('my-woocommerce-style', get_template_directory_uri().'/css/woocommerce.css', array('woocommerce-general'));
    }
   
}

Make sure that you change “get_template_directory_uri().’/css/woocommerce.css'” to the path of your custom style sheet.

In essence, what we did is we changed the order that your styles load so that your theme’s stylesheet is loaded after the WooCommerce stylesheet. i.e. it is enqueued at a higher priority.

Post Class for Resume Database

Useful functions for building a resume database in WordPress

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
<?php
/**
 * @ Author: Carl Victor Fontanos.
 * @ Class: NGP_Posts
 *
 */



add_action('wp_ajax_cvf_ngp_frontend_display_resumes', array('NGP_Posts', 'cvf_ngp_frontend_display_resumes') );
add_action('wp_ajax_nopriv_cvf_ngp_frontend_display_resumes', array('NGP_Posts', 'cvf_ngp_frontend_display_resumes') );

add_action('wp_ajax_cvf_ngp_create_new_resume', array('NGP_Posts', 'cvf_ngp_create_new_resume') );
add_action('wp_ajax_nopriv_cvf_ngp_create_new_resume', array('NGP_Posts', 'cvf_ngp_create_new_resume') );

add_action('wp_ajax_cvf_ngp_create_new_resume_form', array('NGP_Posts', 'cvf_ngp_create_new_resume_form') );
add_action('wp_ajax_nopriv_cvf_ngp_create_new_resume_form', array('NGP_Posts', 'cvf_ngp_create_new_resume_form') );

add_action('wp_ajax_cvf_ngp_pagination_load_resumes', array('NGP_Posts', 'cvf_ngp_pagination_load_resumes') );
add_action('wp_ajax_nopriv_cvf_ngp_pagination_load_resumes', array('NGP_Posts', 'cvf_ngp_pagination_load_resumes') );

add_action('wp_ajax_cvf_ngp_preveiw_resume', array('NGP_Posts', 'cvf_ngp_preveiw_resume') );
add_action('wp_ajax_nopriv_cvf_ngp_preveiw_resume', array('NGP_Posts', 'cvf_ngp_preveiw_resume') );

add_action('wp_ajax_cvf_ngp_edit_resume', array('NGP_Posts', 'cvf_ngp_edit_resume') );
add_action('wp_ajax_nopriv_cvf_ngp_edit_resume', array('NGP_Posts', 'cvf_ngp_edit_resume') );

add_action('wp_ajax_cvf_ngp_update_resume', array('NGP_Posts', 'cvf_ngp_update_resume') );
add_action('wp_ajax_nopriv_cvf_ngp_update_resume', array('NGP_Posts', 'cvf_ngp_update_resume') );

add_action('wp_ajax_cvf_ngp_delete_attachment', array('NGP_Posts', 'cvf_ngp_delete_attachment') );
add_action('wp_ajax_nopriv_cvf_ngp_delete_attachment', array('NGP_Posts', 'cvf_ngp_delete_attachment') );

add_action('wp_ajax_cvf_ngp_delete_resume', array('NGP_Posts', 'cvf_ngp_delete_resume') );
add_action('wp_ajax_nopriv_cvf_ngp_delete_resume', array('NGP_Posts', 'cvf_ngp_delete_resume') );

add_action('before_delete_post', array('NGP_Posts', 'cvf_ngp_delete_all_resume_attachments') );

add_action('init', array('NGP_Posts', 'cvf_ngp_register_resume_post_type') );

add_action('init', array('NGP_Posts', 'cvf_ngp_register_blog_post_type') );
add_action('init', array('NGP_Posts', 'cvf_ngp_create_blog_categories') );

add_action('generate_rewrite_rules', array('NGP_Posts', 'cvf_ngp_blog_datearchives_rewrite_rules') );

add_action( 'admin_menu', array('NGP_Posts', 'cvf_ngp_remove_menus') );

add_filter ('wp_mail_content_type', array('NGP_Posts', 'cvf_ngp_mail_content_type') );



class NGP_Posts {

    public function __construct() {}

    public static function cvf_ngp_get_category_posts($category_slug, $limit) {

        global $post;

        $args = array(
            'numberposts' => $limit,
            'category_name' => $category_slug,
            'order' => 'ASC',
            'post_status' => 'publish'
        );

        $posts = get_posts( $args );

        return $posts;

    }

    public static function cvf_ngp_get_forums($limit, $type){

        if($type = 'category'){
            $post_parent = 'post_parent = 0';
        } else {
            $post_parent = 'post_parent > 0';
        }
        global $wpdb;

        $table = $wpdb->prefix . 'posts';
        $forums = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM $table WHERE post_type = 'forum' AND $post_parent
            ORDER BY post_title ASC LIMIT %d
            "
, $limit ));

        return $forums;
    }

    public static function cvf_ngp_register_resume_post_type() {

        $labels = array(
            'name'                  => _x('NGP Resumes', 'post type general name'),
            'singular_name'         => _x('Resume', 'post type singular name'),
            'all_items'             => _x('All Resumes', 'post type name'),
            'add_new'               => _x('Create New Resume', 'new post'),
            'add_new_item'          => __('Create New Resume'),
            'edit_item'             => __('Edit Resume'),
            'new_item'              => __('New Resume'),
            'view_item'             => __('View Resume'),
            'search_items'          => __('Search Resumes'),
            'not_found'             => __('No resumes found'),
            'not_found_in_trash'    => __('No resumes found in the Trash'),
            'parent_item_colon'     => ''
        );
        $args = array(
            'labels'                => $labels,
            'public'                => true,
            'publicly_queryable'    => true,
            'show_ui'               => true,
            'query_var'             => true,
            'rewrite'               => true,
            'capability_type'       => 'post',
            'hierarchical'          => false,
            'menu_position'         => 8,
            'supports'              => array('title','editor','thumbnail')
        );

        register_post_type( 'resume' , $args );
    }
   
    public static function cvf_ngp_register_blog_post_type() {

        $labels = array(
            'name'                  => _x('NGP Blogs', 'post type general name'),
            'singular_name'         => _x('Blog', 'post type singular name'),
            'all_items'             => _x('All Blogs', 'post type name'),
            'add_new'               => _x('Create New Blog', 'new post'),
            'add_new_item'          => __('Create New Blog'),
            'edit_item'             => __('Edit Blog'),
            'new_item'              => __('New Blog'),
            'view_item'             => __('View Blog'),
            'search_items'          => __('Search Blogs'),
            'not_found'             => __('No blogs found'),
            'not_found_in_trash'    => __('No blogs found in the Trash'),
            'parent_item_colon'     => ''
        );
        $args = array(
            'labels'                => $labels,
            'public'                => true,
            'publicly_queryable'    => true,
            'show_ui'               => true,
            'query_var'             => true,
            'rewrite'               => true,
            'capability_type'       => 'post',
            'hierarchical'          => false,
            'menu_position'         => 8,
            'has_archive'           => true,
            'supports'              => array('title','editor','thumbnail')
        );

        register_post_type( 'blog' , $args );
    }
   
    public static function cvf_ngp_create_blog_categories() {
        $labels = array(
            'name'              => _x( 'Blog Categories', 'taxonomy general name' ),
            'singular_name'     => _x( 'Blog Category', 'taxonomy singular name' ),
            'search_items'      => __( 'Search Blog Categories' ),
            'all_items'         => __( 'All Blog Categories' ),
            'parent_item'       => __( 'Parent Blog Category' ),
            'parent_item_colon' => __( 'Parent Blog Category:' ),
            'edit_item'         => __( 'Edit Blog Category' ),
            'update_item'       => __( 'Update Blog Category' ),
            'add_new_item'      => __( 'Create New Blog Category' ),
            'new_item_name'     => __( 'New Blog Category Name' ),
            'menu_name'         => __( 'Categories' ),
        );

        $args = array(
            'hierarchical'      => true,
            'labels'            => $labels,
            'show_ui'           => true,
            'show_admin_column' => true,
            'query_var'         => true,
            'rewrite'           => array( 'slug' => 'blog-category' ),
        );

        register_taxonomy( 'blog_categories', array( 'blog' ), $args );
    }

    public static function cvf_ngp_frontend_display_resumes() {

        global $wpdb, $current_user;
        $msg = '';

        if(isset($_POST['page'])){
            $page = sanitize_text_field($_POST['page']);
            $cur_page = $page;
            $page -= 1;
            $per_page = 10;
            $previous_btn = true;
            $next_btn = true;
            $first_btn = true;
            $last_btn = true;
            $start = $page * $per_page;

            $posts = $wpdb->prefix . "posts";
            $postmeta = $wpdb->prefix . "postmeta";

            $where_name = ''; $where_experience = ''; $where_education = ''; $where_country = ''; $where_industry = '';

            if(!empty($_POST['ngp_name'])){
                $where_name = ' AND (p.post_title LIKE "%%' . $_POST['ngp_name'] . '%%") ';
            }
            if(!empty($_POST['ngp_industry'])){
                $where_industry = ' AND (m1.meta_value LIKE "%%' . $_POST['ngp_industry'] . '%%") ';
            }
            if(!empty($_POST['ngp_experience'])){
                $where_experience = ' AND (m2.meta_value LIKE "%%' . $_POST['ngp_experience'] . '%%") ';
            }
            if(!empty($_POST['ngp_country'])){
                $where_country = ' AND (m3.meta_value LIKE "%%' . $_POST['ngp_country'] . '%%") ';
            }
            if(!empty($_POST['ngp_education'])){
                $where_education = ' AND (m4.meta_value LIKE "%%' . $_POST['ngp_education'] . '%%") ';
            }

            $joins = '
                LEFT JOIN '
. $postmeta . ' m1 ON p.ID = m1.post_id AND m1.meta_key = "ngp_industry"
                LEFT JOIN '
. $postmeta . ' m2 ON p.ID = m2.post_id AND m2.meta_key = "ngp_experience"
                LEFT JOIN '
. $postmeta . ' m3 ON p.ID = m3.post_id AND m3.meta_key = "ngp_country"
                LEFT JOIN '
. $postmeta . ' m4 ON p.ID = m4.post_id AND m4.meta_key = "ngp_education" ';

            $all_user_resumes = $wpdb->get_results($wpdb->prepare("
                SELECT p.ID, p.post_title, m1.meta_value as 'ngp_industry', m2.meta_value as 'ngp_experience', m3.meta_value as 'ngp_country', m4.meta_value as 'ngp_education'
                FROM "
. $posts . ' p ' . $joins . "
                WHERE p.post_type = 'resume' AND p.post_status = 'publish' "
. $where_name . $where_industry . $where_experience . $where_country . $where_education . "
                ORDER BY p.post_date DESC LIMIT %d, %d"
, $start, $per_page ) );


            $count = $wpdb->get_var($wpdb->prepare("
                SELECT COUNT(p.ID)
                FROM "
. $posts . ' p ' . $joins . "
                WHERE p.post_type = 'resume' AND post_status = 'publish' "
. $where_name . $where_industry . $where_experience . $where_country . $where_education, array() ) );

            $msg .= '
            <br class = "clear" />

            <table class="table table-striped table-hover table-responsive table-resume">
                <tr>
                    <th>Name</th>
                    <th>Industry</th>
                    <th>Experience</th>
                    <th>Education</th>             
                    <th>Country</th>
                </tr>'
;

            if($all_user_resumes):
                foreach($all_user_resumes as $key => $resume):
                    $msg .= '
                    <tr class = "resume_'
. $resume->ID . '">
                        <td><a href="'
.get_permalink($resume->ID).'" id = "resume-'.$resume->ID.'" class = "resume_title">' . $resume->post_title . '</a></td>
                        <td>'
. $resume->ngp_industry .'</td>
                        <td>'
. $resume->ngp_experience .'</td>
                        <td>'
. $resume->ngp_education .'</td>                 
                        <td>'
. $resume->ngp_country .'</td>
                    </tr>'
;
                endforeach;
            else:
                $msg .= '<tr><td colspan="5">No results found.</td></tr>';
            endif;

            $msg .= '</table>';

            $msg = "<div class='cvf-universal-content'>" . $msg . "</div><br class = 'clear' />";

            $no_of_paginations = ceil($count / $per_page);

            if ($cur_page >= 7) {
                $start_loop = $cur_page - 3;
                if ($no_of_paginations > $cur_page + 3)
                    $end_loop = $cur_page + 3;
                else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
                    $start_loop = $no_of_paginations - 6;
                    $end_loop = $no_of_paginations;
                } else {
                    $end_loop = $no_of_paginations;
                }
            } else {
                $start_loop = 1;
                if ($no_of_paginations > 7)
                    $end_loop = 7;
                else
                    $end_loop = $no_of_paginations;
            }

            $pag_container .= "
            <div class='cvf-universal-pagination'>
                <ul>"
;

            if ($first_btn && $cur_page > 1) {
                $pag_container .= "<li p='1' class='active'>First</li>";
            } else if ($first_btn) {
                $pag_container .= "<li p='1' class='inactive'>First</li>";
            }

            if ($previous_btn && $cur_page > 1) {
                $pre = $cur_page - 1;
                $pag_container .= "<li p='$pre' class='active'>Previous</li>";
            } else if ($previous_btn) {
                $pag_container .= "<li class='inactive'>Previous</li>";
            }
            for ($i = $start_loop; $i <= $end_loop; $i++) {

                if ($cur_page == $i)
                    $pag_container .= "<li p='$i' class = 'selected' >{$i}</li>";
                else
                    $pag_container .= "<li p='$i' class='active'>{$i}</li>";
            }

            if ($next_btn && $cur_page < $no_of_paginations) {
                $nex = $cur_page + 1;
                $pag_container .= "<li p='$nex' class='active'>Next</li>";
            } else if ($next_btn) {
                $pag_container .= "<li class='inactive'>Next</li>";
            }

            if ($last_btn && $cur_page < $no_of_paginations) {
                $pag_container .= "<li p='$no_of_paginations' class='active'>Last</li>";
            } else if ($last_btn) {
                $pag_container .= "<li p='$no_of_paginations' class='inactive'>Last</li>";
            }

            $pag_container = $pag_container . "
                </ul>
            </div>"
;

            echo
            '<div class = "cvf-pagination-content">' . $msg . '</div>' .
            '<div class = "cvf-pagination-nav">' . $pag_container . '</div>';

        }
        exit();
    }

    public static function cvf_ngp_create_new_resume() {

        global $current_user, $wpdb;

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'create_new_resume') {

            foreach($_POST as $k => $value) {
                $_POST[$k] = sanitize_text_field($value);
            }

            $ngp_resume = array(
                'post_title'    => wp_strip_all_tags( $_POST['ngp_name'] ),
                'post_status'   => 'publish',
                'post_type'     => 'resume',
                'post_author'   => $current_user->ID,
            );

            $post_id = wp_insert_post( $ngp_resume );

            update_post_meta($post_id, 'ngp_phone', $_POST['ngp_phone']);
            update_post_meta($post_id, 'ngp_industry', $_POST['ngp_industry']);
            update_post_meta($post_id, 'ngp_headline', $_POST['ngp_headline']);
            update_post_meta($post_id, 'ngp_experience', $_POST['ngp_experience']);
            update_post_meta($post_id, 'ngp_education', $_POST['ngp_education']);
            update_post_meta($post_id, 'ngp_country', $_POST['ngp_country']);
            update_post_meta($post_id, 'ngp_zipcode', $_POST['ngp_zipcode']);
           
            echo $post_id;
           
            $subsribed_users = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM wp_users u
            LEFT JOIN wp_usermeta um ON u.ID = um.user_id
            WHERE um.meta_key = 'ngp_subscribe_joblistings'
            AND um.meta_value = 1"
, array() ));
           
            foreach($subsribed_users as $key => $user) {
               
                $from = get_option('admin_email');
                $headers = 'From: NGPPortfolioCo <"' . $from . '">';
                $subject = "New Job Application Submitted ". $_POST['ngp_headline'] . " (" . $_POST['ngp_industry'] .") - " . $_POST['ngp_name'];
               
                ob_start();
                           
               
                echo '
                <p>Dear '
. $user->display_name . ', <br />
                A new job was posted on NGPPortfolioCo, and the details can be found bellow:
                </p><br />
               
                <table style="width:100%; color: #333; font-family: Arial, Helvetica, sans-serif;" border = "1" >
                    <tr>
                        <th colspan = "2" style = "font-size: 30px; padding: 10px 0; background: #5C0F26; color: #fff; ">NGPPortfolioCO</th>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Country</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_country', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Country</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_country', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Zip Code</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_zipcode', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Education</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_education', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Experience</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_experience', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Phone</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_phone', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Industry</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_industry', true) . '</td>
                    </tr>
                    <tr>
                        <th style = "padding: 10px; margin: 10px;">Job Title / Headline</th>
                        <td style = "padding: 10px;">'
. get_post_meta($post_id, 'ngp_headline', true) . '</td>
                    </tr>
                    </tr>
                        <th style = "padding: 10px; margin: 10px;">Resume</th>
                        <td style = "padding: 10px;">Go to our <a href = "'
. home_url() . '">website</a> to view the attached resume</td>
                    </tr>
                </table>
                <br />
                <p>As a member of NGPPortfolioCo, we will send you new job listings. To view other joba, search our list of current openings:'
. home_url('/resume-database/') . '</p>         
                <br />
                <p>Thank you,<br />
                NGPPortfolioCo Team</p>                
                '
;
                   
                $message = ob_get_contents();
               
                ob_end_clean();

                wp_mail($user->user_email, $subject, $message, $headers);
            }
        }

        exit();
    }
   
    public static function cvf_ngp_mail_content_type() {
        return 'text/html';
    }



    public static function cvf_ngp_create_new_resume_form() {

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'create_new_resume_form') {

            $ngp_field = self::cvf_ngp_get_all_pre_resume_fields(166);

            $new_resume_form .= '
            <h1>Step 1</h1>
            <hr /><br />

            <div class = "col-sm-12 pads">
                <div class = "create-new-resume-response"></div>
            </div>

            <div class = "ngp-create-new-resume-form">
                <div class = "col-sm-6 pads">
                    <div class="form-group">
                        <label for="ngp_name">Full Name</label><br />
                        <input type="text" name="ngp_name" class="form-control ngp_name" />
                    </div>'
;

                    foreach( array_slice($ngp_field, 0, 5) as $field ):
                        $ngp_val = get_post_meta(166, $field->meta_key, false);

                        if ($ngp_val[0]['type'] == 'text' || $ngp_val[0]['type'] == 'number'):
                            $new_resume_form .= '
                            <div class="form-group">
                                <label for="'
. $ngp_val[0]['name'] . '">' . $ngp_val[0]['label'] . ':</label><br />
                                <input type="text" name="'
. $ngp_val[0]['name'] . '" class="form-control ' . $ngp_val[0]['name'] . '" />
                            </div>'
;

                        elseif ($ngp_val[0]['type'] == 'select'):
                            $new_resume_form .= '
                            <div class="form-group">
                                <label for="'
. $ngp_val[0]['name'] . '">' . $ngp_val[0]['label'] . ':</label><br />

                                <select id = "'
. $ngp_val[0]['name'] . '" name="' . $ngp_val[0]['name'] . '" class="ngp-select form-control ' . $ngp_val[0]['name'] . '">
                                    <option value="">- Select '
. $ngp_val[0]['label'] . ' -</option>';
                                    foreach ($ngp_val[0]['choices'] as $val):
                                        $new_resume_form .= '<option value="' . $val . '">' . $val . '</option>';
                                    endforeach;
                                $new_resume_form .= '
                                </select>
                            </div>'
;

                        endif;

                    endforeach;

                $new_resume_form .= '
                </div>

                <div class = "col-sm-6 pads">'
;
                    foreach( array_slice($ngp_field, 5, 12) as $field ):
                        $ngp_val = get_post_meta(166, $field->meta_key, false);

                        if ($ngp_val[0]['type'] == 'text' || $ngp_val[0]['type'] == 'number'):
                            $new_resume_form .= '
                            <div class="form-group">
                                <label for="'
. $ngp_val[0]['name'] . '">' . $ngp_val[0]['label'] . ':</label><br />
                                <input type="text" name="'
. $ngp_val[0]['name'] . '" class="form-control ' . $ngp_val[0]['name'] . '" />
                            </div>'
;

                        elseif ($ngp_val[0]['type'] == 'select'):
                            $new_resume_form .= '
                            <div class="form-group">
                                <label for="'
. $ngp_val[0]['name'] . '">' . $ngp_val[0]['label'] . ':</label><br />

                                <select id = "'
. $ngp_val[0]['name'] . '" name="' . $ngp_val[0]['name'] . '" class="ngp-select form-control ' . $ngp_val[0]['name'] . '">
                                    <option value="">- Select '
. $ngp_val[0]['label'] . ' -</option>';
                                    foreach ($ngp_val[0]['choices'] as $val):
                                        $new_resume_form .= '<option value="' . $val . '">' . $val . '</option>';
                                    endforeach;
                                $new_resume_form .= '
                                </select>
                            </div>'
;
                        endif;
                    endforeach;

                    $new_resume_form .= '
                    <input type = "submit" value = "Submit Resume" class = "btn btn-primary create-new-resume" />
                </div>
            </div>

            <br class = "clear" /><br />
            '
;

            echo $new_resume_form;

        }

        exit();
    }

    public static function cvf_ngp_get_all_pre_resume_fields($post_id) {

        global $wpdb;

        $table = $wpdb->prefix . 'postmeta';
        $pre_resume_fields = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM $table WHERE post_id = %d AND meta_key LIKE '%%field_%%'"
, $post_id ));

        return $pre_resume_fields;

    }

    public static function cvf_ngp_get_all_user_resumes() {

        global $wpdb, $current_user;

        $table = $wpdb->prefix . 'posts';
        $user_resumes = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM $table WHERE post_author = %d
            AND post_type = 'resume' AND post_status = 'publish'"
, $current_user->ID ));

        return $user_resumes;

    }

    public static function cvf_ngp_pagination_load_resumes() {

        global $wpdb, $current_user;
        $msg = '';

        if(isset($_POST['page'])){
            $page = sanitize_text_field($_POST['page']);
            $cur_page = $page;
            $page -= 1;
            $per_page = 5;
            $previous_btn = true;
            $next_btn = true;
            $first_btn = true;
            $last_btn = true;
            $start = $page * $per_page;

            $posts = $wpdb->prefix . "posts";
            $postmeta = $wpdb->prefix . "postmeta";

            $where_name = ''; $where_experience = ''; $where_education = ''; $where_country = ''; $where_industry = '';

            if(!empty($_POST['ngp_name'])){
                $where_name = ' AND (p.post_title LIKE "%%' . $_POST['ngp_name'] . '%%") ';
            }
            if(!empty($_POST['ngp_industry'])){
                $where_industry = ' AND (m1.meta_value LIKE "%%' . $_POST['ngp_industry'] . '%%") ';
            }
            if(!empty($_POST['ngp_experience'])){
                $where_experience = ' AND (m2.meta_value LIKE "%%' . $_POST['ngp_experience'] . '%%") ';
            }
            if(!empty($_POST['ngp_country'])){
                $where_country = ' AND (m3.meta_value LIKE "%%' . $_POST['ngp_country'] . '%%") ';
            }
            if(!empty($_POST['ngp_education'])){
                $where_education = ' AND (m4.meta_value LIKE "%%' . $_POST['ngp_education'] . '%%") ';
            }

            $joins = '
                LEFT JOIN '
. $postmeta . ' m1 ON p.ID = m1.post_id AND m1.meta_key = "ngp_industry"
                LEFT JOIN '
. $postmeta . ' m2 ON p.ID = m2.post_id AND m2.meta_key = "ngp_experience"
                LEFT JOIN '
. $postmeta . ' m3 ON p.ID = m3.post_id AND m3.meta_key = "ngp_country"
                LEFT JOIN '
. $postmeta . ' m4 ON p.ID = m4.post_id AND m4.meta_key = "ngp_education" ';

            $all_user_resumes = $wpdb->get_results($wpdb->prepare("
                SELECT p.ID, p.post_title, m1.meta_value as 'ngp_industry', m2.meta_value as 'ngp_experience', m3.meta_value as 'ngp_country', m4.meta_value as 'ngp_education'
                FROM "
. $posts . ' p ' . $joins . "
                WHERE p.post_type = 'resume' AND p.post_status = 'publish' AND p.post_author = %d "
. $where_name . $where_industry . $where_experience . $where_country . $where_education . "
                ORDER BY p.post_date DESC LIMIT %d, %d"
, $current_user->ID, $start, $per_page ) );


            $count = $wpdb->get_var($wpdb->prepare("
                SELECT COUNT(p.ID)
                FROM "
. $posts . ' p ' . $joins . "
                WHERE p.post_type = 'resume' AND post_status = 'publish' AND post_author = %d "
. $where_name . $where_industry . $where_experience . $where_country . $where_education, $current_user->ID ) );

            $msg .= '
            <br class = "clear" />

            <table class="table table-striped table-hover table-responsive table-resume">
                <tr>
                    <th>Name</th>
                    <th>Experience</th>
                    <th>Education</th>
                    <th>Country</th>
                    <th>Action</th>
                </tr>'
;

            if($all_user_resumes):
                foreach($all_user_resumes as $key => $resume):
                    $msg .= '
                    <tr class = "resume_'
. $resume->ID . '">
                        <td><a href="#" id = "resume-'
.$resume->ID.'" class = "resume_title">' . $resume->post_title . '</a></td>
                        <td>'
. $resume->ngp_experience .'</td>
                        <td>'
. $resume->ngp_education .'</td>
                        <td>'
. $resume->ngp_country .'</td>
                        <td>
                            <a href = "#" title = "Edit" class = "edit_resume" id = "edit-'
. $resume->ID . '"><span class = "glyphicon glyphicon-pencil"></span></a>&nbsp;
                            <a href = "#" title = "Delete" class = "delete_resume" id = "del-'
. $resume->ID . '"><span class = "glyphicon glyphicon-trash"></span></a>
                        </td>
                    </tr>'
;
                endforeach;
            else:
                $msg .= '<tr><td colspan="5">No results found.</td></tr>';
            endif;

            $msg .= '</table>';

            $msg = "<div class='cvf-universal-content'>" . $msg . "</div><br class = 'clear' />";

            $no_of_paginations = ceil($count / $per_page);

            if ($cur_page >= 7) {
                $start_loop = $cur_page - 3;
                if ($no_of_paginations > $cur_page + 3)
                    $end_loop = $cur_page + 3;
                else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
                    $start_loop = $no_of_paginations - 6;
                    $end_loop = $no_of_paginations;
                } else {
                    $end_loop = $no_of_paginations;
                }
            } else {
                $start_loop = 1;
                if ($no_of_paginations > 7)
                    $end_loop = 7;
                else
                    $end_loop = $no_of_paginations;
            }

            $pag_container .= "
            <div class='cvf-universal-pagination'>
                <ul>"
;

            if ($first_btn && $cur_page > 1) {
                $pag_container .= "<li p='1' class='active'>First</li>";
            } else if ($first_btn) {
                $pag_container .= "<li p='1' class='inactive'>First</li>";
            }

            if ($previous_btn && $cur_page > 1) {
                $pre = $cur_page - 1;
                $pag_container .= "<li p='$pre' class='active'>Previous</li>";
            } else if ($previous_btn) {
                $pag_container .= "<li class='inactive'>Previous</li>";
            }
            for ($i = $start_loop; $i <= $end_loop; $i++) {

                if ($cur_page == $i)
                    $pag_container .= "<li p='$i' class = 'selected' >{$i}</li>";
                else
                    $pag_container .= "<li p='$i' class='active'>{$i}</li>";
            }

            if ($next_btn && $cur_page < $no_of_paginations) {
                $nex = $cur_page + 1;
                $pag_container .= "<li p='$nex' class='active'>Next</li>";
            } else if ($next_btn) {
                $pag_container .= "<li class='inactive'>Next</li>";
            }

            if ($last_btn && $cur_page < $no_of_paginations) {
                $pag_container .= "<li p='$no_of_paginations' class='active'>Last</li>";
            } else if ($last_btn) {
                $pag_container .= "<li p='$no_of_paginations' class='inactive'>Last</li>";
            }

            $pag_container = $pag_container . "
                </ul>
            </div>"
;

            echo
            '<div class = "cvf-pagination-content">' . $msg . '</div>' .
            '<div class = "cvf-pagination-nav">' . $pag_container . '</div>';

        }
        exit();
    }

    public static function cvf_ngp_preveiw_resume() {

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'preveiw_resume') {

            $resume_id = $_POST['resume_id'];
            $resume = get_post($resume_id);
           
            $avatar_id = get_post_meta($resume_id, 'ngp_avatar', true);
            $avatar_url = wp_get_attachment_image_src($avatar_id);
           
            if($avatar_url[0]) {
                $avatar = '<img src = "' . $avatar_url[0] . '" class = "img-thumbnail" width = "135" />';
            } else {
                $avatar = '<img src = "' . get_template_directory_uri() . '/images/default_avatar.jpg" class = "img-thumbnail">';
            }
           
            $preview_resume .= '
                <div class = "preview_resume_display">
                    <a href = "#" class = "cvf_goback">Go Back</a>

                    <div class = "col-md-12">
                        <div class = "preview_resume_messages"></div>
                        <br />
                        <div class = "col-sm-2 pads">'
. $avatar . '</div>         
                        <div class = "col-sm-10 pads">
                            <h1>'
. $resume->post_title .'</h1>
                            <hr />
                        </div>             
                    </div>
                   
                    <br class = "clear" /><br />

                    <div class = "col-md-12">
                        <table class = "table table-striped">
                            <tr class="success">
                                <th >Country</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_country', true) . '</td>
                            </tr>
                            <tr class="success">
                                <th>Zip Code</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_zipcode', true) . '</td>
                            </tr>
                            <tr>
                                <th>Education</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_education', true) . '</td>
                            </tr>
                            <tr class="info">
                                <th>Experience</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_experience', true) . '</td>
                            </tr>
                            <tr>
                                <th>Phone</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_phone', true) . '</td>
                            </tr>
                            <tr>
                                <th>Industry</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_industry', true) . '</td>
                            </tr>
                            <tr class="warning">
                                <th>Job Title / Headline</th>
                                <td>'
. get_post_meta($resume_id, 'ngp_headline', true) . '</td>
                            </tr>
                            </tr>
                                <th>Resume</th>'
;
                               
                                $attahcment_id = get_post_meta($resume_id, 'ngp_resume', true);
                                $attachment = wp_get_attachment_url($attahcment_id);
           
                                if ( $attachment != false ) {
                                    $preview_resume .= '<td><a href = "' . $attachment . '" class = "btn btn-primary">Download Resume</td>';
                                } else {
                                    $preview_resume .= '<td>N/A</td>';
                                }
                            $preview_resume .= '
                            </tr>
                        </table>
                    </div>
                </div>'
;

            echo $preview_resume;

        }
        exit();

    }


    public static function cvf_ngp_edit_resume() {

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'edit_resume') {

            $resume_id = $_POST['resume_id'];
            $resume = get_post($resume_id);

            $ngp_education = get_post_meta(166, 'field_54602724d1943', false);
            $ngp_experience = get_post_meta(166, 'field_546026eb1d61f', false);
            $ngp_industry = get_post_meta(166, 'field_5460268eaf31f', false);
            $ngp_country = get_post_meta(166, 'field_546027490c53b', false);
            $ngp_phone = get_post_meta($resume_id, 'ngp_phone', false);
            $ngp_headline = get_post_meta($resume_id, 'ngp_headline', false);
            $ngp_zipcode = get_post_meta($resume_id, 'ngp_zipcode', false);
            $ngp_resume = get_post_meta($resume_id, 'ngp_resume', false);
            $ngp_avatar = get_post_meta($resume_id, 'ngp_avatar', false);

            $edit_resume .= '

                <a href = "#" class = "cvf_goback">Go Back</a>

                <div class = "ngp_edit_resume_response"></div>

                <br clsss = "clear" />

                <div class = "ngp_edit_resume">
                    <div class = "col-md-6 pads">
                        <div class="form-group">
                            <label>Full Name:</label><br />
                            <input type = "text" value = "'
. $resume->post_title .'" class = "form-control ngp_name" />
                        </div>

                        <div class="form-group">
                            <label>Phone:</label><br />
                            <input type = "text" value = "'
. $ngp_phone[0] .'" class = "form-control ngp_phone" />
                        </div>

                        <div class="form-group">
                            <label>Industry:</label><br />
                            <select class = "ngp-select form-control ngp_industry">'
;
                                foreach ($ngp_industry[0]['choices'] as $val):
                                    if($val == get_post_meta($resume->ID, 'ngp_industry', true)):
                                        $edit_resume .= '<option value="' . $val . '" selected = "selected">' . $val . '</option>';
                                    else:
                                        $edit_resume .= '<option value="' . $val . '">' . $val . '</option>';
                                    endif;
                                endforeach;
                            $edit_resume .= '
                            </select>
                        </div>

                        <div class="form-group">
                            <label>Headline or Job Title:</label><br />
                            <input type = "text" value = "'
. $ngp_headline[0] .'" class = "form-control ngp_headline" />
                        </div>

                        <div class="form-group">
                            <label>Experience:</label><br />
                            <select class = "ngp-select form-control ngp_experience">'
;
                                foreach ($ngp_experience[0]['choices'] as $val):
                                    if($val == get_post_meta($resume->ID, 'ngp_experience', true)):
                                        $edit_resume .= '<option value="' . $val . '" selected = "selected">' . $val . '</option>';
                                    else:
                                        $edit_resume .= '<option value="' . $val . '">' . $val . '</option>';
                                    endif;
                                endforeach;
                            $edit_resume .= '
                            </select>
                        </div>

                        <div class="form-group">
                            <label>Education:</label><br />
                            <select class = "ngp-select form-control ngp_education">'
;
                                foreach ($ngp_education[0]['choices'] as $val):
                                    if($val == get_post_meta($resume->ID, 'ngp_education', true)):
                                        $edit_resume .= '<option value="' . $val . '" selected = "selected">' . $val . '</option>';
                                    else:
                                        $edit_resume .= '<option value="' . $val . '">' . $val . '</option>';
                                    endif;
                                endforeach;
                            $edit_resume .= '
                            </select>
                        </div>
                    </div>

                    <div class = "col-md-6 pads">
                        <div class="form-group">
                            <label>Country:</label><br />
                            <select class = "ngp-select form-control ngp_country">'
;
                                foreach ($ngp_country[0]['choices'] as $val):
                                    if($val == get_post_meta($resume->ID, 'ngp_country', true)):
                                        $edit_resume .= '<option value="' . $val . '" selected = "selected">' . $val . '</option>';
                                    else:
                                        $edit_resume .= '<option value="' . $val . '">' . $val . '</option>';
                                    endif;
                                endforeach;
                            $edit_resume .= '
                            </select>
                        </div>

                        <div class="form-group">
                            <label>Zip Code:</label><br />
                            <input type = "text" value = "'
. $ngp_zipcode[0] .'" class = "form-control ngp_zipcode" />
                        </div>
                       
                        <div class = "attachment_resume_holder form-group col-md-6">'
;

                            $attachment = get_post($ngp_resume[0]);

                            if ( $attachment->ID ) {
                                $edit_resume .= '
                                <ul class = "resume-attachment no-margin">
                                    <li><img src = "'
.get_template_directory_uri().'/images/document.png" /></li>
                                    <li>
                                        <p><a href = "'
.$attachment->guid.'" target = "_blank">'.substr($attachment->post_title, 0, 20).'</a></p>
                                        <p>
                                            <a href = "#" class = "delete_attachment" id = "del-'
.$attachment->ID.'">Delete</a> |
                                            <a href = "#" class = "change_attachment" id = "change-'
.$attachment->ID.'" data-toggle="modal" data-target="#upload_file">Change</a>
                                        </p>
                                    </li>
                                </ul>
                                <br class = "clear" />
                                '
;

                            } else {
                                $edit_resume .= '
                                <a href = "#" data-toggle="modal" data-target="#upload_file" class = "btn btn-success"><span class = "glyphicon glyphicon-plus"></span> Add Resume</a><br class = "clear" /><br />'
;
                            }
                       
                        $edit_resume .= '
                        </div>
                       
                        <div class = "avatar_resume_holder form-group col-md-6">'
;
                       
                            $avatar = get_post($ngp_avatar[0]);

                            if ( $avatar->ID ) {
                                $edit_resume .= '
                                <ul class = "resume-attachment no-margin">
                                    <li>'
.wp_get_attachment_image($avatar->ID, array(60,60), 0, array('class' => "img-thumbnail")) .'</li>
                                    <li>
                                        <p><a href = "'
.$avatar->guid.'" target = "_blank">'.$avatar->post_title.'</a></p>
                                        <p>
                                            <a href = "#" class = "delete_avatar" id = "del-'
.$avatar->ID.'">Delete</a> |
                                            <a href = "#" class = "change_attachment" id = "change-'
.$avatar->ID.'" data-toggle="modal" data-target="#upload_avatar">Change</a>
                                        </p>
                                    </li>
                                </ul>
                                <br class = "clear" />
                                '
;

                            } else {
                                $edit_resume .= '
                                <a href = "#" data-toggle="modal" data-target="#upload_avatar" class = "btn btn-success"><span class = "glyphicon glyphicon-upload"></span> Upload Avatar</a><br class = "clear" /><br />'
;
                            }

                        $edit_resume .= '
                        </div>
                       
                        <br class = "clear" />
                       
                        <input type = "hidden" value = "'
. $resume->ID . '" class = "resume_id" >
                        <input type = "submit" value = "Save Resume" class = "btn btn-primary save_edited_resume" >

                    </div>
                </div>

            '
;

            echo $edit_resume;

        }

        exit();
    }

    public static function cvf_ngp_update_resume() {

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'update_resume') {

            foreach($_POST as $k => $value) {
                $_POST[$k] = sanitize_text_field($value);
            }

            $resume_data = array(
              'ID'           => $_POST['resume_id'],
              'post_title'   => $_POST['ngp_name'],
              'post_name'    => $_POST['ngp_name']
            );

            $update_status = wp_update_post( $resume_data );

            update_post_meta($_POST['resume_id'], 'ngp_phone', $_POST['ngp_phone']);
            update_post_meta($_POST['resume_id'], 'ngp_industry', $_POST['ngp_industry']);
            update_post_meta($_POST['resume_id'], 'ngp_headline', $_POST['ngp_headline']);
            update_post_meta($_POST['resume_id'], 'ngp_experience', $_POST['ngp_experience']);
            update_post_meta($_POST['resume_id'], 'ngp_education', $_POST['ngp_education']);
            update_post_meta($_POST['resume_id'], 'ngp_country', $_POST['ngp_country']);
            update_post_meta($_POST['resume_id'], 'ngp_zipcode', $_POST['ngp_zipcode']);

            if($update_status > 0) {
                echo '<br /><p class = "bg-success no-margin"><span class = "glyphicon glyphicon-ok"></span>&nbsp; Resume updated successfully.</p>';
            } else {
                echo '<br /><p class = "bg-danger no-margin">An internal error occured</p>';
            }
        }

        exit();
    }

    public static function cvf_ngp_delete_attachment(){

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'delete_attachment'){

            $attachemnt_id = sanitize_text_field($_POST['attachment_id']);
            $type = sanitize_text_field($_POST['type']);
            $post_id = sanitize_text_field($_COOKIE['ngp_edit_resume']);
   
            if(false === wp_delete_attachment($attachemnt_id, true)) {}

            echo 'success';

        }

        exit();
    }

    public static function cvf_ngp_delete_resume() {

        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'delete_resume'){

            $resume_id = sanitize_text_field($_POST['resume_id']);
            wp_delete_post($resume_id, 1);

            echo 'success';

        }

        exit();
    }

    public static function cvf_ngp_delete_all_resume_attachments($post_id) {

        $attachments = get_posts( array(
            'post_type'      => 'attachment',
            'posts_per_page' => -1,
            'post_status'    => 'any',
            'post_parent'    => $post_id
        ) );

        foreach ( $attachments as $attachment ) {
            if ( false === wp_delete_attachment( $attachment->ID, 1 ) ) { /* Log failure to delete attachements */ }
        }

    }
   
    public static function cvf_ngp_upload_resume_data() {
               
        $user_resumes = self::cvf_ngp_get_all_user_resumes();
        $wp_upload_dir = wp_upload_dir();
        $path = $wp_upload_dir['path'] . '/';

        $count = 0;

        if(isset($_POST['upload_resume_file']) and $_SERVER['REQUEST_METHOD'] == "POST"){
           
            if(!$_FILES['files']['name']) {
                echo "<p class='bg-danger'>Please select a file.</p>";
            }
           
            foreach ($_FILES['files']['name'] as $f => $name) {
           
                if(isset($_POST['resume_avatar'])) {
                    $valid_formats = array("jpg", "jpeg", "gif", "png");
                    $max_file_size = 1024 * 500; # in kb
                    $file_title = 'avatar-'.$_COOKIE['ngp_edit_resume'];
                } else if (isset($_POST['resume_file'])) {
                    $valid_formats = array("doc", "docx", "pdf", "txt");
                    $max_file_size = 1024 * 2000; # in kb
                    $file_title = $name;
                }
               
                $extension = pathinfo($name, PATHINFO_EXTENSION);
                $new_filename = cvf_ngp_generate_random_code(20)  . '.' . $extension;
               
                if ($_FILES['files']['error'][$f] == 4) {}
               
                if ($_FILES['files']['error'][$f] == 0) {
               
                    if ($_FILES['files']['size'][$f] > $max_file_size) {
                        echo "<p class='bg-danger'>$name is too large!.</p>";
                       
                    } elseif( ! in_array($extension, $valid_formats) ){
                        echo "<p class='bg-danger'>$name is not a valid format</p>";
                       
                    } else{
                       
                        if(move_uploaded_file($_FILES["files"]["tmp_name"][$f], $path.$new_filename)) {
                           
                            $count++;

                            $filename = $path.$new_filename;
                            $parent_post_id = $_COOKIE['ngp_edit_resume'];
                            $filetype = wp_check_filetype( basename( $filename ), null );
                            $wp_upload_dir = wp_upload_dir();
                            $attachment = array(
                                'guid'           => $wp_upload_dir['url'] . '/' . basename( $filename ),
                                'post_mime_type' => $filetype['type'],
                                'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $file_title ) ),
                                'post_content'   => '',
                                'post_status'    => 'inherit'
                            );

                            $attach_id = wp_insert_attachment( $attachment, $filename, $parent_post_id );

                            require_once( ABSPATH . 'wp-admin/includes/image.php' );

                            $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
                            wp_update_attachment_metadata( $attach_id, $attach_data );
                           
                            if(isset($_POST['resume_file'])) {
                                update_post_meta($_COOKIE['ngp_edit_resume'], 'ngp_resume', $attach_id);
                            } elseif(isset($_POST['resume_avatar'])) {
                                update_post_meta($_COOKIE['ngp_edit_resume'], 'ngp_avatar', $attach_id);
                            }
                           
                            echo 'success';
                           
                        }
                    }
                }
            }
           
            exit();
           
        }
    }
   
   
    public static function cvf_ngp_archive_dates() {
   
        global $wpdb;
       
        $table = $wpdb->prefix . 'posts';
        $archive_dates = $wpdb->get_results($wpdb->prepare("
            SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts
            FROM $table  WHERE post_type = 'blog' AND post_status = 'publish'
            GROUP BY YEAR(post_date), MONTH(post_date)
            ORDER BY post_date DESC"
, array()
        ) );
       
        return $archive_dates;
       
    }
   
    public static function cvf_ngp_blog_datearchives_rewrite_rules($wp_rewrite) {

        $rules = self::cvf_ngp_generate_blog_date_archives('blog', $wp_rewrite);
        $wp_rewrite->rules = $rules + $wp_rewrite->rules;
        return $wp_rewrite;
       
    }

    public static function cvf_ngp_generate_blog_date_archives($cpt, $wp_rewrite) {

        $rules = array();

        $post_type = get_post_type_object($cpt);
        $slug_archive = $post_type->has_archive;
        if ($slug_archive === false) return $rules;
        if ($slug_archive === true) {
            $slug_archive = $post_type->name;
        }

        $dates = array(
            array(
                'rule' => "([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})",
                'vars' => array('year', 'monthnum', 'day')),
            array(
                'rule' => "([0-9]{4})/([0-9]{1,2})",
                'vars' => array('year', 'monthnum')),
            array(
                'rule' => "([0-9]{4})",
                'vars' => array('year'))
        );

        foreach ($dates as $data) {
            $query = 'index.php?post_type='.$cpt;
            $rule = $slug_archive.'/'.$data['rule'];

            $i = 1;
            foreach ($data['vars'] as $var) {
                $query.= '&'.$var.'='.$wp_rewrite->preg_index($i);
                $i++;
            }

            $rules[$rule."/?$"] = $query;
            $rules[$rule."/feed/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
            $rules[$rule."/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
            $rules[$rule."/page/([0-9]{1,})/?$"] = $query."&paged=".$wp_rewrite->preg_index($i);
        }

        return $rules;
       
    }

    public static function cvf_ngp_remove_menus(){
   
        remove_menu_page('edit.php?post_type=resume');
        #remove_menu_page('edit.php?post_type=blog');
    }

}

Optimize WordPress Database by Disabling and Removing Revisions

After several edits on site postings, there may be several dozen copies of revisions and many of them are days or weeks old.

Now that we know that every one of these old versions is stored in the database we understand why it is growing exponentially in size making it all but impossible to manage, download, upload etc. While having all these copies is somewhat helpful during editing, once an article is published they are of little or no further use. Some or all need to be deleted for very practical reasons.

Best way to stop WordPress from saving revisions on database is to simply define this code into your config.php:

1
define('WP_POST_REVISIONS', false );

Additionally, you might also want to increase the autosave time interval:

1
define('AUTOSAVE_INTERVAL', 300 ); // seconds

To delete all revisions that are stored in your database, simply execute this SQL query in your command line:

1
DELETE FROM wp_posts WHERE post_type = "revision"

Make sure to change the wp_ prefix with your database prefix.

URL Rewriting for Custom Post Types Date Archive

Date based archives by default, work only with the default post types, today I am going to show you how to implement date based archives on custom post types.

Year archive: http://www.test.com/2014/
Month archive: http://www.test.com/2014/11/
Day archive: http://www.test.com/2014/30/14/
Month archive, feed: http://www.test.com/2014/10/feed/
Year archive, page 2: http://www.test.com/2014/page/3/

What we would like to achieve are link structures similar to bellow:

Year archive: http://www.test.com/blog/2014/
Month archive: http://www.test.com/blog/2014/11/
Day archive, feed: http://www.test.com/blog/2014/30/14/feed/
Year archive, page 5: http://www.test.com/blog/2014/page/3/

The idea behind the code bellow is: we are simply rewriting URL’s to read as

1
http://www.test.com/blog/2014/

instead of

1
http://www.test.com/2014/?post-type=blog

Copy this block of code into your functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
add_action('generate_rewrite_rules', array('NGP_Posts', 'cvf_ngp_blog_datearchives_rewrite_rules') );

class NGP_Posts {

    public static function cvf_ngp_blog_datearchives_rewrite_rules($wp_rewrite) {

        $rules = self::cvf_ngp_generate_blog_date_archives('blog', $wp_rewrite);
        $wp_rewrite->rules = $rules + $wp_rewrite->rules;
        return $wp_rewrite;
       
    }

    public static function cvf_ngp_generate_blog_date_archives($cpt, $wp_rewrite) {

        $rules = array();

        $post_type = get_post_type_object($cpt);
        $slug_archive = $post_type->has_archive;
        if ($slug_archive === false) return $rules;
        if ($slug_archive === true) {
            $slug_archive = $post_type->name;
        }

        $dates = array(
            array(
                'rule' => "([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})",
                'vars' => array('year', 'monthnum', 'day')),
            array(
                'rule' => "([0-9]{4})/([0-9]{1,2})",
                'vars' => array('year', 'monthnum')),
            array(
                'rule' => "([0-9]{4})",
                'vars' => array('year'))
        );

        foreach ($dates as $data) {
            $query = 'index.php?post_type='.$cpt;
            $rule = $slug_archive.'/'.$data['rule'];

            $i = 1;
            foreach ($data['vars'] as $var) {
                $query.= '&'.$var.'='.$wp_rewrite->preg_index($i);
                $i++;
            }

            $rules[$rule."/?$"] = $query;
            $rules[$rule."/feed/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
            $rules[$rule."/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
            $rules[$rule."/page/([0-9]{1,})/?$"] = $query."&paged=".$wp_rewrite->preg_index($i);
        }

        return $rules;
       
    }
}

Don’t forget to refresh your permalinks by setting it to default then back to Post Name.

Add .html extension to permalinks

html-extension

Reasons to add .html extension to your Permalinks

1. You have a static html site you want to convert to WordPress but do not want to change the URL names. This is a good reason why you should make WordPress html extensions in terms of permalink structure, if you do not want to do a lot of 301 redirects.
2. In my personal experience, .html pages rank higher than my normal WordPress pages. This could be wrong for many reasons. So I can not say 100% that .html WordPress pages rank higher or are better for SEO.
3. People say it looks better. It feels and looks static and some people view this subconsciously with a little more trust than a dynamic page.

 


By default, .html extension is possible on posts but not on pages. So today I am going to show you how you can add .html extension to both posts and pages in WordPress

Adding .html Extension to Posts

Go to Settings -> Permalinks then select the “Custom Structure” radio button. Change the structure of the code to:

1
/%postname%.html/

Done!

Adding .html Extension to Pages

Add this block of code into your functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
add_action('init', 'html_page_permalink', -1);
register_activation_hook(__FILE__, 'cvf_active');
register_deactivation_hook(__FILE__, 'cvf_deactive');


function html_page_permalink() {
   
    global $wp_rewrite;
   
    if ( !strpos($wp_rewrite->get_page_permastruct(), '.html')){
        $wp_rewrite->page_structure = $wp_rewrite->page_structure . '.html';
    }
   
}
add_filter('user_trailingslashit', 'no_page_slash',66,2);
function no_page_slash($string, $type){
   
    global $wp_rewrite;
   
    if ($wp_rewrite->using_permalinks() && $wp_rewrite->use_trailing_slashes==true && $type == 'page'){
        return untrailingslashit($string);
    } else {
        return $string;
    }
   
}

function cvf_active() {

    global $wp_rewrite;
   
    if ( !strpos($wp_rewrite->get_page_permastruct(), '.html')){
        $wp_rewrite->page_structure = $wp_rewrite->page_structure . '.html';
    }
    $wp_rewrite->flush_rules();
   
}

function cvf_deactive() {

    global $wp_rewrite;
   
    $wp_rewrite->page_structure = str_replace(".html","",$wp_rewrite->page_structure);
    $wp_rewrite->flush_rules();
   
}

Contact Form 7 Normalize Form CSS

The CSS code bellow will give you a nice and simple appearance to your Contact 7 Forms

1
2
3
4
5
6
7
8
.wpcf7-form p {line-height: 30px;}
.wpcf7-form p input[type="text"], .wpcf7-form p input[type="email"], .wpcf7-form p textarea, .wpcf7-form p select{padding: 5px; border: 1px solid #DDDDDD;
/*Applying CSS3 gradient: */ background: -moz-linear-gradient(center top , #FFFFFF,  #EEEEEE 1px, #FFFFFF 20px); background: -webkit-gradient(linear, left top, left 20, from(#FFFFFF), color-stop(5%, #EEEEEE) to(#FFFFFF)); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FBFBFB', endColorstr='#FFFFFF');  
/*Applying CSS 3radius: */  -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
/*Applying CSS3 box shadow: */ -moz-box-shadow: 0 0 2px #DDDDDD; -webkit-box-shadow: 0 0 2px #DDDDDD; box-shadow: 0 0 2px #DDDDDD; width: 70%;}
.wpcf7-form p select{width: 72%;}
.wpcf7-form p input[type="text"]:hover, .wpcf7-form p input[type="email"]:hover, .wpcf7-form p textarea:hover, .wpcf7-form p select:hover{border:1px solid #cccccc;}
.wpcf7-form p input[type="text"]:focus, .wpcf7-form p input[type="email"]:focus, .wpcf7-form p textarea:focus, .wpcf7-form p select:focus{box-shadow:0 0 2px #FFFE00;}

Contact Form 7 Country and State Drop-Down List

Country List

1
2
<p>Country:<br />
[select menu-773 "Afghanistan" "Albania" "Algeria" "Andorra" "Angola" "Antigua & Barbuda" "Argentina" "Armenia" "Australia" "Austria" "Azerbaijan" "Bahamas" "Bahrain" "Bangladesh" "Barbados" "Belarus" "Belgium" "Belize" "Benin" "Bhutan" "Bolivia" "Bosnia & Herzegovina" "Botswana" "Brazil" "Brunei" "Bulgaria" "Burkina Faso" "Burundi" "Cambodia" "Cameroon" "Canada" "Cape Verde" "Central African Republic" "Chad" "Chile" "China" "Colombia" "Comoros" "Congo" "Congo Democratic Republic" "Costa Rica" "Cote d'Ivoire" "Croatia" "Cuba" "Cyprus" "Czech Republic" "Denmark" "Djibouti" "Dominica" "Dominican Republic" "Ecuador" "East Timor" "Egypt" "El Salvador" "Equatorial Guinea" "Eritrea" "Estonia" "Ethiopia" "Fiji" "Finland" "France" "Gabon" "Gambia" "Georgia" "Germany" "Ghana" "Greece" "Grenada" "Guatemala" "Guinea" "Guinea-Bissau" "Guyana" "Haiti" "Honduras" "Hungary" "Iceland" "India" "Indonesia" "Iran" "Iraq" "Ireland" "Israel" "Italy" "Jamaica" "Japan" "Jordan" "Kazakhstan" "Kenya" "Kiribati" "Korea North" "Korea South" "Kosovo" "Kuwait" "Kyrgyzstan" "Laos" "Latvia" "Lebanon" "Lesotho" "Liberia" "Libya" "Liechtenstein" "Lithuania" "Luxembourg" "Macedonia" "Madagascar" "Malawi" "Malaysia" "Maldives" "Mali" "Malta" "Marshall Islands" "Mauritania" "Mauritius" "Mexico" "Micronesia" "Moldova" "Monaco" "Mongolia" "Montenegro" "Morocco" "Mozambique" "Myanmar (Burma)" "Namibia" "Nauru" "Nepal" "The Netherlands" "New Zealand" "Nicaragua" "Niger" "Nigeria" "Norway" "Oman" "Pakistan" "Palau" "Palestinian State*" "Panama" "Papua New Guinea" "Paraguay" "Peru" "The Philippines" "Poland" "Portugal" "Qatar" "Romania" "Russia" "Rwanda" "St. Kitts & Nevis" "St. Lucia" "St. Vincent & The Grenadines" "Samoa" "San Marino" "Sao Tome & Principe" "Saudi Arabia" "Senegal" "Serbia" "Seychelles" "Sierra Leone" "Singapore" "Slovakia" "Slovenia" "Solomon Islands" "Somalia" "South Africa" "South Sudan" "Spain" "Sri Lanka" "Sudan" "Suriname" "Swaziland" "Sweden" "Switzerland" "Syria" "Taiwan" "Tajikistan" "Tanzania" "Thailand" "Togo" "Tonga" "Trinidad & Tobago" "Tunisia" "Turkey" "Turkmenistan" "Tuvalu" "Uganda" "Ukraine" "United Arab Emirates" "United Kingdom" "United States of America" "Uruguay" "Uzbekistan" "Vanuatu" "Vatican City (Holy See)" "Venezuela" "Vietnam" "Yemen" "Zambia" "Zimbabwe"]</p>

Country Code List

1
2
<p>Country Code:<br />
[select menu-774 "AL" "AK" "AS" "AZ" "AR" "CA" "CO" "CT" "DE" "DC" "FM" "FL" "GA" "GU" "HI" "ID" "IL" "IN" "IA" "KS" "KY" "LA" "ME" "MH" "MD" "MA" "MI" "MN" "MS" "MO" "MT" "NE" "NV" "NH" "NJ" "NM" "NY" "NC" "ND" "MP" "OH" "OK" "OR" "PW" "PA" "PR" "RI" "SC" "SD" "TN" "TX" "UT" "VT" "VI" "VA" "WA" "WV" "WI" "WY"]</p>

For full names for states and territories within the United States

1
2
<p>State:<br />
[select menu-946 "Alabama" "Alaska" "American Samoa" "Arizona" "Arkansas" "California" "Colorado" "Connecticut" "Delaware" "District of Columbia" "Florida" "Georgia" "Guam" "Hawaii" "Idaho" "Illinois" "Indiana" "Iowa" "Kansas" "Kentucky" "Louisiana" "Maine" "Maryland" "Massachusetts" "Michigan" "Minnesota" "Mississippi" "Missouri" "Montana" "Nebraska" "Nevada" "New Hampshire" "New Jersey" "New Mexico" "New York" "North Carolina" "North Dakota" "Northern Marianas Islands" "Ohio" "Oklahoma" "Oregon" "Pennsylvania" "Puerto Rico" "Rhode Island" "South Carolina" "South Dakota" "Tennessee" "Texas" "Utah" "Vermont" "Virginia" "Virgin Islands" "Washington" "West Virginia" "Wisconsin" "Wyoming"]</p>

Indent WordPress Subcategories in a Drop-down List

Recently I was working on a front-end Ad Listing project, I needed to allow users to make posts on the front-end side and be able to select a category they want their ad to be listed in. Of course we do not want to be listing the selection of these categories in just one line, so in this tutorial I am going to show you how you can nicely indent the children of your categories in a drop-down list

On the back-end, this is how my custom categories look like:
multi-level-drop-down wordpress-cateogories-admin-view

What we are going to do is to convert the above photo into a Drop-down list and indent all the children just like how it was shown on the photo.

Copy this block of code into your theme’s functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* Params: $taxonomy_type - This is the name of your taxonomy
*/

function cvf_td_get_categories($taxonomy_type){
   
    global $wpdb;
   
    $term_taxonomy = $wpdb->prefix . 'term_taxonomy';
    $terms = $wpdb->prefix . 'terms';
    $query_category = $wpdb->get_results("
        SELECT t.term_id, t.name, tt.parent
        FROM $terms t INNER JOIN $term_taxonomy tt ON t.term_id = tt.term_id
        WHERE tt.taxonomy = '"
. $taxonomy_type . "' ORDER BY t.name"
    );
   
    if($query_category){
   
        $data = array();
        $index = array();
        foreach ($query_category as $key => $row) {
            global $index, $data;  
            $id = $row->term_id;
            $parent_id = $row->parent === NULL ? "NULL" : $row->parent;
            $data[$id] = $row;
            $index[$parent_id][] = $id;
        }
       
        function cvf_td_generate_option_spaces($length) {  
            $string = '';
            for ($p = 0; $p < $length; $p++) {
                $string .= '--';
            }    
           return $string;   
        }
       
        function cvf_td_display_child_nodes($parent_id, $level){
       
            global $data, $index;
            $parent_id = $parent_id === NULL ? "NULL" : $parent_id;
           
            if (isset($index[$parent_id])) {
           
                if($level == 0){ echo "<select class = 'form-control td_cat'><option value = 'empty'>Select a Category</option>"; }
               
                foreach ($index[$parent_id] as $id) {
                    $term_data = get_term_by('id', $id, 'td_categories');
                    if($level == 0){
                        echo "<option value = '" . $term_data->slug . "'>".$data[$id]->name."</option>";
                    }
                    if($level >= 1){
                        echo "<option value = '" . $term_data->slug . "'>" . cvf_td_generate_option_spaces($level) . ' ' . $data[$id]->name."</option>";
                    }

                    cvf_td_display_child_nodes($id, $level + 1);
   
                }
               
                if($level == 0){ echo "</select>"; }
            }
        }
        cvf_td_display_child_nodes(0, 0);
       
    } else {
        echo "<select class = 'form-control td_cat'><option value = 'empty'>Select a Category</option></select>";
    }
}

Done!, here is how it will look like on your page:

multi-level-drop-down wordpress-cateogories

Example of an Ajaxified WordPress Admin using OOP Style

ajaxified-wordpress-admin-using-oop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
class Td_Admin {
   
    public function __construct() {
        add_action('admin_menu', array($this, 'cvf_td_register_site_options') );
        add_action('wp_ajax_cvf_td_update_trade_settings', array($this, 'cvf_td_update_trade_settings') );
        add_action('admin_enqueue_scripts', array($this, 'cvf_td_load_admin_scripts') );
    }
   
    public static function cvf_td_load_admin_scripts() {
   
        wp_enqueue_style( 'admin-css', get_template_directory_uri() . '/css/admin.css', false, "1.1.1", false);
    }

   
    public static function cvf_td_register_site_options() {
        add_submenu_page( 'edit.php?post_type=page', 'Settings', 'Settings', 'administrator', 'trade-settings', 'Td_Admin::cvf_td_trade_settings' );
    }
   
    public static function cvf_td_trade_settings(){
        ?>
        <h2>Wp Trade Settings</h2>
        <span class = "update-trade-settings-response td-response"></span>
        <table class="form-table">
            <tbody>
                <tr>
                    <th><label for="limit_repost">Items can be reposted after:</label></th>
                    <td><input type="text" name="limit_repost" id="limit_repost" value="<?php echo get_option('td_limit_repost'); ?>" placeholder = "" class="regular-text"> <span class="description">Specify in hours, Default is: 24</span></td>
                </tr>
                <tr>
                    <th><label for="max_image_upload">Maximum Image Upload</label></th>
                    <td><input type="text" name="max_image_upload" id="first_name" value="<?php echo get_option('td_max_image_upload'); ?>" class="regular-text"></td>
                </tr>
                <tr>
                    <th><label for="max_image_size">Maximum Image Size</label></th>
                    <td><input type="text" name="max_image_size" id="max_image_size" value="<?php echo get_option('td_max_image_size'); ?>" class="regular-text"><span class="description">Specify in kilobytes, Default is: 500</span></td>
                </tr>
                <tr>
                    <th><label for="currency_symbol">Currency Symbol</label></th>
                    <td>
                        <select name="currency_symbol" class = "currency_symbol">
                            <option value = "dollar">$ Dollar</option>
                            <option value = "euro">&euro; Euro</option>
                            <option value = "pound">&pound; Pound</option>
                            <option value = "yen">&yen; Yen</option>
                            <option value = "peso">&#8369; Peso</option>
                       </select>
                    </td>
                </tr>
                <tr>
                     <th><label for="currency_symbol">Editor</label></th>
                    <td>
                        <?php
                        $content = '';
                        $editor_id = 'mycustomeditor';
                        wp_editor( $content, $editor_id );
                        ?>
                    </td>
                </tr>
            </tbody>
        </table>
       
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary update_wp_trade_settings" value="Update Settings"></p>
               
        <script type="text/javascript">
        jQuery(document).ready(function($) {
           
            var current_currency = '<?php echo get_option('td_currency_symbol'); ?>';
            if(current_currency){
                $(".currency_symbol").val(current_currency);
            }
           
            $('input').on('focus', function(){
                $('input').removeClass('border-red');
            });
           
            $("body").on("click", ".update_wp_trade_settings", function(e) {                        
                e.preventDefault();
               
                if(!$("input[name=limit_repost]").val()) {
                    $("input[name=limit_repost]").addClass('border-red');
                } else if(!$("input[name=max_image_upload]").val()) {
                    $("input[name=max_image_upload]").addClass('border-red');
                } else if(!$("input[name=max_image_size]").val()) {
                    $("input[name=max_image_size]").addClass('border-red');
                } else {
               
                    $(".update-trade-settings-response").html('<p><img src = "<?php bloginfo('template_url'); ?>/images/loading.gif" class = "loader" /></p>');
                    var data = {
                        'action': 'cvf_td_update_trade_settings',
                        'cvf_action': 'update_trade_settings',
                        'limit_repost': $("input[name=limit_repost]").val(),
                        'max_image_upload': $("input[name=max_image_upload]").val(),
                        'max_image_size': $("input[name=max_image_size]").val(),
                        'currency_symbol': $(".currency_symbol").val(),
                        'text_area_text': tinymce.editors.mycustomeditor.getContent()
                    };
                   
                    $.post(ajaxurl, data, function(response) {
                        element = $(".update-trade-settings-response");
                        $('html, body').animate({scrollTop: $(element).offset().top-100}, 150);
                        $(element).html(response);
                    });
                }
            });
        });
        </script>
        <?php
       
    }
   
    public static function cvf_td_update_trade_settings(){
       
        $errors = array();
        $currency_list = array('dollar', 'euro', 'pound', 'yen', 'peso');
       
        if(isset($_POST['cvf_action']) && $_POST['cvf_action'] == 'update_trade_settings'){
           
            foreach($_POST as $k => $value){
                $_POST[$k] = sanitize_text_field($value);
            }
           
            if (empty($errors) === true) {
           
                if(!is_numeric($_POST['limit_repost'])){
                    $errors[] = 'Only numeric values are allowed in "Items can be reposted after"';
                } elseif ($_POST['limit_repost'] < 0) {
                    $errors[] = 'Repost limit cannot be negative';
                }
               
                if(!is_numeric($_POST['max_image_upload'])){
                    $errors[] = 'Only numeric values are allowed in "Maximum Image Upload"';
                } elseif ($_POST['max_image_upload'] < 0) {
                    $errors[] = 'Maximum Image Upload cannot be negative';
                }
               
                if(!is_numeric($_POST['max_image_size'])){
                    $errors[] = 'Only numeric values are allowed in "Maximum Image Size"';
                } elseif ($_POST['max_image_size'] < 10) {
                    $errors[] = '"Maximum Image Upload" must be greater than 10kb';
                }
               
                if(!in_array($_POST['currency_symbol'], $currency_list)){
                    $errors[] = 'Invalid Currency Symbol.';
                }
            }
           
            if(empty($_POST) === false && empty($errors) === true) {
               
                update_option('td_limit_repost', $_POST['limit_repost']);
                update_option('td_max_image_upload', $_POST['max_image_upload']);
                update_option('td_max_image_size', $_POST['max_image_size']);
                update_option('td_currency_symbol', $_POST['currency_symbol']);
                update_option('td_text_area_text', $_POST['text_area_text']);
               
                echo '
                <div id="message" class="updated">
                    <p><strong>Settings updated.</strong></p>
                </div>'
;
               
            } elseif (empty($errors) === false) {
                echo '
                <div id="message" class="error">
                    <ul>'
;
                    foreach ($errors as $error) {
                        echo '<li><strong>' . $error . '</strong></li>';
                    }
                echo '</ul>
                </div>'
;
               
            }
        }
       
        exit();
    }
   
} new Td_Admin();

Using Object-Oriented Approach in Building WordPress Admin Menus

The code bellow will output the same way it’s outputted in Procedural Approach.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add_action('admin_menu', array('Ad_Admin', 'cvf_register_site_options') );

class My_Admin {
   
    public function __construct() {
       
    }
   
    public static function cvf_register_site_options() {
        add_submenu_page( 'tools.php', 'Custom Submenu', 'Custom Submenu', 'administrator', 'custom-submenu', 'My_Admin::custom_submenu' );
    }
   
    public static function custom_submenu(){
        echo 'Test';
    }
   
}

Notice how we use My_Admin::custom_submenu to access the static function custom_submenu(). It’s important we declare our outputting function as static so we could access it using the scope resolution operator.

Generate Emails with HTML & CSS in WordPress

emails-with-html-and-css

 

According to Chris Coyier, here are the things you can NOT do when creating emails:

  • Include a <head> section with styles. Apple Mail.app supports it, but Gmail and Hotmail do not, so it’s a no-no. Hotmail will support a style section in the body but Gmail still doesn’t.
  • Link to an external stylesheet. Not many email clients support this, best to just forget it.
  • Background-image / Background-position. Gmail is also the culprit on this one.
  • Clear your floats. Gmail again.
  • Margin. Yep, seriously, Hotmail ignores margins. Basically any CSS positioning at all doesn’t work.
  • Font-anything. Chances are Eudora will ignore anything you try to declare with fonts.

What you CAN do is:

In two words, inline styles. It’s not as awful as you might think, since we are basically developing a one-off email, inline styles are not nearly as egregious as using them on a website. Need a big green title for a block of text?

1
<h3 style="color: #1c70db;">NOW $159</h3>
  • The big can-do is images. Think creatively on what you can do with images.
  • Since you will be using tables, think gridular. Grids are designers friends, there is lots you can do with a grid.

Layout Techniques

Most people suggest using table, tr, and td tags for page layout within an HTML email. Tables are the most stable option, especially if you’re creating email messages that require a more complicated multi-column layout. Gmail simply removes div tags, and coverage in other clients like Hotmail is spotty. Also, floating div tags don’t work in several email clients, so any floating elements can be placed in a table.
Using a div tag for a layout or background color is a better option when you’re working with a single-column layout. Applying styles to div tags using inline styles versus a style tag will help keep that style in place when the email ships out to the masses.

On the bright side, you can scale back on div tags if they’re causing problems, and use the almost universally supported table, tr, and td tags for anything you can’t accomplish reliably with a div.

Sending Emails with HTML and CSS in WordPress

First we need to tell WordPress what content type are we going to use for our emails:

1
2
3
4
5
add_filter ("wp_mail_content_type", "cvf_mail_content_type");
function cvf_mail_content_type() {

    return "text/html";
}

Then let’s modify the wp_mail function by adding some html contents and css on the $message parameter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
function cvf_email($to) {
   
    $from = get_option('admin_email');
    $headers = 'From: Carlo Fontanos <"' . $from . '">';
    $subject = "Welcome to CarloFontanos ".$username."!";
   
    ob_start();
    // Include header contents
    include("email_header.php");   
    ?>
   
    <p>A very special welcome to you, <strong style="color:orange"><?php echo $username ?></strong>. Thank you for joining CarloFontanos.com!</p>
    <p>We hope you enjoy your stay at CarloFontanos.com. If you have any problems, questions, opinions, praise, comments, suggestions, please feel free to contact us at any time</p>
       
    <?php
    // Include footer contents
    include("email_footer.php");

    $message = ob_get_contents();
   
    ob_end_clean();

    wp_mail($to, $subject, $message, $headers);
}

Other Tips from Chris Coyier

  • Remember to use full paths to images, not relative paths. (e.g. http://www.yourserver.com/email/images/logo.gif). Also, link to images from your own server, not anyone elses.
  • Check with your ISP before you go out sending thousands and thousands of emails, they might think you are a spammer.
  • Test, test, and test again with as many different email clients as you can possibly get access to. You will definetly want to test the major online clients like Gmail, Yahoo, and Hotmail, but also definitely check Outlook, Mail.app, and as many other desktop clients as possible.
  • Don’t go over 600px in width. Even that is pushing it. If your design can handle it, 440px is closer to ideal.
  • Think of any extra CSS you may use as upward-compatibility. You can always include some header style CSS if you want, but think of it as a bonus for people using email clients that support it. Then turn it off completely and make sure the design still makes sense.
  • Try not to look like SPAM. Pretty obvious, but just writing good code and honest copy should keep you out of the can here. Your HTML email is definitely NOT the place for a Viagra joke.
  • Just like in web design, it doesn’t hurt to think above the fold. Meaning what users will see before they have to scroll.
  • Use your footer like a footer. This is a great place for lots of things including phone numbers and addresses, about information, unsubscribe options, and perhaps a little reminder of what this email is and why the reader is on the list.
  • OBEY THE LAW. The CAN-SPAM act became law on Jan. 1, 2004. It says there many things you must do as a commercial email-er. Highlights are basically don’t be deceptive, and that you MUST include a physical mailing address as well as a working unsubscribe link.

Add a Widget Area in your WordPress Admin Dashboard

It would be nice to have a Widget that displays statistics of your Plugin data, statistics in chart form, or just summary of calculated data in table format. In this tutorial I will be showing you how to add a simple widget into your Admin Dashboard that will appear on top of all the other Dashboard Widgets.

In WordPress we have this built in function: “wp_add_dashboard_widget();” which automatically registers a new Widget into the Admin Dashboard, all we have to do is provide the parameters and hook it into the “wp_dashboard_setup”.

Place this block of code into your theme’s functions.php (Explanations are on the code)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
add_action('wp_dashboard_setup', 'example_add_dashboard_widgets');
function example_add_dashboard_widgets() {
    wp_add_dashboard_widget( 'my_dashboard_widget', 'My Dashboard Widget', 'my_dashboard_widget_function' );
   
    // Globalize the metaboxes array, this holds all the widgets for wp-admin
    global $wp_meta_boxes;
   
    // Get the regular dashboard widgets array with our new widget appearing at the very end
    $normal_dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
   
    // Backup and delete our new dashboard widget from the end of the array
    $example_widget_backup = array( 'example_dashboard_widget' => $normal_dashboard['example_dashboard_widget'] );
    unset( $normal_dashboard['example_dashboard_widget'] );
 
    // Merge the two arrays together so our widget is at the beginning
    $sorted_dashboard = array_merge( $example_widget_backup, $normal_dashboard );
 
    // Save the sorted array back into the original metaboxes
    $wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard;
}

function my_dashboard_widget_function() {
    // You can remove this line and start placing your own codes.
    echo "Start getting your hands dirty on this area.";
}

If you want to remove all the other Widgets that you no longer need, you can click on the “Screen Options” found at the top-right portion of your Admin Dashboard Screen and just uncheck all the other widgets.

In some situations, especially on multi-user blogs, it may be useful to completely remove widgets from the interface. Each individual user can, by default, turn off any given widget using the Screen Options tab at the top, but if you have a lot of non-technical users it might be nicer for them to not see it at all.

You can use this block of code to remove all widgets in your Admin Dashboard:

1
2
3
4
5
6
7
8
9
10
11
12
function remove_dashboard_meta() {
    remove_meta_box( 'dashboard_incoming_links', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_plugins', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_primary', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_secondary', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );
    remove_meta_box( 'dashboard_recent_drafts', 'dashboard', 'side' );
    remove_meta_box( 'dashboard_recent_comments', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_right_now', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_activity', 'dashboard', 'normal');//since 3.8
}
add_action( 'admin_init', 'remove_dashboard_meta' );

You can now use this as your base code for implementing more sophisticated statistics into your own Dashboard Widget!

Here is a sample jQuery PIE chart that I have integrated into my Dashboard widget which shows the statistics of all browsers that where used by viewers of my website.

dashboard-browser-pie-widget

WordPress Frontend AJAX Pagination

ajax-pagination-wordpress-front-end

Demo

Tutorial

Step 1: Create a custom page in WordPress, if you do not know what a custom WordPress page is, read on this link: http://codex.wordpress.org/Template_Hierarchy

Step 2: Add this code into your custom page (Explanations are on the codes)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php get_header(); ?>

    <div class="col-md-12 content">
        <div class = "inner-box content no-right-margin darkviolet">
            <script type="text/javascript">
            jQuery(document).ready(function($) {
                // This is required for AJAX to work on our page
                var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
               
                function cvf_load_all_posts(page){
                    // Start the transition
                    $(".cvf_pag_loading").fadeIn().css('background','#ccc');
                   
                    // Data to receive from our server
                    // the value in 'action' is the key that will be identified by the 'wp_ajax_' hook
                    var data = {
                        page: page,
                        action: "demo-pagination-load-posts"
                    };
                   
                    // Send the data
                    $.post(ajaxurl, data, function(response) {
                        // If successful Append the data into our html container
                        $(".cvf_universal_container").html(response);
                        // End the transition
                        $(".cvf_pag_loading").css({'background':'none', 'transition':'all 1s ease-out'});
                    });
                }
               
                // Load page 1 as the default
                cvf_load_all_posts(1);
               
                // Handle the clicks
                $('.cvf_universal_container .cvf-universal-pagination li.active').live('click',function(){
                    var page = $(this).attr('p');
                    cvf_load_all_posts(page);
                   
                });
                           
            });
            </script>
            <div class = "cvf_pag_loading">
                <div class = "cvf_universal_container">
                    <div class="cvf-universal-content"></div>
                </div>
            </div>
           
        </div>     
    </div>
   
<?php get_footer(); ?>

We might have different page structures so feel free to edit the code.

Step 3: Add this block of code into your functions.php (Explanations are on the codes)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Receive the Request post that came from AJAX
add_action( 'wp_ajax_demo-pagination-load-posts', 'cvf_demo_pagination_load_posts' );
// We allow non-logged in users to access our pagination
add_action( 'wp_ajax_nopriv_demo-pagination-load-posts', 'cvf_demo_pagination_load_posts' );
function cvf_demo_pagination_load_posts() {
   
    global $wpdb;
    // Set default variables
    $msg = '';
   
    if(isset($_POST['page'])){
        // Sanitize the received page  
        $page = sanitize_text_field($_POST['page']);
        $cur_page = $page;
        $page -= 1;
        // Set the number of results to display
        $per_page = 5;
        $previous_btn = true;
        $next_btn = true;
        $first_btn = true;
        $last_btn = true;
        $start = $page * $per_page;
       
        // Set the table where we will be querying data
        $table_name = $wpdb->prefix . "posts";
       
        // Query the necessary posts
        $all_blog_posts = $wpdb->get_results($wpdb->prepare("
            SELECT * FROM "
. $table_name . " WHERE post_type = 'post' AND post_status = 'publish' ORDER BY post_date DESC LIMIT %d, %d", $start, $per_page ) );
       
        // At the same time, count the number of queried posts
        $count = $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(ID) FROM "
. $table_name . " WHERE post_type = 'post' AND post_status = 'publish'", array() ) );
       
        /**
         * Use WP_Query:
         *
        $all_blog_posts = new WP_Query(
            array(
                'post_type'         => 'post',
                'post_status '      => 'publish',
                'orderby'           => 'post_date',
                'order'             => 'DESC',
                'posts_per_page'    => $per_page,
                'offset'            => $start
            )
        );
           
        $count = new WP_Query(
            array(
                'post_type'         => 'post',
                'post_status '      => 'publish',
                'posts_per_page'    => -1
            )
        );
        */

       
        // Loop into all the posts
        foreach($all_blog_posts as $key => $post):
           
            // Set the desired output into a variable
            $msg .= '
            <div class = "col-md-12">      
                <h2><a href="'
. get_permalink($post->ID) . '">' . $post->post_title . '</a></h2>
                <p>'
. $post->post_excerpt . '</p>
            </div>'
;
           
        endforeach;
       
        // Optional, wrap the output into a container
        $msg = "<div class='cvf-universal-content'>" . $msg . "</div><br class = 'clear' />";
       
        // This is where the magic happens
        $no_of_paginations = ceil($count / $per_page);

        if ($cur_page >= 7) {
            $start_loop = $cur_page - 3;
            if ($no_of_paginations > $cur_page + 3)
                $end_loop = $cur_page + 3;
            else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
                $start_loop = $no_of_paginations - 6;
                $end_loop = $no_of_paginations;
            } else {
                $end_loop = $no_of_paginations;
            }
        } else {
            $start_loop = 1;
            if ($no_of_paginations > 7)
                $end_loop = 7;
            else
                $end_loop = $no_of_paginations;
        }
       
        // Pagination Buttons logic    
        $pag_container .= "
        <div class='cvf-universal-pagination'>
            <ul>"
;

        if ($first_btn && $cur_page > 1) {
            $pag_container .= "<li p='1' class='active'>First</li>";
        } else if ($first_btn) {
            $pag_container .= "<li p='1' class='inactive'>First</li>";
        }

        if ($previous_btn && $cur_page > 1) {
            $pre = $cur_page - 1;
            $pag_container .= "<li p='$pre' class='active'>Previous</li>";
        } else if ($previous_btn) {
            $pag_container .= "<li class='inactive'>Previous</li>";
        }
        for ($i = $start_loop; $i <= $end_loop; $i++) {

            if ($cur_page == $i)
                $pag_container .= "<li p='$i' class = 'selected' >{$i}</li>";
            else
                $pag_container .= "<li p='$i' class='active'>{$i}</li>";
        }
       
        if ($next_btn && $cur_page < $no_of_paginations) {
            $nex = $cur_page + 1;
            $pag_container .= "<li p='$nex' class='active'>Next</li>";
        } else if ($next_btn) {
            $pag_container .= "<li class='inactive'>Next</li>";
        }

        if ($last_btn && $cur_page < $no_of_paginations) {
            $pag_container .= "<li p='$no_of_paginations' class='active'>Last</li>";
        } else if ($last_btn) {
            $pag_container .= "<li p='$no_of_paginations' class='inactive'>Last</li>";
        }

        $pag_container = $pag_container . "
            </ul>
        </div>"
;
       
        // We echo the final output
        echo
        '<div class = "cvf-pagination-content">' . $msg . '</div>' .
        '<div class = "cvf-pagination-nav">' . $pag_container . '</div>';
       
    }
    // Always exit to avoid further execution
    exit();
}

You can add some styling to your pagination by appending this block of CSS code into your style.css

1
2
3
4
5
6
.cvf_pag_loading {padding: 20px;}
.cvf-universal-pagination ul {margin: 0; padding: 0;}
.cvf-universal-pagination ul li {display: inline; margin: 3px; padding: 4px 8px; background: #FFF; color: black; }
.cvf-universal-pagination ul li.active:hover {cursor: pointer; background: #1E8CBE; color: white; }
.cvf-universal-pagination ul li.inactive {background: #7E7E7E;}
.cvf-universal-pagination ul li.selected {background: #1E8CBE; color: white;}

Now you can start using this as your base AJAX Pagination structure to implement more sophisticated logics.

Insert PHP Code to your WordPress Post or Page

php-code-pic

Add this code snippet into your functions.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function cvf_insert_php($content) {

    $cvf_content = $content;
    preg_match_all('!\[php[^\]]*\](.*?)\[/php[^\]]*\]!is',$cvf_content,$cvf_matches);
    $cvf_nummatches = count($cvf_matches[0]);
    for( $cvf_i=0; $cvf_i<$cvf_nummatches; $cvf_i++ ) {
        ob_start();
        eval($cvf_matches[1][$cvf_i]);
        $cvf_replacement = ob_get_contents();
        ob_clean();
        ob_end_flush();
        $cvf_search = quotemeta($cvf_matches[0][$cvf_i]);
        $cvf_search = str_replace('/',"\".'/',$cvf_search);
        $cvf_content = preg_replace("
/$cvf_search/",$cvf_replacement,$cvf_content,1);
    }
    return $cvf_content;
}

add_filter( 'the_content', 'cvf_insert_php', 9 );

Then on your WP editor, you can start your PHP code by using the shortcode: “[php]” instead of “<?php”. The ending tag of your PHP would be “[/php]” and NOT “?>”

Implementing AJAX in WordPress

Ajax1

There are plenty of sources on the Web that cover this topic, when I first began developing for WordPress I was left scratching my head understanding methods that made the process easy and within WordPress best practices. Finally I came up with the most simpliest and standard way of implementing AJAX in WordPress.

NOTE: This article assumes you have a good understanding of jQuery and AJAX (and of course, PHP)

Using Ajax on the Administration Side

Since WordPress 2.8, ajaxurl is always defined in the admin header and points to admin-ajax.php

Here’s a short example. All this will be in one file. First, add some javascript that will trigger the AJAX request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript" >
jQuery(document).ready(function($) {
   
     // the value of 'action' is the key that will be identified by the 'wp_ajax_' hook
    var data = {
        'action': 'my_action',
        'message': 123
    };
   
    // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
    $.post(ajaxurl, data, function(response) {
        // Output the response which should be 'Hellow World'
        alert(response);
    });
});
</script>

Then, set up a PHP function that will handle the request:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
add_action( 'wp_ajax_my_action', 'my_action_callback' );
function my_action_callback() {
    // Check if set and if the received value is 123
    if (isset($_POST['message']) && $_POST['message'] == 123 ) {
        echo 'Hello World';
    }
    // Always exit to avoid further execution
    exit();
   
}
?>

Using Ajax on the Front-end Side

By default, the wp_ajax_ displays the output only to logged-in users, if you want it to fire on the front-end for both visitors and logged-in users, simply add this additional hook into your action handler:

1
add_action( 'wp_ajax_nopriv_my_action', 'my_action_callback' );

Unlike on the admin side, the ajaxurl javascript global does not get automatically defined for you, a quick fix for this is to create a variable which contains the admin-ajax.php

1
2
3
4
5
var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
$.post(ajaxurl, data, function(response) {
    // Output the response which should be 'Hellow World'
    alert(response);
});

Error Return Values

If the Ajax request fails in wp-admin/admin-ajax.php, the response will be -1 or 0, depending on the reason for the failure. Additionally, if the request succeeds, but the Ajax action does not match a WordPress hook defined with add_action(‘wp_ajax_(action)’, …) or add_action(‘wp_ajax_nopriv_(action)’, …), then admin-ajax.php will respond 0

Fix 404 Page Not Found in all Pages in WordPress

Moving files from FTP to localhost usually cause this problem to happen.

To fix this problem, simply add the following block of code into your .htaccess file. If you do not have an .htaccess file, create it.

1
2
3
4
5
6
7
8
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /project_name/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /project_name/index.php [L]
</IfModule>

Make sure to change ‘project_name’ with the folder name of your project.

Set a Default Featured Image for your Posts in WordPress

Simply add the following code to your functions.php

1
2
3
4
5
6
7
8
9
10
function wpse55748_filter_post_thumbnail_html( $html ) {
    // If there is no post thumbnail,
    // Return a default image
    if ( '' == $html ) {
        return '<img src="' . get_template_directory_uri() . '/images/default-thumbnail.png" width="150px" height="100px" class="image-size-name" />';
    }
    // Else, return the post thumbnail
    return $html;
}
add_filter( 'post_thumbnail_html', 'wpse55748_filter_post_thumbnail_html' );

Be sure to replace the default-thumbnail.png with your desired default post thumbnail and adjust the width and height.

Create a Custom Post Type in WordPress

Suppose you want your website to have a section for Game Articles. By using Custom Post Types you can create a new type of item like Posts and Pages, which will contain a different set of data. It will have an administration menu and a dedicated editing page for your blog posts.

custom-post-type

Custom Post Types help us to keep different types of posts in different buckets. It separates our regular posts from others. Simple enough!

Bellow is a list of Default Post Types in WordPress.

Post (Post Type: ‘post’)
Page (Post Type: ‘page’)
Attachment (Post Type: ‘attachment’)
Revision (Post Type: ‘revision’)
Navigation menu (Post Type: ‘nav_menu_item’)

A custom post type can be added to WordPress via the register_post_type() function. This function allows you to define a new post type by its labels, supported features, availability and other specifics. Do pay close attention to not having your custom post type identifier exceed 20 characters, as the post_type column in the database is currently a VARCHAR field of that length.

So let’s register a new post type:

1
register_post_type('articles', $articles_args);

The register_post_type function does most of the work for us. As soon as it is called it prepares the WordPress environment for a new custom post type including the different sections in the admin. This function takes two arguments: the first one is an unique name of the custom post type (for this tutorial, we called it “articles”) and the second one an array demonstrating the properties of the new custom post type.

Now let’s define the properties of our post type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
$articles_args = array(
    'labels'                => array(
        // These are all the labels of our custom post type, you do not have to worry about how you going to layout or create each menu because WordPress already does that for us.
        'name'                  => _x('Game Articles', 'post type general name'),
        'singular_name'         => _x('Game Articles', 'post type singular name'),
        'all_items'             => _x('My Game Articles', 'post type name'),
        'add_new'               => _x('Add New', 'articles'),
        'add_new_item'          => __("Add New Article"),
        'edit_item'             => __("Edit Article"),
        'new_item'              => __("New Article"),
        'view_item'             => __("View Article"),
        'search_items'          => __("Search Article"),
        'not_found'             => __('No articles found'),
        'not_found_in_trash'    => __('No articles found in Trash'),
        'parent_item_colon'     => ''      
    ),
    'public'                => true,
    'publicly_queryable'    => true,
    'show_ui'               => true,
    'query_var'             => true,
    'rewrite'               => true,
    'hierarchical'          => false,
    'menu_position'         => null,
    'capability_type'       => 'post',
    //Define the list of meta boxes that our post type is going to support.
    'supports'              => array('title', 'excerpt', 'editor', 'thumbnail', 'comments'),
    'menu_icon'             => get_bloginfo('template_directory') . '/images/custom-post-icon.png', //16x16 png if you want an icon
    'map_meta_cap' => true,
   
    /*
    Set which capabilities will be associated to our new custom post type.
    By default, seven keys are accepted as part of the capabilities array:
        - read - Controls whether objects of this post type can be read.
        - delete_posts - Controls whether objects of this post type can be deleted.
        - delete_private_posts - Controls whether private objects can be deleted.
        - delete_published_posts - Controls whether published objects can be deleted.
        - delete_others_posts - Controls whether objects owned by other users can be can be deleted. If the post type does not support an author, then this will behave like delete_posts.
        - edit_private_posts - Controls whether private objects can be edited.
        - edit_published_posts - Controls whether published objects can be edited.
   
    I have already defined bellow all the capabilities that we will be needing for our custom post type. Please do note the use of singular and plural (article, articles).
    */

    'capabilities' => array(
        'edit_post'                 => 'edit_article',
        'read_post'                 => 'read_article',
        'delete_post'               => 'delete_articles',
        'edit_posts'                => 'edit_articles',
        'edit_others_posts'         => 'edit_others_articles',
        'publish_posts'             => 'publish_articles',
        'read_private_posts'        => 'read_private_articles',
        'delete_posts'              => 'delete_articles',
        'delete_private_posts'      => 'delete_private_articles',
        'delete_published_posts'    => 'delete_published_articles',
        'delete_others_posts'       => 'delete_others_articles',
        'edit_private_posts'        => 'edit_private_articles',
        'edit_published_post'       => 'edit_published_article'
    ),
    'capability_type' => array('article', 'articles')
);

Add your custom admin columns (Optional):

1
2
3
4
5
6
7
8
9
10
function mmo_add_new_articles_columns( $columns ){
    $columns = array(
        'cb'                =>        '<input type="checkbox">',
        'title'             =>        'Game Article Title',
        'author'            =>        'Author',
        'date'              =>        'Date'
    );
    return $columns;
}
add_filter('manage_edit-articles_columns', 'mmo_add_new_articles_columns');

Create a Submenu Page in WordPress Admin

This can be useful when you plan on creating a simple plugin-like page that doesn’t require an installation, simply add the codes to your functions.php and it will appear right away on your admin page.

First we register our submenu by hooking it to the admin_menu

1
2
3
4
5
add_action('admin_menu', 'register_custom_submenu');

function register_custom_submenu() {
add_submenu_page( 'tools.php', 'Custom Submenu', 'Custom Submenu', 'administrator', 'custom-submenu', 'custom_submenu' );
}

Let’s review the above code

1st Parameter of the add_submenu_page is the slug name for the parent menu (or the file name of a standard WordPress admin page). Use NULL or set to ‘options.php’ if you want to create a page that doesn’t appear in any menu.

2nd Parameter – the text to be displayed in the title tags of the page when the menu is selected

3rd Parameter – This is simply the text to be used for the menu

4th Parameter – The capability required for this menu to be displayed to the user. Check out the list of capabilities by following this link: List of Capabilities

5th Parameter – The slug name to refer to this menu by (should be unique for this menu). If you want to NOT duplicate the parent menu item, you need to set the name of the $menu_slug exactly the same as the parent slug.

6th Parameter – The function to be called to output the content for this page. The codex say this is not required, but of course we have to create this since this will be the function that will be outputting our page contents.

So now that we have registered our menu, we can simply create the function custom_submenu(); where we will be placing our contents

1
2
3
4
function custom_submenu() {
echo '<h2>Custom Submenu</h2>';
echo '<p>This where your custom settings go.</p>;
}

Checkout my other WordPress Tutorials by following this LINK

Exporting Reports to CSV with WordPress

For example you want to export a report containing posts under a specific category ‘uncategorized’ while allowing you to filter the desired date range. Here is how we do it:

download-reports

First we need to enqueue the date picker JS file so that we can use it to our form.

1
2
3
4
5
6
add_action( 'init', 'enqueue_jquery_scripts' );
function enqueue_jquery_scripts() {

    wp_enqueue_script('jquery-ui-datepicker');
    wp_enqueue_style('jquery-ui-css', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css');
}

Then on our custom page we include a custom script to generate the class that we will be using for our date picker input field.

1
2
3
4
5
6
7
<script type="text/javascript">
    jQuery(document).ready(function($) {
        $('.custom_date').datepicker({
        dateFormat : 'yy-mm-dd'
        });
    });
</script>

Create the form:

1
2
3
4
5
6
7
8
<form method = "post">

    <input type="text" class="custom_date" name="date_from" placeholder = "Date From"/>
    <input type="text" class="custom_date" name="date_to" placeholder = "Date To"/>

    <input type="submit" name="btnDownload" id="submit" class="button-primary" value="Download">

</form>

Finally we create the method that will be handling the post submission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
if ( $_POST['btnDownload'] ) {

    global $wpdb;

    // Grab any post values you sent with your submit function
    $DownloadFrom = $_POST['date_from'];
    $DownloadTo = $_POST['date_to'];

    $sql_where = "";
    if ( $DownloadFrom != "" &amp;&amp; $DownloadTo != "" ) {
        $sql_where = $wpdb->prepare( " AND datetime BETWEEN %s AND %s ", $DownloadFrom, $DownloadTo );

    }

    // Build your query
    $MyQuery = $wpdb->get_results($wpdb->prepare("
    SELECT p.post_title, p.post_content, p.post_date, p.guid
    FROM wp_posts p
    LEFT JOIN wp_term_relationships tr ON tr.object_id = p.id
    WHERE post_status = 'publish'
    AND tr.term_taxonomy_id = 1
    AND p.id != 0"
.$sql_where, array() ) );

    // Process report request
    if ( !$MyQuery || empty($MyQuery) ) {

        die("Invalid parameters!");
        header ("Refresh: 1; url=" . home_url() . "/yourwebsite/yourformpage" );

    } else {
        // Prepare our csv download

        // Set header row values
        $csv_fields=array();
        $csv_fields[] = 'Title';
        $csv_fields[] = 'Description';
        $csv_fields[] = 'Date Posted';
        $csv_fields[] = 'Link';

        $output_filename = 'MyPosts_' . $DownloadFrom .'-'. $DownloadTo  . '.csv';
        $output_handle = @fopen( 'php://output', 'w' );

        header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
        header( 'Content-Description: File Transfer' );
        header( 'Content-type: text/csv' );
        header( 'Content-Disposition: attachment; filename=' . $output_filename );
        header( 'Expires: 0' );
        header( 'Pragma: public' );

        // Insert header row
        fputcsv( $output_handle, $csv_fields );

        // Parse results to csv format
        foreach ($MyQuery as $Result) {
            $leadArray = (array) $Result; // Cast the Object to an array
            // Add row to file
            fputcsv( $output_handle, $leadArray );
            }

        // Close output file stream
        fclose( $output_handle );

        die();
    }
}

Checkout my other WordPress Tutorials by following this LINK

How to use the JQuery Date Picker of WordPress

date-picker-jquery-ui

It would be nice to have a pop-up date picker for your input fields that requires date inputs rather than letting your users just type-in the whole date which becomes very tiring. So on this tutorial, I am going to show you how you can use the date-picker of WordPress on your front-end forms.

The first thing we need to do is to enqueue the necessary scripts to our wp_head() function

1
2
3
4
5
6
add_action('wp_head', 'cvf_ps_enqueue_datepicker');
function cvf_ps_enqueue_datepicker() {
    wp_enqueue_script('jquery-ui-datepicker');
    wp_enqueue_style('jquery-style', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css');
   
}

To call the date picker, simply append the script to your form and to define a unique html class or id to identify your target text field (in my case, I created a unique class “date_picker”)

1
2
3
4
5
6
7
8
9
<label for="reg_DateOfBirth"><?php _e('Date Of Birth (MM/DD/YYYY)'); ?></label><br />                          
<input type="text" name="DateOfBirth" class="form-control date_picker" value="<?php cvf_ps_retrieve_input('DateOfBirth'); ?>" />
<script type="text/javascript">
    jQuery(document).ready(function($) {
        $('.date_picker').datepicker({
            dateFormat : 'mm/dd/yy'
        });
    });
</script>