Hiding WordPress categories

Photo by John Poyntz Tyler
Photo by John Poyntz Tyler

When I wrote my first WordPress related post, I admitted that I was only doing it to attract traffic and it would be my last post on the subject. However, I start again. This time around, however, I want to talk about something which isn’t common knowledge and neither did I get any responses on the official WordPress forum regarding this.

Suppose you do not want some of your posts to appear anywhere: not the homepage, not the RSS feeds, not the archives: nowhere. However you DO want it to appear only when its linked to, as a single post on the page. I regularly need to do this, because some part of the post is more like an ‘addendum’ or when including everything would make the post too long.

There is a standard solution available on the forums: creating a plugin and adding code to this effect:


function hs_cat_exclude($query)
{
if ($query->is_feed || $query->is_home || $query->is_archive ) {
$hsq = $query->gt;get('cat');
if (!isset($hsq)) {
$hsq = '-22';
}
else {
$hsq = $hsq . ",-22";
}
$query->gt;set('cat',$hsq);
}
return $query;
}

add_filter('pre_get_posts','hs_cat_exclude');

Here, 22 is the category number of the category I wanted to exclude.

This code works fine, but the moment you add is_category to the ‘if‘ clause, it doesn’t work for the category page. This was perplexing to me, and I did not understand it. I spent a long time and then decided to dig deeper. I found out that the ‘wiring’ is faulty (this is what I believe). It can’t work like this for the category page. What is needed additionally is something like this:


function hs_cat_exclude_cat($where)
{
global $wp_query;
if ($wp_query->is_category) {
$where = $where . " AND NOT EXISTS(SELECT 1 FROM wp_term_relationships WHERE wp_term_relationships.object_id=wp_posts.id AND wp_term_relationships.term_taxonomy_id='22')";
}
return $where;
}

add_filter(‘posts_where’,’hs_cat_exclude_cat’);

So far so good. What I wanted over and above this though, is for the category to not even appear on the category widget. I tried to find a way to get this done through the plugin but it did not work. Ultimately I had to ‘hack’ one of the core files to achieve this. If anyone knows of a better way to accomplish this, please add a comment. The change is to wp-includes/widgets.php. Find the line of code that looks like:

$cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h);

added an ‘exclude’ clause like this:

$cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h, 'exclude' => 22);

Thats all there is to it.

Update Jan 12th 2010: Since WP 2.8 I believe (I noticed the problem in 2.9.1), the last change above needs to be done to default-widgets.php rather than widgets.php

Share

Generating sequential numbers in a database

You are creating an application that allows organisations to manage employees. One of the tasks that it has to do is generate an employee ID when a new employee is being entered. One way of doing this is through this query:

SELECT max(empl_id)+1 FROM employee;

By James Cridland
By James Cridland

However, this query presents a problem in a multiuser environment: if more than one user is entering employee details at the same time, they will both get the same empl_id. To tide over this problem, one way to go is to look at the auto numbering solution provided by the database – however I personally find that solution limiting and have never used it.

The other approach is to create a single row table for global settings (in all probability your application will already have this) and maintain a field in that table as the last number used. Thereafter, the code can be written as below:

UPDATE settings_tbl SET lastnumber=lastnumber+1;

SELECT lastnumber FROM settings_tbl;

Remember that the order of the queries is important in a multiuser environment. Placing SELECT before the UPDATE can cause problems (locking has to happen first).

This piece of code should be executed at the time of saving the employee and not when a request for the blank employee form is generated. This is necessary so that one user of the application doesn’t have to wait for another to be finished. Note that the UPDATE lock is released only when you do the COMMIT or ROLLBACK.

Another way of doing the same thing in Oracle, one that I prefer myself and have used in a number of tight situations is the FOR UPDATE clause. This one allows you to do the SELECT first:

SELECT lastnumber FROM settings_tbl FOR UPDATE;

UPDATE settings_tbl SET lastnumber=lastnumber+1;

Share

Is the Windows registry a good idea?

Compared to Unix config files, and even to Windows 3.1 ‘ini’ files is the windows registry a good idea? This was the question that presented itself in my mind this weekend.

First, what is the windows registry? Windows registry is a Hierarchical database that stores configuration settings for Windows, and for the software installed on your machine.

