27 Aug 2013

Get List of Assigned Custom Taxonomies for Custom Post Type

Wordpress No Comments

If you ever need to pull a list of custom taxonomies for a custom post type (or even a custom taxonomy for a regular page or post), the following code will grab all taxonomies into a variable. Once you have this variable, simply loop through it to either display or further modify the data.

<?php
    $terms = get_the_terms( $post->ID , 'taxonomyname' );

    foreach ( $terms as $term ) {
        echo $term->name;
    }
?>

This code is also a good starting point for creating an autocomplete field on a form, which is one of my next projects.

06 Jun 2013

W3 Total Cache CDN to Local Fallback

jQuery, Wordpress No Comments

The W3 Total Cache WordPress plugin is a great solution for speeding up any website. The CDN (content delivery network) option allows you to offload images from your server onto another service, such as Amazon S3. This is a great way to decrease server load and improve the overall performance of your server.

However there is a major issue with using the CDN with W3. Whenever you add a new image, you either have to wait for W3 to run a sync with your CDN, or you have to manually go into W3 and initiate a file sync. Between this time the image will appear broken.

The Solution

Using the imagesloaded plugin (included), we can quickly loop through the loaded images and verify that they are not broken. The following jQuery solution will only work with the following criteria:

  • Your CDN runs as a CNAME in the following format: http://cdn.mydomain.com
