drupal

Today I Learned: Translate custom (core) strings

Ben Buckman's picture
Tags:

On a site we're starting to build, Drupal's core "Sticky at top of lists" functionality isn't necessary (using Nodequeue for lists), but nodes need to be included or excluded from search results on a per-node basis. Rather than create a custom CCK field, and since we're building search (as usual) through Views, I decided the simplest answer was to rename "Sticky" to "Appear in Search Results," and filter accordingly in the view.

So how to do this? hook_locale() only works on non-English languages (see t()) and seems to define groups rather than strings.

The simplest solution, it seems, is to use settings.php (or in this case, a common.settings.php used by all the others). Like so:

<?php
$conf
['locale_custom_strings_en'] = array(
    
'Sticky at top of lists' => 'Appear in search results',
);
?>

Voila! It works even with the Locale module disabled. (It's not really needed since it's an English-only site.) And since the string is always run (by core and Views) through t(), it applies universally. It's still `sticky` to the database but that doesn't matter.

New to me: Popups module

Ben Buckman's picture

I've started using and loving the Popups API module. It allows other modules to create lightbox-type ajax popups of other pages and forms. Out of the box, it includes the option to convert several admin UI pieces into popups, like the Configure Block links, Taxonomy Add/List/Edit links, etc. Saving a few seconds here and there adds up to a lot of time!