Now, coming back to the original question I feel while at the core, the registry is a good idea – keeping every setting in a centralised place – it encourages non-portable software. If you install, say, CorelDraw at a certain location on your harddisk, and want to move it to a different location, you need to uninstall it and then reinstall again. Ditto for moving from one computer to another. A time-consuming process – especially if you use multiple computers – one at work, one at home, and another at a cafe.

Why is it that I say the registry, per se, encourages non-portable software? The reason is, when you had config files, and you moved software, the files moved with it too. With registry this doesn’t happen. Note however, either way its possible to write portable or non-portable software. Just that, when a programmer writes code without portability as a key focus, and uses config files, the end product is more likely to be portable.

What can be done about it? When a software program runs, it needs to check if the keys it needs are present or not. If not, it should try to default the parameters, and add them to the registry.

One current approach to writing portable software, is to use config files and provide an update program: if you move the software, you run the update program. This program will detect the file-paths and update those in the config files. The same can be used for registry.

Share

The Spirograph

I became a kid again and purchased a Spirograph set. The Spirograph is a mathematical toy, which you can use for drawing nice figures. In the simplest case it exists of a fixed circle, used as a template, and a smaller rolling circle with holes. The result of my experiment is at the end of this post.
Thereafter, I turned my attention to mathematical generation of the Spirograph figures, and to my surprise I was able to find a number of good resources on the net.
The parametric equations for a Spirograph are:
x(t)=(R+r)cos(t) + p*cos((R+r)t/r)
y(t)=(R+r)sin(t) + p*sin((R+r)t/r)

More explanation here:
http://www.mathematische-basteleien.de/spirographs.htm

A digital spirograph can be created using, for example, the following equations:
(5-1.02)*cos(1.02*x/5)+2*cos((5-1.02/5)*x)
(5-1.02)*sin(1.02*x/5)+2*sin((5-1.02/5)*x)
with HSB Grapher.

An applet, and some data to play with it can be found here.
However, what I found more useful and interesting is this page: http://linuxgazette.net/133/luana.html. It provides a Spirograph compiler (an awk script). This awk script takes a Spirograph specification and generates a gnuplot script to create the Spirograph. Go try it – its very interesting. If you are on windows, read these to help you get started on awk and gnuplot.

Spirograph
Spirograph

Share

Moving multiple files

One common requirement on *nix systems is to rename (or move-and-rename) more than one file, matching a wildcard. However, if you try to do this:

mv ACK*.xml /code/xml/ACK*.xml.done

it wont work for more than one file. By the way, this is one of those odd places where Windows command line works better!

How to work around this? As below:

ls ACK*.xml | sed -e 's/.*/mv & \/code\/xml\/&.done/' | sh

I have already talked about sed, sh and this style of scripting: it can work also on Windows. In the target path all slashes need to be escaped like this: \/ since its being used inside a sed script. Of course, this was easy because we had to suffix something to the filename – had we needed to add something in the middle, it would have been more difficult (and a bigger sed script would be needed).

Here is a more ‘capable’ script: it takes two parameters, first one the path to the source directory, and next one the path to the target directory. It moves ACK*.xml files from source to target/.processed (.processed folder under target) renaming them with the current datetime stamp.

Customise it to suit your needs.

Share

Splitting a contact sheet

When I received a CD containing contact sheets from my photographer, I was in a hurry to put the photos online. He had promised to send me another CD containing individual photos later, when the printing for the album was completed.

I tried to use normal software like Photoshop to do the trick, and looked for tools online. While there were a number of tools to create a contact sheet, there were none to take it apart. Strange.

After thinking, I was able to write a small Python program to do the trick. I am sharing it for the benefit of everyone, releasing it under GNU GPL. Download Python and run it like: python split.py Contact.jpg (where split.py is a file containing the code below and Contact.jpg is the contact sheet).
Download split.py here.

Here, the contact sheet is 1000×800 pixels, and I want to split it into 5 parts horizontally and 4 parts vertically. These parameters need to be changed in the first four lines as per need.

I noticed that since the contact sheet was JPG, and the output was also JPG, it was causing degrading in quality. So, I changed the output format to BMP, and then reconverted back to JPG using other software.

Share

Deleting zero byte files

In the past I have shown ways to run Linux scripts on windows based system. I have also talked about one use of the find command, in conjunction with grep command to search for files having a given text content (say a word) in multiple folders.

Today I will show you another use of the find command: to automate tasks such as deleting zero byte files. This is a pretty common cleanup task that’s carried out on machines that are involved in EDI file transfers.

Here is the script:

find . -size 0 | sed -e 's/^/rm /' | sh

It deletes all zero byte files in the current folder, and in the folders below it.

In order to understand this, you may need to read about pipes. The task is carried out in three steps:

  1. find . -size 0 searches for all zero byte files in the given folder and the folders below it and returns the filepaths.
  2. sed -e 's/^/rm /' turns the list of names into a script – for example if the name is ‘/data/x’ it changes it to ‘rm /data/x’. More on sed here.
  3. the last steps simply forwards the script to the Linux shell for execution.

This is also a very flexible script and can be customised to carry out a wide variety of tasks. Please post your variations as comments.

Share

Automate encryption with GPG

Privacy
Privacy

This blogpost requires some familiarity with GPG.

Today I want to share the scripts for running GPG in the batch (unattended) mode. You can have a password on the keys if you want, but since this is the automated mode, you may want to use keys without a password. Irrespective of whether or not you use a password – use a separate set of keys that you will not use for anything that not batch processed. The scripts are primarily for Linux. If you need them for Windows, please read this and post any problems you face in comments section, and I will help.

The script for encryption is here:

#! /bin/sh
GNUPGHOME='/apps/gpg/'
export GNUPGHOME
gpg --batch -r <reciepient> --output $2 --passphrase-fd 3 --sign --encrypt $1 3</apps/gpg/passph

The first line shows the location of the shell – please change it according to where the shell is on your system. The second line has the location of the folder where the keys are located – the pubring.gpg and secring.gpg. In the last line, replace <reciepient> with the name on the reciepient key. The /apps/gpg/passph points to the location of the file containing the passphrase. The script will both sign and encrypt the file – change this to suit your needs. The script expects the name of the input file and the name of the output files as parameters in that order.

The script for decryption is here:

#! /bin/sh
GNUPGHOME='/apps/gpg/'
export GNUPGHOME
gpg --batch --passphrase-fd 3 --status-fd 1 --decrypt $1 3</apps/gpg/passph | grep '\[GNUPG:\] GOODSIG'
if [ $? -eq 0 ]; then
  gpg --batch --passphrase-fd 3 --output $2 --decrypt $1 3</apps/gpg/passph
fi

The first three lines are similar. Line 4 just checks if the file has a valid signature. If you want to skip this step remove lines 4, 5 and 7. No effort is made to see who signed the file. In my scenario, I could control the people who could sign by having only those public keys in the repository, and the repository could only be written to by ‘root’.

Please post comments in case of questions, concerns.

Share

Searching files within multiple folders

How to search file contents for a specific phrase using grep within multiple folders. Shows ways for both Linux and Windows.

On Linux, the normal way to search for some text within a file is to use grep (Global Regular Expression Print). However, grep has a limitation: it cannot automatically search folders within the current folder. It can only search within files in the current folder. Today I will show you how to use grep to search within all files and folders inside a current folder (recursively).

Windows users – despair not. If you find the standard windows search brain-dead or want to automate the task through scripts, you can also use this script. I have already explained various ways to run Linux scripts on windows – use the one that suits you.

We will couple grep with the find command, to unleash the power.

Here is what you need to do on Linux (or Cygwin):

find . -type f -exec grep -iH 'dedicated' {} \;

and use this for UnxUtils on Windows:

find . -type f -exec grep -iH "dedicated" \"{}\" ;

This does a case insensitive search for the word ‘dedicated’ in the current folder and all subfolders under it. Change -iH to -H for case sensitive search.

You can read the manuals for find and grep and change the commands to suit your needs – this method provides a lot of flexibility. Post your precise usage in comments, especially for Windows.

Share

Using SQL potential

How to use the database SQL to its full potential. The idea is to reduce procedural coding and thereby improve performance, reduce defects.

I like to use the database to its full potential. For example, suppose someone has a list of vouchers and needs to find the vouchers that were paid later than the due date. One way to do this might be to read the vouchers one by one from the database, compare the due date with the payment date and determine the results. The other, recommended method will be to add the required criteria to the query itself so that only the exact result is obtained. With the second method, only 5% or 10% of the vouchers will need to be transferred from the database to the application while in the first method, all vouchers will need to be transferred.

In other words, the exact business requirements should determine the query. While you are at it, you should also keep in mind the indexes. Queries should always be written to minimise Disk I/O and transfers between the DB and the Application (server).

