Creating a WP e-Commerce plugin: Settings Page API (fix)

Recently I’ve been developing a WordPress plugin to extend WP e-Commerce, a free storefront plugin. I used to use osCommerce, a robust PHP solution for shopping carts and heavily customized it. I was looking at this implementation of osCommerce (a great effort) as a WordPress plugin, but it was like reinventing the wheel–not worth it. WP e-Commerce is a native WordPress plugin, works well, has a huge user base and is (mostly) easy to extend.

The WP e-Commerce developers are kind enough to provide documentation for developers of plugins. Their page on “Settings Page API” describes the process of adding a tab to the WPEC settings page. While mostly straightforward there are two points that tripped me up:

  1. The  class WPSC_Settings_Tab_PLUGIN_SLUG extends WPSC_Settings_Tab needs to be inside the plugin tab function being described. This issue was mostly a result of my own dimness…
  2. This one seems to be a typo. The action hook tying in your plugin tab function as described is: However while using this action hook, my plugin settings tab was not accessible directly, only through an AJAX call after the settings page was already loaded. Also, I was using the optional  public function callback_submit_options()  to handle the form submission myself, and this was non-functional! Using the following hook fixed all this and resulted in a settings tab that functioned identically to the default tabs:

add_action( 'wpsc_register_settings_tabs', 'my_plugin_settings_tabs');  

It’s this the main query?

The main query in a WordPress site is the query based on the URL being accessed. When you access this post’s single page, the URL indicates the main query is the text you are reading right now. The “Recent Posts” widget on the sidebar is generated from a query, but not the main query.

There are a few methods to test for the “main query”. As of WordPress 3.3, there is a new way:

$query->is_main_query()

Examples: Using pre_get_posts hook | Using posts_where hook

There’s the old way (which is the basis for the above method):

In situations where I wanted to alter the content of a post (in the loop) if it was the main query, the above methods didn’t work reliably. I attempted to hook into the pre_get_posts action and set a global variable, which I would then test in the content filter function. I reasoned that the variable would be set to true before the loop, and then set to false if another query was called after that. The results however were always false.

My solution was to set a global variable with the post ID in a wp_head action, then test that against the post ID while in the loop (every time the_content filter is called.) Code below…

Now it’s quite possible this is not the most direct, efficient or reliable method. I welcome your comments if you have suggestions or concerns.

 

Displaying Custom Post Types by Term

It’s documented, it’s used, but it wasn’t obvious to me. After hunting around, I finally found what I needed in the first place I looked: the WordPress Codex.

This is the wp_query argument needed to display posts assigned a particular term:

Using the term ID as a cat ID wasn’t working. I came across the above here, and used it in two simple loops to display the custom posts under category headers.

Want to display posts in a nested hierarchy? This goal led me to what is probably a cleaner solution. The following code is adapted from Hierarchical Category List with Post Titles, and blended with the code above.

It’s in action here under Sites

Conditional Display of Widgets in WordPress

Many times I’ve needed to display or hide a widget based on some condition. Usually that condition is which page is being served. That’s easy enough with my own widgets by wrapping the output in an if statement employing a built-in WordPress Conditional Tag. The same can of course be done by directly editing 3rd-party widgets/plugins.

What if you need to change the behavior of a 3rd-party widget, but don’t want to alter it’s code? There’s a great plugin called Widget Logic that can help you do just that. The only caveat is that it directly runs code that you input. That opens up a security hole by executing code stored in the database. Not always a show stopper, but when developing for clients I try to play it safe.

Almost all the code and comments below are borrowed directly from the Widget Logic plugin. I’ve greatly simplified it to effect only the widget(s) and condition(s) hard coded. Also I removed the filtering function, as I was only using this to hide or show a widget. If you want to alter the output of a widget, it’s a pretty simple copy and paste from the original plugin to the second function.

Find the unique widget ID by inspecting the source of a page displaying the widget, like:

Insert the ID into the space specified. Next add in some conditional tags in the space specified like:

Slow Mail.app in Lion OS X 10.7

I recently helped a client with a terribly crippled Mail.app on their Mac. They had upgraded to 10.7 (Lion) from 10.6 (Snow Leopard) not long ago and are a heavy user of Mail.app. Unfortunately, as seamless as upgrading OS X often is, installing a new system over an existing one sometimes brings current issues along with, or create new ones.

They reported that Mail.app would get so slow that typing would be delayed to an unusable extent.