(Note: Make sure to set the Content Selector in your theme settings (/admin/build/themes/settings/themename). The default is strange, but in most themes div#content should do it.)

Environments module: simplifying developers' management of 2-dimensional site environments

Ben Buckman's picture

This week, I released a Drupal 6 module called Environments (shortname Envts), my first released module and EchoDitto's first for Drupal 6. Its purpose is to help developers manage "two-dimensional" Drupal site environments. Drupal is fairly good at running multi-site builds, with sites folders and different settings.php files. Modules like Domain Access are good at managing those sites one-dimensionally - a single list of hosts with primary (mysite.com), secondary (special.mysite.com), etc, and deep integration with Views and other modules.

But this single list of sites is built for one environment: one set of domains, such as for a live site. For example:

  • dartcenter.org [primary]
  • dartsociety.org [secondary]

Domain Access is good at listing these domains and filtering nodes by them. But in our work, there's never only one environment. There's a development environment -

  • dartcenter.dev.echoditto.com [primary]
  • dartsociety.dev.echoditto.com [secondary]

- and each developer usually has a local server environment -

  • dartcenter.ben.local [primary]
  • dartsociety.ben.local [secondary]

- and sometimes there's a staging server between dev and live -

  • staging.dartcenter.org [primary]
  • staging.dartsociety.org [secondary]

- and so forth.

OH NOES, a DRUPAL CORE HACK DETECTED!

JP's picture

After working on some 50-odd Drupal projects over the past few years and inheriting a number of (often questionable) others, it occurred to us it'd be a good idea to have a script that detects hacks and/or "modifications" to Drupal core for a given Drupal install. The result is this remedial PHP script (or core agent) that detects your version of Drupal, downloads the appropriate tarball from Drupal.org, runs a diff on /modules, /misc and /includes, and returns you the results.

Don't Share Your Cookies: Drupal and the HttpOnly Flag

Tom Lee's picture

a cookieJeff Atwood has an interesting discussion of the HttpOnly flag and how it can help protect your site from cross-site scripting attacks. For those hoping for a quicker rundown than Jeff's excellent explanation: XSS attacks are based upon the injection of third-party Javascript into your site. This usually happens via a comment or search form — really, it can be anything that accepts user input that is then rendered as part of the site's HTML. If that user input isn't properly sanitized it can trick other users' browsers into running the attacker's Javascript as if it were part of your site.

Once that happens the attacker has access to all sorts of goodies. The most valuable is the document.cookie object, which will generally contain the string that identifies your session to the site. They'll shuttle its contents off to their own server, and with your session in hand they can pretend to be you (at least until the session expires), doing whatever sorts of authenticated tasks you have access to on the site.

The HttpOnly flag tells most modern browsers that scripts shouldn't be allowed access to the cookie — it's for the webserver's eyes only. Handy! So what would it take to get this into Drupal?

Well, with the caveat that I haven't previously dug into Drupal's session-handling code, it looks like a relatively simple fix. In both d5 and d6 there's a function called sess_regenerate that's called at login and which sets the session cookie. This seems to be the only call to setcookie() in core, in fact, making me think that it's the whole enchilada as far as HttpOnly support goes. The line in question looks like this:

Capistrano and Drupal

Tom Lee's picture

I've been messing around with Capistrano over the last few days. The system is best known for deploying Rails applications, but at its heart it's really just a Ruby-based way of making it easier to manage repetitive server tasks over SSH.

There's one task in particular that I've used it to automate, and which you might find useful, too. Here at EchoDitto we're spread out over a number of offices — DC, Boston, New York, and often who-knows-where-else. This presents some development challenges when collaborating on sites built on a system that, like Drupal, puts alot of site configuration in the database, where version control systems can't be used to manage it.

Of course we do development locally when possible, but we also run a remote dev server to let us collaborate off a single database (and to allow clients to see our work). The obvious solution is to tunnel our local server's database traffic to the central server, allowing different developers' filesystems to connect to the same datastore. But this is slooow.

So other options: setting up MySQL replication? Well, that seems like overkill, and precludes offline development. The solution I've come up with is automating a simple database dump from the dev server. When I need to make config changes I do it on the dev site; when I'm working in the filesystem and need fresh data, I simply invoke a command to pull down the dev DB. For most sites this only takes a few seconds to run, and invoking it is as simple as running `cap get_db` from anywhere within the dev site directory structure.

I used to do this with a somewhat horrible bash script. But it made various assumptions about filesystem and had to be modified for each new local project. Capistrano has let me make this a bit more abstract and reusable. The following script still makes a few assumptions about our servers and configuration, but you might find it useful, too.

Drupalcon Szeged: What I'm Missing

Tom Lee's picture

Well, I'm not in Hungary, and it's a drag. Drupalcon has begun! It looks pretty exciting, but at EchoDitto I think we're collectively keeping our fingers cross for Drupalcon DC.

Still, I'm doing my best to review the stuff coming out of Szeged. So far it's just a few sets of slides, but I'm optimistic that we'll get some podcasts or maybe even Qik streaming before too long. Inspired by Alex's list, here are the sessions I'm bummed to be missing — not that I could have physically attended them all, of course. But these are the ones that look particularly interesting to me:

CSS Aggregation Problems in Drupal 5

Tom Lee's picture

This may be old hat to some of you, but it was a near-revelation to me (and tough to find through Googling) so I thought I'd share it and potentially save others some hair pulling.

If your Drupal 5 site goes crazy when you turn on CSS aggregation, try examining your stylesheet(s) for absolutely-addressed url()s. There was (and is) a bug in Drupal 5's CSS aggregator code — since aggregated CSS has to live in a different directory from the original stylesheets, all those URLs have to be rewritten. The function that rewrites them expects them all to be relative, though. So:

background-image: url(/sites/all/themes/zen/xxx/images/something.gif);

gets turned into:

background-image: url(/sites/all/themes/zen/xxx//sites/all/themes/zen/xxx/images/something.gif);

That's obviously no good. Instead, simply make sure that your URLs are all relatively-addressed:

background-image: url(./images/something.gif);

That should be rewritten properly by the aggregator.

The more complete solution, of course, is to fix drupal_build_css_cache(). There is a patch available, but since the issue has been more thoroughly addressed in Drupal 6 and 7, not much attention is being paid to fixing it in 5. And that's probably okay: in most cases it's going to be quicker and easier to simply edit your stylesheet to use relative paths. It's best to avoid patching core when possible, and this is no exception.

Drupal Internationalization: Part I, and introduction

Ethan's picture

We've recently begun putting together the infrastructure for a number of upcoming projects which will organize people around the world toward some pretty powerful, ambitious goals. While the details of those sites are still in the works, working with Drupal to create multi-language sites has been a great experience involving a great deal of learning. This is the first of a two part series covering how Drupal works with multiple languages and the best practices/tricks of the trade for making the most of the Drupal Local, i18n and L10n systems.

What the h3l is this i18n/L10n c2p all about, anyway?

Don’t be scared by the geekspeak! “i18n” and “L10n” are just abbreviations for “internationalization” and “localization”, the engineering methodologies used to create software packages and websites which can be used by speakers of different languages. i18n is the process of engineering an application so that it can be adapted for use by speakers of different languages or in different regions without needing major modifications and L10n is the related step of providing conversions between languages, date formats, etc. so the software or site can be accessed in any particular language (see the Wikipedia article on i18n and L10n for more).

Setting up Eclipse to Debug Drupal with XDebug

Ethan's picture

Every 6 months or so I take a stab at setting up line-level, breakpoint style debugging for PHP. In the past I've been able to get the debugger installed, but the lack of a decent interface to use in setting breakpoints and watches has limited the value of a debugger (one thing that I have found helpful is XDebug's profiling feature that lists all function calls in a nested format along with performance timing). The recent announcement of a new XDebug client for OS X got me back on that track, and while I wasn't able to get MacGDBP working as I wanted, it did lead me to re-evaluate the newest Eclipse offering for PHP coders: PDT. PDT has come a long way, and the combination of a robust IDE, nimble 'jumpt to' shortcuts and killer debugger integration are more than I can pass up. Here's how I got everything working on a Mac OS X 10.5 box.

Much of what follows was made possible by and incorporates instructions from these very good tutorials:

Dorkbot Tomorrow, Drupal on Wednesday

Tom Lee's picture

Fonera glamour shot

I'm going to be presenting at tomorrow's Dorkbot DC meeting — anyone who's interested should come on by. I'm going to be talking about making a Fonera router talk to an Arduino, a subject I first blogged about right here.

And of course the DC nerd community also shouldn't forget about Wednesday's Drupal Meetup at Affinity Lab, which is once again being hosted by our friend Mike McCaffrey. You can find details here. I think it's safe to say you'll be able to find some of us at each of these events.

Using Blip.tv With FeedAPI

Tom Lee's picture

Yesterday Development Seed was kind enough to give Chris & me a rundown of how the Drupal community is organizing its participation in the Google Summer of Code program. Along the way I got a chance to chat with Alex and Ian about FeedAPI and FeedAPI Mapper, two excellent projects that DevSeed ushered into being through last year's Summer of Code and now continues to maintain and extend.

I've just begun using FeedAPI for the first time in a project destined for production, and so far I'm very pleased with it. It offers a more fully-considered alternative to aggregator.module — and with the addition of the optional Mapper module it becomes simple to turn aggregated RSS items into Drupal nodes, with the items' attributes stuck in whatever CCK fields you care to create. It's really slick.

In my case I'm using it as an integration point for Blip.tv. Our client needs video capabilities, but I saw no reason why we should mess around with transcoding, customizing an FLV player and all the rest of the headaches that come with web video (been there, done that). Blip does all of that stuff very well, and has social features baked in, too. I'd rather just have the client upload their videos there, then count on FeedAPI to turn them into nodes that can be exposed through Views. Any configuration that we can't get from the Blip RSS feed can be manually handled by an editor — Workflow-NG fires off a "please come edit and publish me!" email whenever a new video node is created.

Drupal Data Importing as a Thrill-seeking Behavior

Tom Lee's picture

Yesterday Ethan, Chris and I found ourselves needing to move some data into Drupal — we're building an existing client a new site and they need some blog posts moved over from their old one. It sure sounds simple. But as anyone who's actually done data migration from one blogging platform to another (or just from one version to another) can attest, it's rarely that easy. There are a few options, though, which I present here in increasing order of their likelihood to unexpectedly spiral into a huge, awful mess:

Automenu.module: automatic menu parenting for Drupal 5.x

Chris's picture

I've had a couple projects that needed the menu system to stay open when a full node was being displayed. The Drupal menu system, while dynamic and mostly awesome, didn't seem to allow for that, at least not in the way that I needed it to. So, I wrote Automenu.

For each content type, Automenu allows an administrator to set a default parent in the menu system. If a node is given an entry in the menu system, that default parent is ignored. Relatively simple.

To illustrate, let's say you'd like all nodes of type 'news' to show up under an entry in Primary links called 'Newsroom,' but you don't want to actually create an entry in the menu system for each one. Automenu will do that for you, specifically without creating additional entries in the menu system. If you want to give an individual news node its own special place in the menu system, you can still do that the standard way.

Give it a shot!

Thanks to merlinofchaos and the folks in the #drupal IRC channel for steering me in the right direction on this. They're a bunch of great and helpful folks.

making profile.module appear in places it doesn't want to

Tom Lee's picture

Out of all the modules in Drupal Core, Profile has always seemed me to be the most half-assed. It's clear that something needs to happen to it — using CCK field types at the least, maybe removing profile data from the user object, and, if I had my way, permanently node-ifying users. I haven't poked around Drupal 6 as thoroughly as I ought to, but based upon what I've heard from Phil's writeup and the Lullabot podcast, I may have to wait a bit longer to see meaningful change come to Profile.

So others may find themselves in the situation I did yesterday for a bit longer: trying to embed a profile form somewhere that Profile.module doesn't think it ought to go — for instance, yesterday I needed to let users edit their name and address on their user_profile page, rather than expecting them to mess around with a bunch of tabs and only-vaguely-different categories.

You'd think there'd be a way to call drupal_get_form() to collect your users' addresses and names whereever you might care to. But you'd be wrong, so far as I can tell — profile.module acts as a user.module parasite, accomplishing what it does through hook_user() and the abuse of arg().

But it's very easy to re-wrap Profile's form-generating functions in your own module and then call drupal_get_form() against it. Here's the example that I came up with: