Easily Search a Twitter Stream

Update: this project is no longer active

I occasionally see people wondering how to search their Twitter stream, or anybody's Twitter stream for that matter. Sure, you can easily search all of twitter for something, but what about limiting a search to a particular user? Twitter's search features are pretty hit or miss for such specific searches, so as long as the account being searched is public and accessible to anyone, Google will have indexed it much more efficiently than Twitter.

So... I would try to explain how to use Google to restrict a search to a particular website or even part of a website. That wasn't working so well. So I just made a website that does it for you - Search a Peep's Tweets. It even remembers your most recent searches for you.

Searc a Peep's Tweets

As a comparison, I used Twitter's advanced search to search my Twitter account (stoptime) for "beer" -  I get no results. The same search on Google gives me 10 results. So hopefully this'll be useful and easy, cheers!

Posted in twitter | Leave a comment

Using the Shell and Cron to Automate Your MySQL Backups

This little shell script has proven to be a handy way to backup my local databases:

#!/bin/bash
cd '/folder/to/store/your/backups/'
for x in db-name-a db-name-b db-name-c
do
   mysqldump --user=username --password=pass $x --lock-tables --add-drop-table > $x`date +%u`.sql;
done

So what's up with this little snippet? The first line with the 'cd' commad, we're just changing into the directory where all the sql dumps will be stored. I also store this little script in there (backup.sh).

The next line is a basic loop. we list all of the local database names we want to backup, right after the "for x in" phrase, separated by a space.  The indented line is what actually dumps, names, and saves the file. On my local development computer, I just pass in the root username and pass, so it will work for each database. Each backup file then follows the "db-name" + "day of week" naming convention. For example, db-name-a1.sql would be Monday's backup, db-name-a2.sql would be Tuesday's backup, etc. A week's worth of backups is fine for me, though you can customize this to your liking.

And the command line, you can test if this is working like so (assuming you are in the same directory as the file):

$ sh db-backup.sh

Not Working?

If it's not working, a couple things may be happening. You can alter the script with more conditionals to output some general errors, but OS X will actually mail you a detailed copy of the error. Getting at it can be a little cryptic though. At the prompt, type mail:

$ mail

If you see a message like "You have mail in /var/mail/your-username" then you should check it out. You can use the terminal to check it (man mail to check the manual entry for mail). Or, what I tend to do is just open the entire mail file in my text editor, which is TextMate - using the command line tool:

$ mate /var/mail/your-username

Some juicy debugging info may be in there. You can delete everything in the file and save it if  you like. This will get rid of the "You have mail in..." message whenever you start up terminal.

Automating It

I have this script run everyday at 3pm - I do that with a cron job, using Cronnix as a graphical interface:

cronnix

One problem you may have is that your crontab's path is not necessarily the same as your user account's shell path. For example, if you are seeing an error like "mysqldump - command not found" - simply go back to the terminal, and enter "which mysqldump":

$ which mysqldump

It will output the full path to this command. Copy that path and paste the full path into your script. So for example, the line following the "do" command may look like:

/Applications/MAMP/Library/bin/mysqldump --user=user --password=pass $x --lock-tables --add-drop-table > $x`date +%u`.sql;

I use the MAMP version of mysql dump, so I need to tell the script exactly what version to use.

From here, I use SVN to keep this folder under version control as well, which makes switching from the desktop to the laptop much easier when developing. Cheers!

Posted in mysql, OS X | Leave a comment

Drupal Multi-site Configuration on a Dedicated Server

So I just figured out how to get Drupal's multi-site configuration working on my Rackspace Linux server.

Let's say your Drupal core is located at my-drupal-core.com. Install Drupal there, this will be your master copy.

Within your 'sites' folder, create a folder for each domain you will want to run off this core installation. Let's say we'll make a Drupal site, based on this core, which will be drupal-slave.com. So, within your sites folder, create a drupal-slave.com folder. I prefer using a different database for each site. So create the database for the slave site, and then copy the settings.php file from your sites/default folder, alter it for your slave database, and put it into the drupal-slave.com folder. While you're in there, create a files folder, you may need to change its permissions to 777.

So, your drupal-slave.com folder should look like this:

sites
   -- drupal-slave.com [folder]
      -- settings.php
      -- files [folder]
      -- modules [modules specific to this domain]
      -- themes [themes specific to this domain]

Now, on my server, every domain has a vhost.conf file, located inside a domains conf directory - this is at least how Red Hat does it. If you have a conf folder, but no vhost.conf file, go ahead and create it - you'll likely need to login as root to do this.

Now, within your vhost.conf file (for drupal-slave.com, in this scenario), you'll need to do something like this (alter for your own needs):

DocumentRoot /httpd/vhosts/my-drupal-core.com/httpdocs
<Directory /httpd/vhosts/my-drupal-core.com/httpdocs>
  php_admin_value open_basedir /tmp:/httpd/vhosts/my-drupal-core.com/httpdocs
  AllowOverride All
</Directory>

Then, (gracefully) restart Apache - viola! Works for me at least. By the way, the AllowOverride All is to allow the my-drupal-core.com's .htaccess file to work on all slave sites. The /tmp directory allows the slave site to write to the tmp directory for file uploads. Hope this helps someone.

Security

Note that with a multi-site setup, you really need to be careful of who can use the PHP filter (preferably, nobody). Reason being you can use PHP to read/output any domain's settings.php file and gain access to the database username and password for any domain sharing the Drupal installation, including the master domain. Careful! This alone makes me think I won't be using a multisite setup for high profile sites, or any site I'm not directly managing.

Posted in Drupal | 10 Comments

Drupal – Adding Author Name Search to Main Search Results Page

Perhaps you typed an author's name into Drupal's search box, looking for content from that particular author. But for all Drupal knows, you're just looking for content that matches the search phrase you just entered and could care less about who wrote it.

What do to?

Note: This page assumes some familiarity with how Drupal works.

The answer is the following sequence:

  1. Alongside the regular data query, query the user database for a username using a LIKE query, and if one is found, grab the user id too (uid).
  2. Perform a search for published content that matches that user id.
  3. Count the total pieces of content found.
  4. Spit out a link on the search result page that says something like, "Looking for posts written by Joe Author? (# found)"

I was hoping I could use the Views module for this, but I couldn't figure out how to do a "LIKE" query with it. Besides, rather than loading a huge view object, why not to write a couple simple functions to do this? They can be efficient, and still take advantage of Drupal's nifty (and secure) database functionality. The finished search page will look something like this:

include-user-name-search.png

Step 1: Build the query function

First things first - open up your theme's template.php file. This is the place we store functions that we want to make available to the theme you are using. We're going to add the following function to it - I've commented it pretty heavily:

/**
  * This function will search the database for an
  * aurhor's name, if it finds a match, will
  * count how many published stories and forum topics
  * they have
  * @param $name - the user name being searched for
  */
function get_user_content_total($name) {
  // this is the placeholder for the output link we're building
  $output = '';
 
  // build a user name search query with a string placeholder
  $sql = "SELECT name, uid FROM {users} WHERE name LIKE '%%%s%%'";
  $result = db_query(db_rewrite_sql($sql), $name);
 
  // build a count query with a digit placeholder for the user_id (uid from last query)
  $sql_count = "SELECT COUNT(node.uid) as count
  FROM {node} WHERE ({node}.status &lt;&gt; 0) AND ({node}.type in ('forum', 'story')) AND ({node}.uid = %d)";
 
  // iterate over the results
  while ($data = db_fetch_object($result)) {
    $result_count = db_query(db_rewrite_sql($sql_count), $data-&gt;uid);
    $data_count = db_fetch_object($result_count);
 
    // if we found any stories or forum topics, generate the text and link
    if ($data_count-&gt;count &gt; 0) {
      $link_text = t('» Looking for content written by %author (%count)?', array('%author' =&gt; $data-&gt;name, '%count' =&gt; $data_count-&gt;count));
      $link = l($link_text, 'user/' . $data-&gt;uid . '/recent', array('html' =&gt; TRUE));
      $output .= '&lt;p&gt;' . $link . '&lt;/p&gt;' . "\n";
    }
  }
  return $output;
}

You might be wondering about my link. Basically, I have a view already setup to fetch content written by a specific user. More on that over here. Ok, now we have some data to work with, so on to displaying it.

Step 2: Override the Search Results Template