Here’s the initial course of action I suggested:

  1. Install any updates by going to “Software Update” in the Apple Menu
  2. Open “Disk Utility” (find in Spotlight menu or in Applications folder > Utilities)
    1. Select your harddrive on the left pane
    2. Click “Verify Disk”
    3. Note if there are any errors reported once the process is done
    4. Click “Repair Disk Permissions” (errors are probably not important with this step)

This last step, “Repair Disk Permissions” is a sort of cure-all for Mac problems. It isn’t a specific, targeted, remedy, but often can solve problems (especially after something like a system upgrade.) In the case at hand, this was enough to fix the poor performance in Mail.app.

If this hadn’t solved the problem, I would have continued with the following, proceeding down the list if the problem wasn’t remedied:

  1. Remove all Mail preferences
  2. Remove all Mail files in the user Libary (this will remove all locally stored emails! Procede with caution and back-up!)
  3. Create a new user account and manually migrate your data

Useful Terminal Commands for Remote Computing

These commands are especially useful in remote access situations, using ssh. See my post about SSH here. These commands all work in OS X 10.7 Lion. Many of them will not work in earlier versions of OS X.

Screen Sharing

on:

off:

Remote Management

on:

off:

FTP Server

on:

off:

Simple Web Server

on:

Remote Login (ssh)

I find “Remote Login” an incredibly useful tool. On a daily basis I employ it to transfer files, screen share, and directly access the shell. Turning this on (located in the Sharing pane of System Preferences) enables the local SSH server. This enables Secure Shell login, and SFTP access to transfer files. This technology is employed by thousands of machines on the internet. It is safe, secure and encrypted as long as it is properly configured and monitored. To keep your machine secure, consider some of these options or habits:

  • Turn if off when you don’t need it
  • Restrict which users can login remotely (right in the same Preference pane)
  • Change the default port (locally and/or at your router)
  • Monitor secure.log (manually with Console or with a 3rd-party app)
  • Setup SSH keys instead of using passwords

There may be some additional setup to access your machine via SSH remotely. If you’re behind a router, you’ll need to forward an external port to the internal SSH port (default is 22) of your machine. Any firewall will need to be configured to allow the appropriate incoming access. When your machine goes to sleep, it may or may not interfere with SSH access. Turn off sleep or turn on “Wake for network access” (Energy Saver preference pane).

Now that’s it’s running, how do you connect to the shell? In the terminal on another Mac or a linux computer, type:

ssh yourusername@[EXTERNAL IP ADDRESS]

…that’s it. If you’re using a Windows computer to connect, you’ll have to download a client.

If you want to transfer files securely, you can use the same command above but replace ssh with sftp . Then you’d have command line access to downloading and uploading files. Much easier would be to use an App like CyberDuck, or the popular Transmit for SFTP access.

One of the best features of an SSH connection is the ability to forward ports. You can forward ports in either direction, but most common is to forward a remote port to a port on the local machine. This can be setup in the initial command (ssh user@IP...) or while you’re in the session. To do the latter, once your in a remote session, type ~C at an empty prompt. If it doesn’t work, hit return to get a clear prompt and try again. This will drop you to a prompt like so:

To forward the remote port 5900 to the local port 5901, type the following a the ssh> prompt:
-L5901:localhost:5900 (no spaces)
You can continue to use the shell as normal, or not, but if you close it, so too does the “tunnel” you created close. In the example above, port 5900 is the default port for VNC, the screen sharing protocol. You could now enter “vnc://localhost:5901” in the Finder’s Connect to Server dialog on the local computer, and it would attempt to access the remote computer’s VNC server via the encrypted SSH connection.

Another type of port forwarding is “dynamic”. Using the above method, after connecting to a remote machine and typing ~C, type the following:
-D8888 (again, no spaces)
This creates a SOCKS proxy tunneled through the encrypted ssh connection. Next you would enter proxy settings, either for the entire local computer (in System Preferences) or in specific applications. Here’s how it would look in Firefox:

Now every connection Firefox makes is routed through the encrypted tunnel to the remote host. To the outside world (internet) your requests appears to come from the remote computer. Be aware that it takes proper configuration to mask all your activity. You must check on each application’s use of proxies. As well, DNS requests (resolving domain names to IPs) may not be masked by the proxy at all without additional steps.

Be sure to read the next article, Useful Terminal Commands for Remote Computing, for more ssh fun…