Sep 01 2012
Sep 01

I'm not sure how it happened, but today I noticed that Drupal's menus were behaving very oddly. After upgrading to Drupal 6 and installing several additional modules, I noticed duplicate menu entries as well as other disturbing oddities. Items I was placing into the menu were not showing up. Menu items that I moved around were apparently saved but they did not appear properly in a dropdown context. 

Looking further into it via the absolutely awesome SQLYog tool, I verified that there were dozens of duplicate entries. Some items were duplicated up to six times. It was a real mess.

The database tables involved here are menu_links and menu_router. These are two of the more mysterious tables in Drupal. I haven't had the need to spend much time with them in the past, and I know now that this is a good thing. Fortunately, you do not have to know anything about them to fix this problem. While I spent a couple hours carefully deleting items from this table, ultimately I gave up. I was able to remove all the duplicates, but the menus were still misbehaving. At this point, I just wanted to do a factory reset on the menus, but it's not so simple as flushing cache. However, that is not far from the solution.

This solution will do a 'factory reset' on your menus. You will lose any customizations you have made. However, all core and contrib module entries will be restored very nicely.

Please backup your entire database before doing any destructive database manipulation. 

Step one is to empty the corrupted tables:

In your favorite SQL client, run the following commands:

truncate menu_links;
truncate menu_router;

At this point, your site will be completely unusable. But not for long. To complete the final step, you will need to be comfortable with the Drupal admin's best friend, drush.

Simply run the following commands from your terminal (Viva tcsh!):

drush php-eval 'menu_router_build();'
drush cc menu

Now my menus are as fresh as the day they were installed.

Though I could not clearly identify the cause of this problem, I would suggest backing up your database before installing the Taxonomy Menu module

Apr 01 2009
Apr 01

Developers are all familiar with the default behavior of the drupal menu systems "local tasks" (aka tabs). These appear throughout most Drupal sites, primarily in the administration area, but also on other pages like the user profile.

Generally, developers are pretty good about creating logical local tasks, meaning only those menu items which logically live under another menu item (like view, edit, revisions, workflow, etc... live under the node/% menu item).

But sometimes, these tabs either don't really make sense as tabs or you simply want to have the flexibility of working with the items as "normal menu items", or those menu items which appear under admin/build/menu.

I recently wanted to move some of the tabs on the user profile page (user/UID) into the main menu so that I could include them as blocks.

For some reason, developers think the user profile page is a great place to put tabs for user related pages such as friendslist, tracker, bookmarks, notifications and so on. But these types of items are less a part of the user's account information than they are resources for specific users. Personally, I would not think to look at my account information on a site to find stuff like favorites or buddies. I'd expect those items to be presented somewhere much more obvious like a navigation block.

Initially, this may seem like a trivial task. My first thought was to simply use hook_menu_alter() and change the 'type' value of the menu item from MENU_LOCAL_TASK to MENU_NORMAL_ITEM. However, for reasons I don't understand well enough to explain in detail, this does not work.

In order to achieve the desired result, you must change the path of the menu item and incorporate the '%user_uid_optional' argument, replacing the default '%user' argument.

All very confusing, I know. Let's look at an example.

The notifications module (which provides notification on changes to subscribed to content) uses the user profile page rather heavily. I don't want its links there, I want them in the sidebar where users can always see them.

<?php
/**
* Implementation of hook_menu_alter().
*/
function MODULENAME_menu_alter(&amp;$callbacks) {
 
// NOTIFICATIONS MODULE
 
$callbacks['notifications/%user_uid_optional'] = $callbacks['user/%user/notifications'];
 
$callbacks['notifications/%user_uid_optional']['type'] = MENU_NORMAL_ITEM;
  unset(
$callbacks['user/%user/notifications']);
  <
SNIP>
}
?>

So I have moved the notifications menu into my own menu, changed the type, used %user_uid_optional instead of %user, and unset the original menu item.

This works fine except for the fact that you'll lose all of the other menu items under user/%user/notifications! You need to account for all menu items in the hierarchy to properly reproduce the tabs in the main menu system, so we add the following:

<?php
    $callbacks
['notifications/%user_uid_optional/thread'] = $callbacks['user/%user/notifications/thread'];
    unset(
$callbacks['user/%user/notifications/thread']); $callbacks['notifications/%user_uid_optional/nodetype'] = $callbacks['user/%user/notifications/nodetype'];
    unset(
$callbacks['user/%user/notifications/nodetype']); $callbacks['notifications/%user_uid_optional/author'] = $callbacks['user/%user/notifications/author'];
    unset(
$callbacks['user/%user/notifications/author']);
?>

And of course, we don't want this code executing at all if our module is not enabled, so you'd want to wrap the whole thing in:

<?php
 
if (module_exists('notifications')) {
 
  <
SNIP>

  }

?>

Keep in mind that not all modules implement menu items using hook_menu(). It's becoming more and more common for developers to rely on the views module to generate menu items, and this is a wise choice. Menus generated using views (ala bookmark module) can be modified to get the desired result without any custom code.