The database itself is quite powerful (esp Oracle) and I feel that its potential is always under-utilised. Let me show through an example.

I once had a requirement that there is a table having first, middle and last names of employees and the email ID. Something like this, ignoring the datatypes – assume all are VARCHAR2:

create table userlist(fname,mname,lname,emailid);

Each employee has middle name blank. Its possible that multiple employees have identical fname, lname with each other. For example, there can be two people having name ‘Hardeep Singh’. In this case, if the emailid of the two employees is same that means they are the same person having multiple rows, else they are different persons having the same name.

For example:

  1. Hardeep Singh alpha@gmail.com
  2. Hardeep Singh beta@gmail.com
  3. Hardeep Singh beta@gmail.com
  4. Satinder Singh gamma@gmail.com
  5. Satinder Singh gamma@gmail.com
  6. Gorakh Nath gn@gmail.com

In this case, 2 & 3 are the same person and 4 & 5 are also the same person. 1 & 2 are two different people.

Now the requirement is that we have to modify the middle name by adding a number such that every different person has a unique name. In the example above, the names should be:

  1. Hardeep Singh '1' alpha@gmail.com
  2. Hardeep Singh ‘ ′ beta@gmail.com
  3. Hardeep Singh ‘ ′ beta@gmail.com
  4. Satinder Singh ‘ ′ gamma@gmail.com
  5. Satinder Singh ‘ ′ gamma@gmail.com
  6. Gorakh Nath ‘ ′ gn@gmail.com

Now we know that ‘1’ is different from ‘2’ and ‘3’ because he has a different middle name.

The middle name to be added is given at the end of the name, in quotes. Gorakh Nath does not get any middle name since his name is unique. Any Tom, Dick or Harry would do this requirement in the following way: Read all the details one by one, look for people having the same name, then check the emailID then issue an UPDATE like this:

UPDATE userlist SET mname='1' where emailID='alpha@gmail.com';

Such UPDATES would need to be issued one for each person. However, this can be done through just a single UPDATE statement, without reading the list of employees at all. Here is the query:

update userlist a
set mname=(select x from (select rownum x,emailid,fname,
                                 lname
                          from userlist xa
                          where exists
                          (select 1
                           from userlist xb
                           where xa.lname=xb.lname and
                           xa.mname=xb.mname and
                           xa.fname=xb.fname and
                           xa.emailid<>xb.emailid))
                          ord
           where ord.emailid=a.emailid and
                 ord.fname=a.fname and
                 ord.lname=a.lname)
where exists(select 1
             from userlist b
             where a.lname=b.lname and
                   a.mname=b.mname and
                   a.fname=b.fname and
                   a.emailid<>b.emailid);
  

I guess an explanation is owed as to how it works. To my knowledge this query would work only in Oracle – but there would be ways to make it work in other Databases as well.

‘rownum’ returns the number of that particular row in the result set. The ‘exists’ clause at the end makes sure only people with same names are processed (‘gn@gmail.com’ is ignored). The part:

(select x from (select rownum x,emailid,fname,lname
from userlist xa
where exists
(select 1
from userlist xb
where xa.lname=xb.lname and
xa.mname=xb.mname and
xa.fname=xb.fname and
xa.emailid<>xb.emailid))
ord

creates a temporary view having the number, the email ID and the firstname. In the given scenario the result from this will be something like:

  1. 1, alpha@gmail.com, Hardeep, Singh
  2. This row will be absent because of the xa.emailid<>xb.emailid clause
  3. This row will be absent because of the xa.emailid<>xb.emailid clause
  4. This row will be absent because of the xa.emailid<>xb.emailid clause
  5. This row will be absent because of the xa.emailid<>xb.emailid clause
  6. This row wont even be considered, as I explained above

 

Had there been yet another ‘Hardeep Singh’ with a different email ID, he would have got a middle name of ‘2’.
Now the last step is to copy over the numbers based on the first and last names only – that part is pretty simple. Please post any questions in the comments area.

Solution using PGSQL:

update userlist a
set mname=(select rn from (
select row_number() over(order by(select null)) rn,emailid from(
select distinct p.emailid from userlist p) x) y
where y.emailid=a.emailid)
where exists(select 1
from userlist b
where a.fname=b.fname and
a.mname=b.mname and
a.lname=b.lname and
a.emailid<>b.emailid)

Share

Licensing and information about the blog available here.