Easy WordPress Plugin Development (Part 4)
by Luke America on May 11, 2011
This is the fourth article in our plugin development tutorial series. In the last segment, we covered our plugin's primary file source code. You can access the linked table of contents for the entire series below.
Today, we'll complete our plugin's source code and review a few design techniques for Dashboard admin pages.
Table of Contents for this Tutorial
- Introduction: Plug-in Development Overview
- Plug-in Development Fundamentals
- Our Plug-in's Primary File Source Code
- Implementing Dashboard Admin Pages (you are here)
- Additional Plug-in Considerations
- Plug-in Testing Techniques
- Finalizing Our Plug-in
- (coming soon) Submitting Your Plug-in to WordPress
- (coming soon) Promoting Your Plug-in's Future
- (coming soon) Advanced Plug-in Techniques
Our Dashboard Admin Page PHP
Many admin pages contain both settings specific to the plug-in and some helpful usage information. For our basic plug-in, there are no settings. But, to make it easy for the user to get the most out of our shortcode, we offer a full page of useful tips.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?php /************************************************************************** ADMIN INITIALIZE **************************************************************************/ if ('wcs-qrc-options.php' == basename($_SERVER['SCRIPT_FILENAME'])) { die ('Please do not access this admin file directly. Thanks!'); } if (!is_admin()) {die('This plugin is only accessible from the WordPress Dashboard.');} if (!defined('WP_CONTENT_DIR')) define('WP_CONTENT_DIR', ABSPATH . 'wp-content'); if (!defined('WP_CONTENT_URL')) define('WP_CONTENT_URL', get_option('siteurl') . '/wp-content'); if (!defined('WP_ADMIN_URL')) define('WP_ADMIN_URL', get_option('siteurl') . '/wp-admin'); if (!defined('WP_PLUGIN_DIR')) define('WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins'); if (!defined('WP_PLUGIN_URL')) define('WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins'); ?> |
At the top of wcs-qrc-options.php, we have a short section of PHP code. First, we make sure that the file can not be loaded directly (starting at line 7) … eg, in the browser's address bar. Then, as an added security measure, we make sure that the file is being accessed from the WordPress Dashboard (line 13).
Next, we define some URL and directory constants (starting at line 16) … just in case they're not already defined. This is a good code segment to include in all plug-ins.
Our Dashboard Admin Page Content
The lower portion of this file contains the HTML that's output on our admin page. Some plug-ins include their HTML content inside of a function. But, for our simple usage, we include the HTML directly.
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 | <div class="wrap"> <table width="600" border="0" cellspacing="0" cellpadding="0"> <tr> <td> </td> <td> </td> </tr> <tr> <td colspan="2"><img width="600" height="128" src="<?php _e(WP_PLUGIN_URL); ?>/wcs-qr-code-generator/images/wcs-qrc-header.png" border="0" alt="WCS QR Code Generator Options" title="WCS QR Code Generator Options" /></td> </tr> <tr> <td><br/> <iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.wpcodesnippets.info%2Fblog%2Fwcs-qr-code-generator.html&layout=standard&show_faces=true&width=450&action=like&font=lucida+grande&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe></td> <td> </td> </tr> </table> <table cellpadding="0" cellspacing="0" border="0" style="width:600px; margin-left:5px; color:black; background:#F6F6F6; border:1px solid #606060; padding:4px; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px;"> <tr><td> <h2> About this Plug-in</h2> <div style="margin-left:20px; line-height:150%; font-size:14px;"> This plugin is a <b>QR Code</b> (Quick Response) generator for <b>mobile tagging</b>. It allows you to create one of the ever-popular QR Code images anywhere on a page/post or in a text widget. A <b>simple shortcode</b> defaults to generating the 2d code for the current page/post.<br/><br/> Plus, <b>attributes allow you to set</b> the: size, color, url, tooltip, image format (png or jpg), and error correction level ... along with an option to use a shortened url. The url attribute can be utilized instead as an email address, plain text, phone number, vcard, sms, <i>etc</i>. <br/><br/> </div> <h2> Links Related to this Plug-in</h2> <div style="margin-left:20px; line-height:150%; font-size:14px;"> <ol style="margin-left:40px;"> <li>Visit our web site, <a target="_blank" href="http://wpcodesnippets.INFO">wpCodeSnippets.INFO</a>, for lots of WordPress tips, tweaks, shortcodes, and plugins.</li> <li>View the prototype <a target="_blank" href="http://wpcodesnippets.info/blog/how-to-add-qr-codes-to-your-blog.html">source code</a> explanation for this plug-in. This page also includes a variety of useful information regarding QR Codes.</li> <li>View <a target="_blank" href="http://wpcodesnippets.info/blog/wcs-qr-code-generator.html">details for this plug-in</a> at our web site.</li> <li>Revisit the <a target="_blank" href="http://wordpress.org/extend/plugins/wcs-qr-code-generator/">plug-in download</a> page at the WordPress repository. In the sidebar on the right, click your "<b>My Rating</b>" preference (hopefully 5 stars).</li> <li>Did you find this plug-in useful? A small <a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=USD&business=LukeAmerica2020%40gmail.com&item_name=WP%20Code%20Snippets%20donation&amount=0.00&image_url=http://wpcodesnippets.info/files/paypal_header.png">donation</a> will support our continued WordPress development efforts. Enter any amount.</li> </ol> <br/> </div> <h2> Shortcode Usage Information</h2> <div style="margin-left:20px; line-height:150%; font-size:14px;"> Upon activation of this plug-in, you'll have access to a <b>new shortcode</b> that generates QR Code images. In its default usage, a QR Code is generated for the current page/post. Additionally, you can use the shortcode anywhere on a page/post or in a text widget.<br/><br/> <b>Basic Usage Syntax:</b><br/> <div style="margin-left:25px;">[wcs_qr_code]</div> <br/> <b>Example Usage:</b><br/> <div style="margin-left:25px;"> [wcs_qr_code <b>url=</b>'now is the time' <b>size=</b>'128' <b>color=</b>'darkblue']<br/> </div> <br/> <b>Available Attributes:</b><br/> <ol style="margin-left:40px;"> <li><b>url</b> is the content for the qr code image (defaults to the current page/post url)<br/>-- <i>more on this below</i></li> <li><b>size</b> is square image's size in pixels (default is '100')</li> <li><b>color</b> can be a 6-digit hex color value or an HTML color name (default is 'black')<br/></li> <li><b>ecl</b> is the error correction level (default is 'L|4')<br/>-- <i>more on this below</i><br/></li> <li><b>shorten</b> is a toggle for shortening the url (default is 'false')<br/></li> <li><b>format</b> is the image format (PNG or JPG) (default is 'png')<br/></li> <li><b>tooltip</b> is the image's tooltip text (default is none)<br/></li> </ol> <br/> <b>ECL Details:</b><br/> <div style="margin-left:25px;"> This plug-in utilizes the <a href="http://code.google.com/apis/chart/image/docs/gallery/qr_codes.html" target="_blank">Google Charts API</a>. You can view the various ECL settings <a href="http://code.google.com/apis/chart/image/docs/gallery/qr_codes.html#details" target="_blank">here</a>.<br/><br/> Essentially, the ECL code consists of two components: (1) the error correction level (L, M, Q, or H), and (2) the white border row/column width (1 - 40 rows, not pixels). </div> <br/> <b>URL Details:</b><br/> <div style="margin-left:25px;"> If omitted, the URL defaults to the current page/post url. You can also use any valid url or text (within the character limitation of the selected ECL attribute value).<br/><br/> When using an <b>email address</b>, a standard format is expected. For example <b>me@here.com</b> should be implemented as <b>mailto:me@here.com</b>.<br/><br/> <b>Telephone numbers</b> also have a standardized format. For example, <b>212-555-1212</b> should be implemented as <b>tel:+12125551212</b>.<br/><br/> An <b>SMS link</b> is encoded in a fashion similar to an email address. For example, a link to the number <b>12345</b> should be implemented as <b>sms:12345</b>.<br/><br/> These and many other possibilities are explained in detail <a href="http://code.google.com/p/zxing/wiki/BarcodeContents" target="_blank">here</a>.<br/> </div> <br/> <h2> Additional References</h2> <div style="margin-left:20px; line-height:150%; font-size:14px;"> Here's a site that DECODES a QR image to verify the data:<br/> <a href="http://zxing.org/w/decode.jspx" target="_blank">http://zxing.org/w/decode.jspx</a>.<br/><br/> This <a href="http://wpcodesnippets.info/blog/how-to-add-qr-codes-to-your-blog.html" target="_blank">page</a> offers a variety of information regarding QR Codes.<br/><br/> Here are a few sites that let you download a QR Code reader for your mobile device.<br/> <ol style="margin-left:40px;"> <li><a href="http://www.i-nigma.mobi/" target="_blank">i-nigma.mobi</a></li> <li><a href="http://reader.kaywa.com/" target="_blank">kaywa.com</a></li> <li><a href="http://get.neoreader.com/" target="_blank">neoreader.com</a></li> <li><a href="http://www.quickmark.com.tw/En/basic/index.asp" target="_blank">quickmark.com.tw</a></li> </ol> <br/> </div> </td></tr> </table> </div> <br/><br/> |
We start off with an identifying image in the header. This PNG file is in the images folder. Then, we output the content in a formatted table.
Our HTML is non-standard for an admin page. I personally like this because it stands out. Less than half of the plug-ins I've used do it this way.
How to Style Dashboard Admin Pages
As mentioned, we use non-standard styling with this plug-in. However, with a little tweaking, there are several useful styles available for us to utilize in Dashboard admin pages.
The following example demonstrates a few possibilities, accessing the admin stylesheets. Here, we show you how to display admin content in: (1) styled tables, (2) styled multi-column tables, (3) single column blocks using DIV's, and (4) double column blocks using DIV's. Plus, we include a couple of styled buttons.
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 | <div class="wrap"> <!-- ADMIN HEADER SAMPLE --> <?php screen_icon('ms-admin'); ?> <h2>Sample Dashboard Page</h2> <p> This page demonstrates several of the cool CSS features that are available for WordPress Dashboard admin pages. </p> <!-- ADMIN TABLES --> <h2>Using Tables</h2><br/> <table class="widefat"> <thead> <tr> <th>Content Header #1</th> </tr> </thead> <tr> <tbody> <td> Now is the time for all good men to come to the aid of their country. The quick red fox jumped over the lazy brown dog.<br/><br/> The quick red fox jumped over the lazy brown dog. Now is the time for all good men to come to the aid of their country.<br/><br/> </td> </tr> </tbody> </table> <br/> <table class="widefat"> <thead> <tr> <th>Content Header #2</th> </tr> </thead> <tr> <tbody> <td> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc arcu enim, adipiscing eget porta ut, tempor gravida sapien. Nulla ac venenatis dolor. Nam egestas ante ac nisi pellentesque congue.<br/><br/> Nulla et felis a mi euismod accumsan. Aenean quis urna ante. In vitae turpis sit amet odio tincidunt tincidunt at eget ipsum. Aliquam dapibus adipiscing orci, sit amet sodales quam tempus nec. Maecenas sed ante sem, at fringilla neque.<br/><br/> </td> </tr> </tbody> </table> <br/> <!-- ADMIN TABLES with COLUMNS --> <h2>Using a Table with Columns</h2><br/> <table class="widefat"> <thead> <tr> <th>This</th> <th>That</th> <th>The Other</th> </tr> </thead> <tfoot> <tr> <tr> <th>This</th> <th>That</th> <th>The Other</th> </tr> </tfoot> <tbody> <tr> <td>Now is the</td> <td>time for all</td> <td>good men to</td> </tr> <tr> <td>The quick red</td> <td>fox jumped over</td> <td>the lazy brown dog</td> </tr> <tr> <td>Lorem ipsum dolor</td> <td>Nunc arcu enim</td> <td>vitae turpis sit</td> </tr> </tbody> </table> <br/> <!-- ADMIN TABLES with DIV's - SINGLE COLUMN --> <h2>Using Div's - Single Column</h2><br/> <div class="metabox-holder"> <div class="postbox"> <h3 style="cursor:default;">Content Header "C"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Now is the time for all good men to come to the aid of their country. The quick red fox jumped over the lazy brown dog. </p> <p> The quick red fox jumped over the lazy brown dog. Now is the time for all good men to come to the aid of their country. </p> <p> <input class="button-primary" value="Primary Button"> <input class="button-secondary" value="Secondary Button"> </p> </div> </div> <div class="postbox"> <h3 style="cursor:default;">Content Header "D"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc arcu enim, adipiscing eget porta ut, tempor gravida sapien. Nulla ac venenatis dolor. Nam egestas ante ac nisi pellentesque congue. </p> <p> Nulla et felis a mi euismod accumsan. Aenean quis urna ante. In vitae turpis sit amet odio tincidunt tincidunt at eget ipsum. Aliquam dapibus adipiscing orci, sit amet sodales quam tempus nec. Maecenas sed ante sem, at fringilla neque. </p> </div> </div> </div> <!-- ADMIN TABLES with DIV's - TWO COLUMNS --> <h2>Using Div's - Two Columns</h2><br/> <div class="metabox-holder" style="width:65%; float:left; margin-right:10px;"> <div class="postbox"> <h3 style="cursor:default;">Content Header "C"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc arcu enim, adipiscing eget porta ut, tempor gravida sapien. Nulla ac venenatis dolor. Nam egestas ante ac nisi pellentesque congue. </p> <p> Nulla et felis a mi euismod accumsan. Aenean quis urna ante. In vitae turpis sit amet odio tincidunt tincidunt at eget ipsum. Aliquam dapibus adipiscing orci, sit amet sodales quam tempus nec. Maecenas sed ante sem, at fringilla neque. </p> </div> </div> <div class="postbox"> <h3 style="cursor:default;">Content Header "D"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Now is the time for all good men to come to the aid of their country. The quick red fox jumped over the lazy brown dog. </p> <p> The quick red fox jumped over the lazy brown dog. Now is the time for all good men to come to the aid of their country. </p> <p> <input class="button-primary" value="Primary Button"> <input class="button-secondary" value="Secondary Button"> </p> </div> </div> </div> <div class="metabox-holder" style="width:30%; float:left;"> <div class="postbox"> <h3 style="cursor:default;">Content Header "E"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc arcu enim, adipiscing eget porta ut, tempor gravida sapien. Nulla ac venenatis dolor. Nam egestas ante ac nisi pellentesque congue. </p> <p> Nulla et felis a mi euismod accumsan. Aenean quis urna ante. In vitae turpis sit amet odio tincidunt tincidunt at eget ipsum. Aliquam dapibus adipiscing orci, sit amet sodales quam tempus nec. Maecenas sed ante sem, at fringilla neque. </p> </div> </div> <div class="postbox"> <h3 style="cursor:default;">Content Header "F"</h3> <div class="inside" style="padding:0px 6px 0px 6px;"> <p> Now is the time for all good men to come to the aid of their country. The quick red fox jumped over the lazy brown dog. </p> <p> The quick red fox jumped over the lazy brown dog. Now is the time for all good men to come to the aid of their country. </p> </div> </div> </div> </div> <!-- required to clear for additional content --> <div style="clear:both;"></div> |
To the right is a screen capture of the Dashboard page that this styled HTML creates.
You can click this thumbnail to open the enlarged image in a new browser tab. Depending on your browser's settings, you might need to also click the enlargement to view it at full size.
Among the four admin content styles presented, you may well find one that suits the needs of your own plug-in.
This content is created with standard HTML and tweaked admin styles. The only exception is our use of the WordPress screen_icon function.
How to Use the screen_icon Function
Unfortunately, WordPress hasn't documented the screen_icon function. Further … and amazingly … looking at the function's source code is of little help. It's located in: /wp-admin/includes/template.php.
Here's what it does. It loads one of 12 icons from a css sprite image (/wp-admin/images/icons32-vs.png). Each icon represents a major section of the Dashboard menu system. The function takes one parameter, a slug that identifies the specific icon to use. And … you guessed it … these 12 slug names are not documented either.
But … we dug through the code to get the data. Here are the slugs and the icon that each displays. BTW, each image is a 32×32 pixel icon. And, the typical file association URL would start with http://TLD/wp-admin/.
| Slug Name | Description | File Association |
| 'edit' | push pin icon (as with “Posts”) | edit.php |
| 'edit-comments' | speech bubble icon (as with “Comments”) | edit-comments.php |
| 'edit-pages' | pages icon (as with “Pages”) | edit.php?post_type=page |
| 'link' | link icon (as with “Links”) | link-manager.php |
| 'index' | house icon (as with “Dashboard”) | index.php |
| 'ms-admin' | keys icon (as with “Network -> Sites”) | network/sites.php |
| 'options-general' | sliders icon (as with “Settings”) | options-general.php |
| 'plugins' | plug icon (as with “Plugins”) | plugins.php |
| 'themes' | properties icon (as with “Appearance”) | themes.php |
| 'tools' (or 'admin') | tools icon (as with “Tools”) | tools.php |
| 'upload' | media icon (as with “Media”) | upload.php |
| 'users' | people icon (as with “Users”) | users.php |
Additional Admin Page Functions
You may, in fact, want to include assorted user options on your admin page … which can be stored in and retrieved from the database. The following articles explain how to do this.
- WordPress: Creating Options Pages
- WordPress: Settings API
- Gneu.org: Intro to Settings API
- OttoPress.com: Settings API Tutorial
- PlanetOzh.com: Handling Plugins Options
- Presscoders.com: Settings API Explained
- TutsPlus.com: How to Create a Better Options Panel
Although the official documentation doesn't mention it, it's a very good idea to save the version number of your plugin as a (hidden) setting. This will be of use with future updates.
Admin Page Fade Out Alerts
You've probably seen plug-ins that displayed a “Settings saved.” message at the top of the admin page when you saved your settings. Writing this article, I was curious how to accomplish this … adding a fade out effect. So … I coded a little snippet that you might find useful.
The following code is added to wcs-qrc-options.php just above the original line 25, <div class="wrap">. The cool part is that with a few style tweaks and some jQuery trickery, the alert appears for 5 seconds and then it fades out for 3 seconds … removing itself from the page. Plus, when it's clicked by the user during those 8 seconds it closes itself instantly.
1 2 3 4 5 6 7 | <div id="message" class="updated below-h2 fade" style="margin-top:30px; margin-left:5px; width:600px; cursor:pointer;" onclick="jQuery('div#message').css('display','none');"> <p style="float:right; font-size:10px; font-variant:small-caps; color:#600000; padding-top:4px;">(close)</p> <p><b>Settings saved.</b></p> </div> <script type="text/javascript"> jQuery(document).ready(function($) {$(".fade").fadeTo(5000,1).fadeOut(3000);}); </script> |
Here's what the output looks like:

And, by simply changing “updated” on the first line to “updated highlight”, we get the following output.

Finally, by changing “updated” on the first line to “error”, we get this output.

How to Add Your RSS Feed on Your Admin Page
It can be a useful marketing strategy to include your blog's RSS feed on your plug-in's dashboard admin page. WordPress offers the undocumented wp_widget_rss_output function. However, it opens links within the current tab. And, it's probably not a good idea to discard someone's dashboard when they click on one of your article links.
Our wcsp_qrc_dashboard_get_rss_feed function resolves this shortcoming by inserting a target="_blank" attribute inside the HTML of the link code. We do this by buffering the content and using the handy preg_replace() function. Here's the source 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 | function wcsp_qrc_dashboard_get_rss_feed($url, $count=5, $summary=1, $author=1, $date=1) { // SITE FEED: // http://YOUR_TLD/feed // CATEGORY FEED: // http://YOUR_TLD/category/CAT_SLUG/feed // TAG FEED: // http://YOUR_TLD/tag/TAG_SLUG/feed // start output $output = '<div class="rss-widget">'; // buffer feed data so that we can change it ob_start(); wp_widget_rss_output(array( 'url' => $url, 'title' => '', 'items' => $count, 'show_summary' => $summary, 'show_author' => $author, 'show_date' => $date )); $rss_feed = ob_get_contents(); ob_end_clean(); // force links to open in a new tab $find_this = "/<a (.+?)[title='](.+?)['](.+?)>/i"; $rss_feed = preg_replace($find_this, '<a class=\'\\3 target="_blank">', $rss_feed); // exit $output .= $rss_feed . '</div>'; return $output; } |
Here's an example usage. You should implement your version of this example inside a formatted block on your admin page. And, the block should have a title … something like “Read Our Latest Articles”.
1 | <?php echo wcsp_qrc_dashboard_get_rss_feed('http://wpcodesnippets.info/feed', 10, 0, 0, 0); ?> |
Also, note that we are only displaying the link … when the plug-in user hovers over this link, the excerpt is displayed in a tooltip.
Continue Reading…
In our next article, we'll continue with Additional Plug-in Considerations.
And, in the previous article, was our Our Plug-in's Primary File Source Code.
Comments
Share Your Thoughts 7 Responses to “Easy WordPress Plugin Development (Part 4)”Trackbacks
Check out what others are saying about this post...-
[...] BlogIntroducing Extended ExcerptsThree Videos for DevelopersFive Easy Dictionary Lookup ShortcodesEasy WordPress Plugin Development (Part 4)Mastering Smart QuotesHow to Build an ‘Archives by …Shortcode Fundamentals (Part 1)A [...]
-
[...] the original post: Easy WordPress Plugin Development (Part 4) | WP Code Snippets This entry was posted in Techblog and tagged design, eyes, plugin installation, the-design, [...]
-
[...] Implementing Dashboard Admin Pages Our Dashboard Admin Page PHP · Our Dashboard Admin Page Content ·How to Style Dashboard Admin Pages · How to Use the screen_icon Function ·Additional Admin Page Functions [...]
Subscribe to our RSS feed
Follow Us on Twitter



This whole tutorial has lots of good info that I haven’t seen anywhere else. Wow.
Great tutorial … simply awesome.
I like looking through an article that will make men and women think.
Also, thanks for allowing for me to comment!