<script type="text/javascript">
    /* imagesLoaded v3.0.2 */
    (function(e){"use strict";function t(){}function n(e,t){if(r)return t.indexOf(e);for(var n=t.length;n--;)if(t[n]===e)return n;return-1}var i=t.prototype,r=Array.prototype.indexOf?!0:!1;i._getEvents=function(){return this._events||(this._events={})},i.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},i.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},i.addListener=function(e,t){var i,r=this.getListenersAsObject(e);for(i in r)r.hasOwnProperty(i)&&-1===n(t,r[i])&&r[i].push(t);return this},i.on=i.addListener,i.defineEvent=function(e){return this.getListeners(e),this},i.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},i.removeListener=function(e,t){var i,r,s=this.getListenersAsObject(e);for(r in s)s.hasOwnProperty(r)&&(i=n(t,s[r]),-1!==i&&s[r].splice(i,1));return this},i.off=i.removeListener,i.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},i.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},i.manipulateListeners=function(e,t,n){var i,r,s=e?this.removeListener:this.addListener,o=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)s.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?s.call(this,i,r):o.call(this,i,r));return this},i.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},i.emitEvent=function(e,t){var n,i,r,s=this.getListenersAsObject(e);for(i in s)if(s.hasOwnProperty(i))for(n=s[i].length;n--;)r=t?s[i][n].apply(null,t):s[i][n](),r===!0&&this.removeListener(e,s[i][n]);return this},i.trigger=i.emitEvent,i.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},"function"==typeof define&&define.amd?define(function(){return t}):e.EventEmitter=t})(this),function(e){"use strict";var t=document.documentElement,n=function(){};t.addEventListener?n=function(e,t,n){e.addEventListener(t,n,!1)}:t.attachEvent&&(n=function(t,n,i){t[n+i]=i.handleEvent?function(){var t=e.event;t.target=t.target||t.srcElement,i.handleEvent.call(i,t)}:function(){var n=e.event;n.target=n.target||n.srcElement,i.call(t,n)},t.attachEvent("on"+n,t[n+i])});var i=function(){};t.removeEventListener?i=function(e,t,n){e.removeEventListener(t,n,!1)}:t.detachEvent&&(i=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var r={bind:n,unbind:i};"function"==typeof define&&define.amd?define(r):e.eventie=r}(this),function(e){"use strict";function t(e,t){for(var n in t)e[n]=t[n];return e}function n(e){return"[object Array]"===a.call(e)}function i(e){var t=[];if(n(e))t=e;else if("number"==typeof e.length)for(var i=0,r=e.length;r>i;i++)t.push(e[i]);else t.push(e);return t}function r(e,n){function r(e,n,o){if(!(this instanceof r))return new r(e,n);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=i(e),this.options=t({},this.options),"function"==typeof n?o=n:t(this.options,n),o&&this.on("always",o),this.getImages(),s&&(this.jqDeferred=new s.Deferred);var h=this;setTimeout(function(){h.check()})}function a(e){this.img=e}r.prototype=new e,r.prototype.options={},r.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);for(var i=n.querySelectorAll("img"),r=0,s=i.length;s>r;r++){var o=i[r];this.addImage(o)}}},r.prototype.addImage=function(e){var t=new a(e);this.images.push(t)},r.prototype.check=function(){function e(e,r){return t.options.debug&&h&&o.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var s=this.images[r];s.on("confirm",e),s.check()}},r.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emit("progress",this,e),this.jqDeferred&&this.jqDeferred.notify(this,e)},r.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emit(e,this),this.emit("always",this),this.jqDeferred){var t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},s&&(s.fn.imagesLoaded=function(e,t){var n=new r(this,e,t);return n.jqDeferred.promise(s(this))});var f={};return a.prototype=new e,a.prototype.check=function(){var e=f[this.img.src];if(e)return this.useCached(e),void 0;if(f[this.img.src]=this,this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this.proxyImage=new Image;n.bind(t,"load",this),n.bind(t,"error",this),t.src=this.img.src},a.prototype.useCached=function(e){if(e.isConfirmed)this.confirm(e.isLoaded,"cached was confirmed");else{var t=this;e.on("confirm",function(e){return t.confirm(e.isLoaded,"cache emitted confirmed"),!0})}},a.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit("confirm",this,t)},a.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},a.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindProxyEvents()},a.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindProxyEvents()},a.prototype.unbindProxyEvents=function(){n.unbind(this.proxyImage,"load",this),n.unbind(this.proxyImage,"error",this)},r}var s=e.jQuery,o=e.console,h=o!==void 0,a=Object.prototype.toString;"function"==typeof define&&define.amd?define(["eventEmitter","eventie"],r):e.imagesLoaded=r(e.EventEmitter,e.eventie)}(window);

    //Check CDN image references
    imagesLoaded('body', function() {
        for(i = 0; i <= document.images.length; i++) {
            if(typeof document.images[i] != "undefined") {
                var source = document.images[i].src;
                if(source.indexOf("cdn.") !== -1) {
                    if (!document.images[i].complete || typeof document.images[i].naturalWidth == "undefined" || document.images[i].naturalWidth == 0) {
                        document.images[i].src = source.replace(document.location.protocol + '//cdn.',document.location.protocol + '//');
                    }
                }
            }
        }
    });

    //Check all other CDN references (optional)
    var links = document.getElementsByTagName('a');

    for(var i = 0; i < links.length; i++) {
        if(typeof links[i] != "undefined") {
            //href link?
            if(typeof links[i].href != "undefined") {
                var source = links[i].href;
                if(source.indexOf("cdn.") !== -1) {
                    var http = new XMLHttpRequest();
                    http.open('GET', source, false);
                    http.send();
                    if(http.status != 200) {
                        links[i].href = source.replace(document.location.protocol + '//cdn.',document.location.protocol + '//');
                    }
                }
            } else if (typeof links[i].src != "undefined") {
                var source = links[i].src;
                if(source.indexOf("cdn.") !== -1) {
                    var http = new XMLHttpRequest();
                    http.open('GET', source, false);
                    http.send();
                    if(http.status != 200) {
                        links[i].src = source.replace(document.location.protocol + '//cdn.',document.location.protocol + '//');
                    }
                }
            }
        }
    }
</script>

Place this code directly above the </body> tag on your website.

Next Step: CDN Cross Domain Policy

If you decide to keep all code below the “//Check all other CDN references (optional)” line, you will need to setup your CDN to accept cross domain ajax requests. For Amazon S3 this is done by editing CORS (Cross Origin Resource Sharing).

To do that, click on a bucket then click “Properties”. Under the “Permissions” area, click “Add CORS Configuration”. In there copy/paste the following configuring, editing “mydomain.com” to match your website’s domain.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

NOTE: Each time this script runs and checks against each link it counts as a GET request on your CDN. You can remove the link checking portion of the code if you wish to conserve the amount of GET requests your website uses. The image portion of this script does not use any additional CDN GET requests.

14 Mar 2013

Easy Way to Run Yii’s yiic.php Command Line Tool (Windows)

Quick Tips, Yii 1 Comment

To avoid having to change any PATH variables in your windows installation, you can run yiic.php commands by defining the exact path to your php.exe file in your PHP installation.

C:/path/to/php/php.exe C:/path/to/yii/framework/yiic.php webapp C:/path/to/website/folder

18 Feb 2013

WordPress Custom Post Type Permalink not working on 3.5

Wordpress 2 Comments

This was a fun one. I created a custom post type on WordPress 3.5.1 and everything appeared like it should be working. The permalink was displaying correctly underneath the title of the post, however once I hit “View Post” it threw me to a 404 page.

The solution was to go to Settings -> Permalinks, then hit “Save Changes”.

It seems that WordPress requires some sort of undocumented refresh of the permalinks after you create a custom post type. Hopefully that saves someone else a lot of time!

13 Feb 2013

jQuery Focus / Scroll to element on page

jQuery, Quick Tips No Comments

A simple way to adjust the position of the browser window to an element of interest is by using the jQuery animate function. Adjust the selector (#focus_element) in the following code to an element you want the screen to scroll to.

$('html, body').animate({
    scrollTop: $("#focus_element").offset().top
}, 2000);

12 Nov 2012

Major Performance Optimization for Joomla 2.5 Websites

PHP No Comments

Joomla has always been a resource hogging monster, but I have recently discovered its default behavior is loading a lot of extra javascript files on the frontend. If you are using a template and extensions that rely on jQuery instead of Mootools, you can remove 332.3kB on every page load with a simple fix. This fix resulted in a 33% reduction in page size for the website I recently launched.

Place the following code in the main index.php file of your template (i.e. /templates/beez/index.php), immediately following the code at the top: “defined(‘_JEXEC’) or die;”

if(JFactory::getUser()->guest == 1){
    JHtml::_('behavior.framework', true);

    unset($this->_scripts[JURI::root(true).'/media/system/js/mootools-core.js']);
    unset($this->_scripts[JURI::root(true).'/media/system/js/mootools-more.js']);
    unset($this->_scripts[JURI::root(true).'/media/system/js/core.js']);
    unset($this->_scripts[JURI::root(true).'/media/system/js/caption.js']);

    if (isset($this->_script['text/javascript']))
    {
       $this->_script['text/javascript'] = preg_replace('%window\.addEvent\(\'load\',\s*function\(\)\s*{\s*new\s*JCaption\(\'img.caption\'\);\s*}\);\s*%', '', $this->_script['text/javascript']);
       if (empty($this->_script['text/javascript']))
          unset($this->_script['text/javascript']);
    }
}

The first line of the code checks to see if the user is logged in before removing the Mootools-related files. Mootools is required for frontend editing, and this conditional ensures that the scripts are available to users who might use that feature.

18 Sep 2012

A “fix” for Nivo Slider not working in IE7 or IE8

HTML, jQuery No Comments

As I was performing browser-testing on a site that is nearing release I found that the Nivo Slider would sit there with a spinning loading icon on IE7 and 8. It worked perfectly on all other browsers (including IE9), and the developer tools in Internet Explorer were not throwing up any errors. I tried modifying the Nivo Slider call to no options, CSS changes, and changing the order of Nivo options without any luck.

The Solution

There was a form field in the footer of my page that had an extra quotation mark:

<input type="submit" id="site-index-redirect" value="Go"" />

Removing the extra quotation mark fixed the issue, and Nivo Slider began working again in Internet Explorer 7 and 8. Check the forms on your page for extra quotation marks, hopefully this saves someone a lot of time troubleshooting!

10 Sep 2012

Memcached (Memory-based cache) Caching for OpenCart

OpenCart 4 Comments

Current Version: v1.1 (9/9/2012)

Safely increase the speed of your store by converting OpenCart’s hard drive based caching approach to memory. This non-invasive caching uses OpenCart’s already existing caching mechanisms to implement Memcached. Additionally, this extension adds individual product and category caching to speed up every area of your website.

What is Memcached?

Memcached is a “high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.” It is used extensively by sites such as Facebook, Twitter, Youtube, and Wikipedia.

How does this compare to other OpenCart caching solutions?

1) Many other caching solutions are hard-drive based. While you may get to avoid database calls, you are going to be limited by the speed of your hard drive to serve that information. Also, any hard-drive based page cache extension will have a hard time with individual product page caching (creating, deleting, and modifying thousands and thousands of temporary files on your server is not a good idea).

2) Some solutions do far to much processing. This caching solution is for developers familiar with good practices in building a template. Your javascript and CSS files should already be minified on a production environment, and any script that does that on the fly is going to tax your CPU every page load.

3) This solution is not invasive to your OpenCart installation. It safely modifies the existing caching mechanism already in place, and leaves all functions and calls to the cache untouched.

4) Any additional extensions you have installed that use OpenCart’s cache will automatically switch to Memcached, no extra code required.

How does OpenCart benefit from Memcached?

By default, OpenCart’s caching mechanism stores all cache files in /system/cache. Each cache file is either a single database result, or a collection of several rows of database results. Because it reads and writes from that folder, its speed is limited by how fast the hard drive can react to many tiny files. Also, since we are dealing with so many tiny files, the OpenCart developers have opted to not include caching for individual product pages.

Memcache excels at handling these tiny database results. The speed of your server’s memory is going to be far faster than your hard drives as well, making the read and writes near instant. Now that the read/write speed has increased, we can safely cache thousands of product pages and categories.

Since this extension safely hooks in with the existing OpenCart cache, any changes you make to the site will invalidate the cache and rebuild the next time the pages are hit.

Features

  • Memcached (memory-based caching)
  • Major increase to site speed
  • Adds individual product page caching
  • Adds category caching
  • Template independent
  • Multilingual
  • Multi-store support

02 Apr 2012

Using Yii Framework Validators Outside of a Form

PHP, Yii 3 Comments

The Yii Framework comes bundled with a large collection of validation methods used to validate user input data in a form. In some situations you might want to validate data outside of a form, and instead of rolling your own validation methods it is best to hook into the existing Yii ones.

For example, in a method you want to check if a value is a valid email address, you can use the following code:

$validator = new CEmailValidator;

if(!$validator->validateValue($email_address))
{
   //invalid
}

For a list of all the Yii validators you can hook into, please visit the system.validators reference page.

21 Mar 2012

Phone Number HTML Links for Visitors on Mobile Devices

HTML, Website 4 Comments

Phone CallDisplaying mobile numbers as clickable links on mobile devices can be a very easy task. If your numbers are formatted in any of the following ways, the vast majority of your mobile visitors (iPhone and Android) will automatically see the phone number as a clickable link:

  • (555) 555-5555
  • 555 555 5555
  • 555.555.5555

If your phone numbers are not formatted this way you can add the following HTML around your phone numbers:

<a href="tel://555.555.5555">555.555.5555</a>

It is important that only your mobile visitors see this link, as the majority of desktop users will be greeted with an error message when clicking the link. Make sure the linked and non-linked versions of the phone number are swapped out accordingly depending on the device the user is browsing your website with.

For more information, please check out mobile Tuts+ Mobile Web Quick Tips post.