Several years ago, I decided to try my hand at making salsa. The experiment was a success, however, the notes I made on how I made it were promptly lost. Months later, I tried again. I made another satisfactory batch of salsa, took good notes, and lost them again. Since then, I’ve made salsa on a number of occasions, ballparked it each time, and gave up on writing it down. A couple days ago, I made a batch, and decided to document the recipe while it is still fresh in my memory.
Before I get to the recipe, a few notes. The reason I decided to make my own salsa in the first place is that I don’t care much for supermarket salsas available in jars. These products are too wet, containing much more liquid than vegetables, and are often too sweet. I like fresh salsa, full of lots of fresh vegetables, and not a lot of liquid. These days, you can often find decent fresh salsas in the produce or deli section of the supermarket; when I first tried this recipe, this wasn’t the case. Fresh salsas which I’ve enjoyed from the local megamart include Rojos, here in the greater Philadelphia area, and Sister’s Salsa, found in Maine.
I’ll also note that this salsa recipe is a work in progress. I’ll probably make some adjustments next time; I’ll discuss some of these after the recipe. I’ll also add that my taste runs to medium-hot or less, if you like your salsa rocket hot, you’ll want to crank up the fresh hot pepper content, and possibly adjust the seasonings as well.
Fresh Salsa Recipe
- 4 cups Roma tomatoes (approx 12-14 tomatoes)
- 2 cups finely diced onion (I like 1/3 red onion and 2/3 Vidalia)
- 2 cups diced orange and yellow bell peper (1 large of each)
- 2 medium jalapeño peppers, seeded and finely minced
- 1 tbsp paprika
- 1 1/2 tsp salt
- 1 tsp white pepper
- 1 tsp chili powder
- 1 tsp cayenne pepper
- 1/2 tsp dried cumin
- 2 tbsp apple cider vinegar
- juice of 1/2 lime, freshly squeezed
To prepare the tomatoes, slice each lengthwise into quarters. Using a teaspoon or grapefruit spoon, remove any seeds and inner membrane from each quarter, leaving only the outer flesh of the tomato. Dice. Combine all of the vegetables in a large bowl.
Combine the salt and all of the dried spices in a small bowl and mix thoroughly. Sprinkle about half the mixture over the vegetables and toss. Cover and refrigerate for 8 to 12 hours.
Remove the salsa from the chill chest and stir well. Have a taste, but take note… the salsa isn’t really done yet. You just need to decide how much more of the seasoning mixture to add. I usually add about half the remaining mixture, holding the rest in reserve. Toss again to combine, and add the cider vinegar and fresh lime juice. Refrigerate another 12 hours. You can serve it now, but it improves with age- it’s really good after a total of 48 hours fridge time.
Its worth noting here that I’ve never quite gotten the seasoning nailed down. I always make approximately 3 tablespoons worth, and add it a bit at a time, sampling as I go. The first couple times, it always seemed too hot at first tasting, so I needed something to reduce the heat. I found that a tablespoon or two of apple cider vinegar seems to fit the bill. At a later tasting, I end up adding more seasoning anyway, so I’m sure there’s more tinkering to do. The lime juice was a new addition this time around, as I had limes on hand for adding to Corona, and I think it added a bright note. Next time I may try the juice of a whole lime.
For those watching salt intake, I’d suggest that the salt in this recipe is necessary. The salt draws moisture out of the vegetables, allowing them to soften slightly, and helping the flavors to mingle. Also, 1 1/2 tsp (or less, depending on how much seasoning you end up using) in about 8 cups of salsa should work out to a reasonable amount per serving.
One of these days, I’ll make a full batch of vegetables, split it into 4 batches, and try a few variations on the seasoning concurrently, to try and nail the recipe down. The other thing I want to try is to use some fresh herbs to help shape the flavor, perhaps with less dried seasonings. The obvious choice is cilantro, but I’m not a very big fan. The taste seems to overwhelm everything else in a dish. If you should try this recipe, please post a comment with your thoughts, as well as any changes you may have made.
John Gruber recently concluded his annual Daring Fireball membership drive. Since quitting his day job a couple years ago to write DF full time, John has sold annual memberships and t-shirts (free annual membership included) to support himself in his new profession. When you become a member (as I recently did), you not only get a warm-fuzzy for supporting a great writer, and perhaps a t-shirt, you also get members-only goodies: full text RSS feeds for Daring Fireball and The Linked List. These feeds use HTTP authentication to allow access to members only.
For several years, I have used Bloglines as my feed reader of choice. It’s easy to use, does what it says on the tin, and I can access my feeds from anywhere. I have been subscribed to the standard Daring Fireball RSS feed for years, which contains only brief descriptions of each article. When I recently became a DF member, I unsubscribed from the old feed, and subscribed to the full content feed and the Linked List feed in its place (John also offers members a combined feed, but I prefer to see them separately in Bloglines). Unfortunately, as a result, Daring Fireball no longer appears in my blogroll (at least as of this writing). To see why this is, I’ll explain how my blogroll is maintained, and why this method doesn’t work for the DF members feeds.
Like most feed reading applications, Bloglines allows you to export your list of subscriptions as an OPML file. Because OPML is an XML format, its easy to turn this list of subscriptions into something else, via the magic of XSLT. This is how I create both of my blogrolls- the short version in the right-hand column of every page on this site, and the long version linked to from the end of the short version.
The first step is to obtain a copy of my Bloglines subscriptions in OPML format. Bloglines makes this available at an URL, in my case http://www.bloglines.com/export?id=jclark. As part of my larger (and desperately in need of updating) effort to backup my data stored around the web, I have a daily cron job that runs at my webhost, grabbing a copy of the file using wget (curl would also work) and sticking it in my datasafe directory. One of these days, I’ll get that directory under version control with cvs or svn. For now, at least I have a daily backup. The wget command is simple:
wget -O datasafe/bloglines.xml http://www.bloglines.com/export\?id=jclark
The next step is to turn the OPML into something more useful- in this case, a piece of HTML that I can include in my web pages. I have another daily cron job that runs the following perl script:
#!/usr/bin/perl -w
use XML::LibXML;
use XML::LibXSLT;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $opml = $parser->parse_file('/home/jclark/datasafe/bloglines.xml');
my $ssdoc = $parser->parse_file('/home/jclark/xslt/blogroll.xsl');
my $ssshortdoc = $parser->parse_file('/home/jclark/xslt/blogroll-short.xsl');
my $ss = $xslt->parse_stylesheet($ssdoc);
my $ssshort = $xslt->parse_stylesheet($ssshortdoc);
my $blogroll = $ss->transform($opml);
my $blogroll_short = $ssshort->transform($opml);
$ss->output_file($blogroll, '/home/jclark/jclark.org/blogroll.html');
$ssshort->output_file($blogroll_short, '/home/jclark/jclark.org/blogroll-short.html');
The above script uses LibXSLT to transform the OPML file twice- once to create the full blogroll, and once to create the short blogroll. The transforms are similar, but the ’short’ version only considers items in my Bloglines “Favorites” folder. If you are interested, you can see the full blogroll XSLT, and the short blogroll XSLT stylesheets.
This method of driving my blogroll from my Bloglines subscriptions has served me well - no maintenance needed. I even have control over what to include. Bloglines allows you to flag each subscription as public or private. For example, I have a few Google query feeds that don’t need to show up in my blogroll. I simply flag them as private in Bloglines, and they aren’t part of the export.
Now we’ve arrived at the crux of the matter. Because the Daring Fireball member feeds require HTTP authentication, Bloglines automatically treats them as private. This makes alot of sense, but it means that these feeds are no longer in my OPML export, and so are no longer on my Blogroll. Of course, I could have left the original feed subscribed, but I didn’t realize I would need to at the time, and I don’t want to see duplicate entries if I do. Unless I can think of a better alternative, I’ll probably add an extra folder within Bloglines to store feeds I subscribe to for the benefit of my blogroll, but which I never read directly. The other alternatives I’ve considered involve a separate list of blogroll entries stored somewhere and managed manually, so I might as well manage them where I manage the rest of my blogroll- in Bloglines. Also, because DF is in my Bloglines “Favorites” folder, it used to show up on the short blogroll on every page. If I want it to remain in the short blogroll (and I do), I’ll need to tweak the XSLT stylesheet to include entries from both folders.
According to Jeff Foxworthy, “naked” means wearing no clothes, whereas ‘nekkid’ means you’re naked and up to something.
April 5 is CSS Naked Day, but we’re up to something. According to the site,
The idea behind this event is to promote Web Standards. Plain and simple. This includes proper use of (x)html, semantic markup, a good hierarchy structure, and of course, a good ‘ol play on words. It’s time to show off your <body>.
A completely unstyled website may not look glamorous, but it proves a point. Well designed, standards-based websites work, with or without styles. I’ve tried to design this site according to these standards, and it should work reasonably well on a cell phone, in Lynx, in a screen reader, or even a search engine. As Mark Pilgrim once said, “Googlebot is just another blind user with 100 million friends.” To me, CSS Naked Day is a pointed opportunity to focus on content over style - both the explicit content of a blog post, and the implicit content (headers, lists, ALT attributes, etc) inherent in a well-designed, standards-savvy layout.
CSS Naked Day at jclark.org is proudly powered by Guff’s CSS Naked Day WordPress plugin.
Note: those of you following along in via Bloglines or other feed reader, be sure to actually visit this and other participating sites today!
Joe Gregorio asks how many devices in your house have an IP address. Here’s the count:
- My iMac
- My wife’s MacBook
- My daughter’s MacBook
- The kid’s PC in the basement (for playing PC games)
- A Ubuntu-based file server in the basement
- A Ubuntu-based security camera DVR server
- The cable modem
- A Linksys WRT54G Wireless router running OpenWRT - my primary router
- A Linksys BEFSR41 wired router, which is acting as a switch slaved to the 54G
- Another Linksys WRT54G, a revision 5 running DD-WRT (getting OpenWRT working on the low-memory rev 5 is a pain), acting as a wireless bridge to the primary 54G
- An XBox 360
- A Nintendo Wii
- My son’s PSP
- My daughter’s PSP
- The upstairs TiVo
- The downstairs TiVo
In addition, there are several other devices in the house that could have an IP address if connected:
- My wife’s old Windows Laptop, no longer in use
- A PS2
- An XBox
- Four cell phones, I think- they have web-browsing capabilities we don’t use, I assume that when browsing the device obtains an IP address.
I have a WordPress problem, and I’m hoping someone can point me at a plug-in that will help. If not, perhaps I’ll take a crack at it myself.
Like most bloggers, I use comment moderation to help prevent spam (along with Akismet, which does alot of the heavy lifting). I’m also using the option “Comment author must have a previously approved comment”. This option allows comments to bypass moderation if the commenter has a previously approved comment. In general, I prefer this approach - there are certainly some of you who comment here from time to time, and I’d rather your comments go live straightaway rather than wait on me to moderate. On the other hand, I’ve got about 10 comments right now awaiting moderation, because they aren’t really spam, but I don’t want to give the commenter a free pass to comment in the future. For example, my Ubuntu drive mounting tutorial draws a lot of thank-you comments, and many of them are very brief- they could be legit, or they could be someone trying to get me to approve one comment, so they can spam freely.
So what I want - I’d like an option on the Moderation Queue screen to approve a comment without “approving the commenter” so to speak. Anyone know of a plugin to accomplish this?
That’s all really… I’ll try to post a pic later.
Back in June (when this site was still running on Blosxom), I disabled comments because of the amount of comment spam I was receiving. Over the course of several years I built a number of anti-spam measures into my Blosxom install, but without a moderation feature, I could only respond to spam that got past my tests by cleaning it up after the fact. Eventually, it was just too much.
When I switched to Wordpress in late August, I reenabled comments, because Wordpress offers comment moderation. I even added the following note to my comment form:
Hey Spammers! Comments are Moderated, so please save us both some time and move along. No matter how many you send, they’ll never show up. Really.
To the best of my knowledge, not a single spam has made it through- but not for a lack of trying. On an average day, I probably moderate 50 or so spam attempts. On an average day, I probably get <1 real comment.
Nearly three years ago, Mark Pilgrim posted a warning to those trying to develop anti-spam measures. Some folks criticized him for being too pessimistic, but he was dead on. From that post:
Spammers are smart and determined, and people are numerous and stupid, and spam pays. You can’t make it not pay. Going after their ISPs won’t help; they’ll auto-register somewhere else. (Already happening.) Going after their upstream provider won’t help; they’ll cut deals with the backbone providers and keep going. (Already happening.) Going after them in court won’t help; they’re already living under friendly governments. (Already happening.) You can’t stop them with Turing tests; they’ll hire child workers to read your images and manually register/post/ping/trackback/whatever. (Already happening.) Then they’ll attack you with the power of 100 million owned Windows boxes and knock you off the Internet. (Already happening.) They will keep coming and coming and coming until you give up, go home, cry uncle, take Prozac, get a regular day job to replace the one you quit when being an anti-spammer became your full-time job.
And they have kept coming and coming. I often get several copies of the same spam commented on the same post, each using a different method of encoding links: one with html <a> tags, one with wiki-like [brackets], and one with raw link text. Why bothering to figure out what kind of comment system you’re targeting when your automated spam-bot and cover every contingency, shotgun-style?
A couple of days ago, I got one that almost slipped past me. The following comment was left on this post about mounting an OS X partition from Ubuntu:
heyas all.
my 40 gig drive is going to good use now. I have installed UBUNTU and have ordered KUBUNTU.
I dont know how to install the driver for my ati radeon 9600xt.
Actually i dont know if i am meant to be downloading and installing XFREE86 or the XORG version of the driver. I am downloading them both but i dont know how to do anything in Linux really.
I dont know where I am meant to set up my modem or set up a net account. (no INETWIZ.EXE)
So yeah, can someone help me out with getting my ATI driver installed?
and does anyone know of a good long PDF file i can read and wrap my brain around.
I’m still a Windows user, but I want to use Linux as much as possible.
Thanks. :)
At first glance, it looks like a typical cry for free tech support, only tangentialy related to the topic at hand, as seen on many a help forum and blog comment feed. If it were legitimate, I’d allow it, even though I expect no one to help. But I’ve learned that not every spam contains a link in the body text, so I always check the link to the commenter’s web site. The site address was http://camera-digital.us, which looks like the kind of hokey “every variation on a theme” URLs that spammers and affiliate sites use. Even more curious, the authors email address was for the digital-camera.com domain- similar but different.
So I tried a google search for a line of text from the comment. The first three matches are blog posts with the exact same comment posted. In a couple cases, they actual comment was showing up in a Recent Comments list on the side of the page, but the original post that drew the comment was actually about Ubuntu.
So what’s the point of this post? None exactly- just like Mark’s post above, I don’t have a solution. I only know that the problem is getting worse.
Daniel at The Web Design Journal emailed me to ask about the post count block that appears in my sidebar. For example:
There are 2 posts in the last 30 days, and 323 total posts.
There are a number of ways to get the total post count. The last 30 days post count is a feature I wanted to add when I switched to Wordpress and designed this theme. After searching for an existing solution, I ended up rolling my own. For reasons I don’t recall, I also wrote my own function for getting the total post count. I had meant to post it here, but had forgotten about it.
Instead of designing this as a Wordpress plugin, I added it directly to template. In my sidebar template, I added the following:
<p>There are <?php echo get_post_count_inlast(30) ?> posts in the last 30 days,
and <?php echo get_post_count() ?> total posts.
<a class="sidelink" href="<?php echo get_bloginfo('wpurl')?>/archive/">Archives »</a></p>
The special bits here are the calls to get_post_count_inlast() and get_post_count(), which are simple PHP functions I wrote to query the Wordpress MySQL database. I added these functions to functions.php in the theme directory for my theme. I’m not sure if all Wordpress theme include this file, but the default theme, on which I based my theme, already provided this file. In this file, I added the code for the two new functions:
function get_post_count() {
global $wpdb;
return $wpdb->get_var('select count(*) from wp_posts where post_status = "publish"');
}
function get_post_count_inlast($days=30) {
global $wpdb;
return $wpdb->get_var("select count(*) from wp_posts where post_status = 'publish' and DATEDIFF(CURRENT_DATE, POST_DATE) <= $days");
}
I’m no PHP expert, I’ve picked up just enough to make the tweaks I wanted to my templates, but I believe you could even include the function definitions directly in the sidebar.php file, as long as they’re inside a <?php ... ?> tag.
Hope someone finds this useful. If you decide to add this to your site, please drop me a comment, and be sure to fill in your site URL.
(The source code in this post is released under the GNU GPL (just like Wordpress), and comes with no warranty of any kind, express or implied.)
When I switched from Blosxom to Wordpress, I had intended to write a HOWTO explaining exactly how to accomplish the task. Unfortunately, this proved to be a real challenge, since no two Blosxoms are exactly the same - there are hundreds of plugins that can change the core behavior in ways both subtle and profound. Instead, I decided to write up some notes on what I did (and why), in hopes that it can help others who want to try and make the switch. Note: I’m using Wordpress 2.0.4, if you are on an older (especially pre-2.0) Wordpress, your mileage may vary.
After getting a basic Wordpress install setup on my webserver, I began by looking for a Blosxom import tool for Wordpress. I found Eric Davis’ import-blosxom.php, available from Eric’s software page. (Note: Houran Bosci has a modified version of Eric’s script which I only discovered after the fact, I’ve not tested it, but you may want to have a look at the changelog). Eric’s script didn’t come with any docs, so I tried dropping it into my wp-admin/import folder. I then went to the Import section of my Wordpress admin page, expecting to find a new option. Instead, the Blosxom import script’s output ( a series of instructions ) were oddly interspersed with the normal Import screen. I eventually found that the script had to be put directly in my wp-admin folder, and I had to go directly to that page with my browser.
Once you load import-blosxom.php in your browser, you get a page full of instructions. These instructions include the text for a Blosxom theme file, which creates a special RSS feed for your Blosxom site. It uses the extension .rss20 instead of .rss, so it shouldn’t conflict with your existing feed. You then fetch a copy of this feed, and the importer reads the feed to load your new Wordpress blog with your old content. Sounds simple, right? Yeah, I thought so too.
First, a few notes about setting up the special RSS feed. By default, it needs the theme plugin, which I didn’t use. You can break the theme up into multiple flavour files, but I found it easier to just install the theme plugin. Drop and go, as I recall. The theme plugin requires the interpolate_fancy plugin, which I did use, and the filesystem plugin, which I didn’t, but that was also a quick install.
Now, by default, Blosxom doesn’t show all posts, only the most recent (by default, 10). This limit is applied for any theme/flavour, including feeds. The number can be configured via the $num_entries variable in the Blosxom script. I played around briefly with trying to use the config plugin to allow me to change the number of entries for only this feed- I didn’t want anyone visiting the site or fetching the feed to get all my entries accidently. Unfortunately, config could not do what I wanted (plugins are just loaded too late, I believe), so I cheated and made a copy of my blosxom.cgi, changed $num_entries, and used that one to fetch the special feed. I also had to disable my moreentries plugin, although I can’t remember exactly what it was breaking.
Now, the importer doesn’t fetch your new feed directly, you have to fetch and save a copy (curl or wget are good for this), copy the file to your server, and edit the import script to point to this file. This is for safety, to make sure you only import what you want to import. My first attempt revealed a few deficiencies in the rss feed and the importer.
First of all, I used Markdown to author my posts in Blosxom, and intended to keep doing so with Wordpress. However, the RSS feed contains the rendered HTML, which is what gets imported into Wordpress. This imports your posts just fine, but I wanted to maintain my Markdown formatting in case of future edits. I ended up disabling Markdown long enough to fetch the feed with the original Markdown formatting intact.
The next hurdle was URIs. In an earlier post I discussed some of the steps I went through to ensure all of my old URIs would work in Wordpress, but the very first hurdle was preserving the post slug. In Wordpress parlance (and I believe, in publishing in general), the slug is a short name for an article. Specifically for Wordpress, the slug is the ‘file name’ of the post URI. The original import-blosxom.php created a slug for each post based on the title, similar to Wordpress’s default mechanism for creating slugs. However, in order to keep my old URIs working with a little mod_rewrite magic, I needed the slugs to match the original filenames used to store the Blosxom posts. I hacked this in as follows:
I modified the rss20 theme file to include the original file name, by adding a line to the <item> section:
<slug>$fn</slug>
I re-fetched the feed to pick up the change.
I edited import-blosxom.php to use the slug. I replaced this line:
$post_name = sanitize_title($title);
with this:
$slug = ''
preg_match('|<slug>(.*?)</slug>|is', $post, $slug);
$post_name = $slug[1];
Now, my after import, my Wordpress slugs matched my Blosxom post names, making support of old URIs much simpler (see The Permalink Problem for more). But I wasn’t done yet.
Now, this is going to seem petty to some of you, and that’s fine. But it bothered my that the Wordpress post numbers of my import posts were backwards. The most recent imported post was post 1, and the oldest imported post was post 300-and-something. Even though you should never see the post number since I use fancy URIs, I bugged me. So, I fixed it. It may also be worth noting that though these iterations, I ended up futzing my Wordpress DB by hand to remove prior imports and reset the post numbering. Hopefully, if you’re following along at home on your own import, you’ll get this all right the first time.
The problem is, Blosxom renders posts in reverse chronological order, like every other blog. The import script reads the rss file and imports the posts in the order in which they appear in the file (which is to say, reverse chronological). But I wanted my posts numbers to be chronological. Yes, I’m a geek. Came to terms with it years ago. Anyway, remember earlier that I couldn’t get config to change the number of entries for the feed? I was, however, able to use it to install a custom Blosxom sort method, like so:
Config has to run first, so i renamed the installed config plugin to 000config, the standard Blosxom hack for plugin load ordering.
In my Blosxom content directory, I created config.rss20, the theme-specific config file for the rss20 theme:
package config;
sub sort {
return sub {
my($files_ref) = @_;
return sort { $files_ref->{$a} <=> $files_ref->{$b} } keys %$files_ref;
}
};
1;
Another fetch-import cycle, and my posts were numbered chronologically.
Almost there. I had my content (including comments), but the comment and posts-per-category counts were all 0. It seems Wordpress stores these numbers in the database instead of calculating them on the fly, and the importer didn’t update them. A couple of quick sql statements set everything right:
UPDATE wp_posts p SET comment_count = ( SELECT count( * )
FROM `wp_comments` c
WHERE c.comment_post_id = p.id )
UPDATE wp_categories c SET category_count = ( SELECT count( * )
FROM wp_post2cat p
WHERE p.category_id = c.cat_id )
That’s everything I have in my notes. Hopefully, there’s enought here to help others with a similar conversion. If you find other conversion issues or have questions about my process, please leave a comment below and I’ll try to help
Avast! Today do be International Talk Like a Pirate Day! As usual, the whole site is in pirate speak for the day. Now that I’m on Wordpress, I’ll raise a mug o’ grog to Dougal Campbell for his Text Filters Suite, which is making all the magic happen.