Feb 16 2008
Feb 16

The importance of project management tools is almost never fully appreciated. I am shocked at how common it is for a group of developers to go working without version control, ticket tracking, development documentation and so on. The very first thing I do when working with a new client is to make sure that they get these tools in place if they haven't already.

Those who are used to working without a complete set of project management tools never fail to appreciate the benefits of them once they are introduced. I consider it next to impossible for a team to work together without managing code and tasks in an efficient and highly organized way.[img_assist|nid=155|title=|desc=|link=none|align=right|width=250|height=156]

Hopefully you do not need to be sold on this idea and are using CVS or SVN to manage your project already. You likely have some sort of ticket system. It is a little less likely that you have both of these components integrated with each other.

When it comes to choosing a solution for project management software, a die-hard Drupal user has a dilemna. On one hand, Drupal seems as though it should be the perfect solution. It's fully customizable, has lots of nifty project management related modules and, most importantly, it's Drupal! Why would you not use it? "Eating your own dogfood" is the way to go, right? Meh...

Drupal is generally considered a content management system. Personally, I like to refer to it as a website management system. It is great at managing website related stuff like users, posts, permissions, categorization, and so on. Using contrib modules, you can customize and enhance this core functionality to almost no end. But at the end of the day, Drupal is designed to handle web content and the users that are accessing it. That's what a content management system is (and if content is king, that would make Drupal... well... God).

Managing a project, on the other hand, is a much different business from managing a website. Yes, you have many shared properties such as content and users. But the essence of project management involves things that have nothing to do with website management such as a revision controlled code base edited by multiple users, a need for efficient ticket management, and ideally full integration of everything. Essentials also include stuff like a nice repository browser, user management interface for repository access, fancy reporting for tickets, organization of tasks by milestone, date, person, severity, etc...

It's a very tall order. Yes, you can do all this in Drupal, but not very well. You can piece together something that sorta kinda resembles a project management solution, but in the end, you need to invest a relatively large amount of time to create something that is less than ideal and will require ongoing tweaking and modification. Unless your business is creating an effective project management solution in Drupal (something I dream of!), you should not be using Drupal for project management.

I'm a one man shop, and I do not have time to spare. I cannot justify spending any time at all kludging together a project management solution for a client when there are already far superior solutions available at low cost. I would much rather pay someone a few bucks a month and be done with it. Let them deal with SVN administration and enhancements; let me focus on my primary task which is building cool sites with Drupal.

While there are numerous project management related service providers out there (Fogbugz, Basecamp , Beanstalk to name a few), I want to talk about my personal favorite, Unfuddle. Unfuddle has taken obvious inspiration from the folks over at 37signals, innovators of the simple, clean, effective, it-just-works web application. Unfuddle is an instant project management solution that takes minutes to set up and costs a few dollars a month. The time you'll save in not having to set up SVN and manage SVN users alone makes it worth every penny.

[img_assist|nid=156|title=|desc=|link=none|align=left|width=250|height=221]What you get with a solution such as unfuddle is a ready-to-use repository with integrated documentation, ticketing and reporting. It takes seconds to set up a new user account with permission levels fit for everyone from a developer (gimme root!) or a suit (look but don't touch).

From a single interface, you can browse code, tickets and documentation. Every component integrates with the others. You can even resolve a ticket with an SVN commit message, saving you the trouble of having to go and edit the ticket after your commit! Users can individually subscribe to whatever level of email notificaton they would like to recieve and how often. The developer can shut off all notifications while the manager can get a nice daily summary each morning of milestone completion progress, new tickets, added documentation and so on. The project manager can glance over one of the ticket reports and group tickets into milestones for reasonable short vs long term goals.

SVN comments link back to the tickets they are related to. Tickets contain links to the changesets that resolved them. Viewing these changesets, you can see a beautiful code diff and quickly see what fixed the problem. Senior team members can quickly and easily review code changes submitted by junior staff.

With tools like this available these days, it's just not worth it spending any effort whatever on a lesser solution.

Jun 27 2006
Jun 27

The contributions process for non-core Drupal themes and modules is in need of a revamp.

Currently the process is a very loose one that does not require adherence to any tagging conventions or release process. This makes it very difficult to know the status of the modules that make up a given site. I believe this probably results in many site admins out there just leaving their site as-is because the process of updating is somewhat confusing and tedious.

We want to fix this.

While there are existing 4.6, 4.7, etc... tags applied to contrib modules, they are not consistently used by module authors. Furthermore, tags are being applied inappropriately (4.7 tags applied to 4.6 code that is not actually upgraded).

Ultimately, I'd love to see version and update management built right into Drupal (in the same way it's built into most OSs and many software packages). Drupal might have a single page the admin can visit to check the status of his core Drupal installation as well as all modules and themes he is using. The process of upgrading to the latest versions could be a very simple matter of selecting desired updates and hitting a submit button.

That, however, is a long way off. We need to start by creating a process with which Drupal developers and contributors can "tag" their software releases and make those releases available quickly and easily.

To take part in defining this new release process, please visit "Versioning and release process for contributed projects" and leave a comment.

May 03 2006
May 03

One of the great features of Drupal is its ability to run any number of sites from one base installation, a feature generally referred to as multisites . Creating a new site is just a matter of creating a settings.php file and (optionally) a database to go with your new site. That's it. More importantly, there's no need to set up complicated Apache Virtual hosts, which are a wonderful feature of Apache, but can be very tricky and tedious, especially if you're setting up a large number of subsites.

No worries, there is a solution.

Create a new LogFormat

Copy the LogFormat of your choice, prepend the HTTP host field, and give it a name:

LogFormat "%{Host}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" vcombined 

Get the script

Next, download the attached script (split-logfile) and store it somewhere like /usr/bin (don't for get to chmod 755 that baby!)

Now, tell apache to use pipe logfiles to your script rather than writing them directly to disk:

CustomLog "| /usr/bin/split-logfile" vcombined 

Restart Apache

/etc/rc.d/init.d/httpd restart

That's it.

Naturally, you may have to modify split-logfile if you don't store your logfiles in the default location.

 

 

Apr 21 2006
Apr 21

This article explains a practical implementation of a technique outlined in the article "Sharing Drupal tables between databases using MySQL5 Views".

Problem

You have multiple (multisite) Drupal sites and you would like to manage the content for all of these sites through a single interface. Depending on the nature of a given piece of content, you may want the content published on one, several or all of your subsites, but you do not want to have to create copies of the same content for each site.

Solution

Taxonomy plus MySQL5 views. (NOTE: this solution will not work with versions of MySQL prior to 5.)

Assumming you have your subsites properly set up and running, the first step is to create a special vocabulary which you will use to target content.

Go to [your site's baseurl]/admin/taxonomy/add/vocabulary and create a vocabulary. We'll call it simply "sites".

Next, go back to your taxonomy page (/admin/taxonomy) and select "edit vocabulary" for the "sites" vocabulary.

Add a name for each of the subsites you would like to manage. For our example, we'll have two subsites, "foo" and "bar", and one master site, "master".

Now add at least three pieces of test content. Target one piece of content for each of foo, bar and both.

Next, we're going to create a node view for each of our subsites that we'll use to replace the actual node table.

The SQL is as follows:

CREATE VIEW [subsite, eg. "foo"]_node AS SELECT n.* FROM node n, term_data td, term_node tn, vocabulary v WHERE v.name = '[vocabulary name, eg. "sites"]' AND td.vid = v.vid AND td.name = '[subsite vocab term, eg. "foo"]' AND td.tid = tn.tid AND n.nid = tn.nid ;

Because the terms that serve as our subsite labels may very well exist within other vocabularies, we also need to join on the vocabulary table to ensure our solution works reliabley.

Finally, we need to have our subsites use the views we have created instead of our master nodes table, which only the "master" site will have access to directly.

In your drupal's sites directory, you should have directories that correspond to each of your drupal sites (both master and subsites). Edit the settings.php file for each of your subsites, and use the db_prefix variable to point the site to your view. So sites/foo.example.com/settings.php would contain the following:

$db_prefix = array( 'node' => 'foo_', );

At this point, you'll want to disable creation of content from within each of your subsites. You can do this in the from the admin/access page. If you attempt to create content from within the subsites, you'll likely get a 'duplicate key' error.

I hope that explanation is clear. These articles are written rather hastily. If you questions or suggestions regarding this solution, please leave a comment.

Mar 24 2006
Mar 24

Problem:

You have a "master" database that contains data from multiple Drupal sites and you want to share it among them. Normally, you could use table prefixing to allow each of your sites to point to a single table. But what if you do not want content from one site to "bleed" across to the other sites? Let's say you have a network of Drupal sites sharing a user database. You want to share that user's information across your entire network of sites, but only make the information visible from those sites to which the user has subscribed. Or maybe you want to populate baz.com with users who meet some arbitrary criteria. There are lots of possibilities here, but nobody's paying me to write this, so let's get on with it.

Solution:

Use MySQL5 Views. For our example, we'll use the users table. Our "master" users table (the table that contains all users for all of our sites) resides in a database called "master". The database of the our example site that will have restricted access to our masters users table is called "banana". Assuming you're starting with a fresh instance of the Drupal schema in your database "banana", do this:

use banana;
drop table users;
CREATE VIEW users AS
SELECT *
FROM master.users
WHERE uid IN (
SELECT uid
FROM somedatabase.sometable
WHERE uid = 0
OR label = 'foo'
)

Drupal will use banana.users just as it would a normal users table. No other modifications are necessary. Now only "foo" users will be included in the users table for your banana website. Note: Drupal has a dependency that is not really documented. Every users table must have an entry that contains uid=0. It's a "stub" entry that Drupal needs to function properly when a user is anonymous. A workaround for this dependency is to include "user 0" in the results set that defines your view. See, wasn't that easy? Please post questions here, and I'll update these instructions as needed.

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web