The search module has a template file name search-results.tpl.php - we're going to override that template file simply by making a file of the same name in our theme's directory. So, your theme's directory, create a file called search-results.tpl.php and put this into it:

<?php print get_user_content_total(arg(2)); ?> 
<dl class="search-results <?php print $type; ?>-results"> 
<?php print $search_results; ?> 
</dl> 
<?php print $pager; ?>

Now, the only difference between the search module's search-results.tpl.php and ours is that ours calls our function at the top - the rest is exactly the same. And the nice thing about overriding it in this manner is that the next time we upgrade Drupal, our custom template here won't get overridden because we're not touching any core files.

After you create this file, you'll need to visit admin/build/modules, which will rebuild the template files and let Drupal know that we are overriding something.

The last thing you may be asking is what's with the arg(2) thing. That is just Drupal's way of fetching things from the current URL. When you search with the search module, the URL is something like:

http://www.your-drupal.com/search/node/search words

The counting starts at 0, and does not include the domain. So 'search' is 0, 'node' is 1, and 'search words' is 2. Since this URL is always structured this way for a search result page, we can just pull the search query words directly from it, and rely on Drupal's database functions to clean them up for safe querying.

This may not be the best way to do this - so I'm all ears if there's a better way, cheers!

 

 

 

Posted in Drupal | 1 Comment

Drupal: Get a Username from a User ID

I've been really enjoying working with Drupal - the more I learn about it, the more ideas I get on how to use it. Yet, sometimes it can make a simple thing a bit daunting - like fetching a user's name from a user id, without needed to load the entire user object with user_load() - which can get expensive.

So, here's a handy little function. Just plop it in your template.php file for your theme of choice, and it will be available where you need it on your site (as long as you're using that template!):

// DON'T DO IT THIS WAY - READ ON
//function getUserName($id) {
//  $sql = "SELECT name FROM {users} WHERE uid = '$id'";
//  $result = db_query(db_rewrite_sql($sql));
//  $username = db_fetch_object($result);
//  return $username-&gt;name;
//}
 
// After learning more Drupal, this is the better way:
// note that $id must be a number
 
function getUserName($id) {
  return db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $id));
}

To use it for output, just do something like:

<?php print getUserName(1234); ?>

Using This With the Views Module

I often use this with the Views module. For example, I have a View for displaying all content created by a given user. The URL looks something like this:

http://www.some-drupal-site.com/user/1234/recent

The "1234" is the user id argument getting passed to the view. The view will then go in and fetch content created by this user id. Oftentimes I want to create a nice header in the View with dynamic output - but Views doesn't really allow that sort of thing in the header/footer areas under Basic Settings. So I use this function to get around it. In the view's header, I do this to fetch a username:

<?php
  $view=views_get_current_view();
  $user_name = getUserName($view->args[0]);
  print '<h1>Content created by  ' . $user_name . '</h1>';
?>

*Note that to get the PHP input type working, you need to allow the PHP Filter in the modules section, and then select PHP as the input type for this view's header.

Note that after you click the Update button, the Views module may give you an error message - that's OK. It just doesn't know what to do with this php code outside of the actual page you're going to use it on.Below is a screenshot example.

user-header-views.png

Hope this helps someone! And if you know a better way to do this, speak up! Cheers.

Posted in Drupal | 12 Comments

Drupal – Encoding Email Addresses to Deter Spam-bots

While working on my latest Drupal project, I really wanted an easy, brainless way for users to create clickable "mailto:" links that gave pesky spam-bots a hard time.

Turns out, the Bbcode module does just the trick. Even if you don't normally use it, you can add this input filter to your standard HTML filters, and it will work. The first step is to download and install the module.

Next up, enable the module, and add it to your input filters (located at /admin/settings/filters):

input-filters.png

As you can see,  you don't even need to assign it any roles. However, the Filtered HTML and Full HTML filters can use it, just set them up like so:

Input Filters

Next, go in and and configure the Bbcode module to encode email addresses:

bbcode-email.png

Now, to use it - just use the standard bbcode to encode any email address:

[email]someone@domain.com[/email]

And when the page is rendered, the underlying HTML will all be JavaScript gobbly-gook, but to the end user, it will be a normal link.

Posted in Drupal | 3 Comments