Drupal: Changing module behavior without changing the source code

For the project I’m currently working on, I’m not allowed to make any changes to third-party source code. Yes, I’ve pointed them to the GPL and Drupal FAQs about being legally allowed to make modifications to a system that will be hosted but not publicly discributed, without being required to contribute those changes back to the community. As much as I would like to release all the modifications back to the community, I also care about proper interpretation of the GPL. I suspect our project restrictions are a combination of wanting to be able to upgrade modules easily and erring on the safe side when it comes to the legal requirements of open source.

Anyway. If you’re trying to avoid making changes to third-party source code when you’re customizing your Drupal system, here are several ways you can alter the module’s behavior:

  • Hooks: Drupal has an extensive hook system allowing you to alter forms (hook_form_alter), node behavior (hook_nodeapi), user behavior (hook_user), and do other cool things. Read about the hooks in your version of Drupal, and see if any of those hooks can help you wrap code around what you want to modify.
  • Theme functions: Well-written Drupal modules use theme functions to make the HTML output easily customizable. Check the function you want Create a custom theme and override the functions in your template.php. For example, if you’re using the PHPTemplate engine, you can override a theme_foo_bar(…) method by defining a phptemplate_foo_bar(…) function in your template.php.
  • String overrides: If you want to change a displayed string and it’s wrapped in t(‘…’) calls (as it should be), use String Overrides to set up an English-to-English translation for that string. If you use the internationalization and localization features of Drupal, you don’t need String Overrides – just set up custom English translation for your site.
  • Menu overrides: Use this if the module outputs HTML directly, or if you want to change more of the functionality. In your custom module, you can define a menu item with the same path as a menu item defined by a different module. Copy the menu item definition from the other module, then change the callback function and the arguments to point to customized copies of the functions. If you do this, check the {system} table to make sure that your module has a greater weight than the module it’s overriding. You may have to duplicate a number of functions in order to get to the menu item you need to change.
  • String replacement: If you’re desperate, you can use string replacement on the page before it’s sent to the browser. Use this only in emergencies, as it’s ugly and it wastes all the processing time spent coming up with the string you’re going to replace.
  • Module replacement: If you’re absolutely desperate, make a copy of the module and change all the names so that you remember this is a customized module instead of something that you can cleanly update.

Your Drupal modifications are also under the GPL, so pay attention to the licensing terms. =)

  • http://sachachua.com Sacha Chua

    Here’s a confusing bit – the module with the lower weight in the {system} table gets priority in terms of label, but the model with the higher weight appears to get priority for execution. Boggle!

    I’ll update this when I figure out the proper way to override that.

    • http://misplaced-soul.blogspot.com Vidhya Shankar

      hmmm…
      that weights thing is sure a problem

      I think it affects files I include via module_load_include() too.

      It does include it before its needed, But the function it defines returns false for function_exists()!

      or am a a total dunderhead to miss something and blame it unnecessarily on weights

      • http://misplaced-soul.blogspot.com Vidhya Shankar

        it so happened that all our modules had a default weight of 0
        We had a ‘news’ module, a ‘nodewords’ module and a ‘research’ module

        We needed ‘nodewords’ to work for both research and news. A simple inc file with the required functions worked for news while the same didn’t for research. This was because news_init() was called before nodewords_init() and only then was research_init() called. So we made nodewords put on some weight.

        And I didn’t get your “priority in terms of label” statement :-/

  • http://franskuipers.nl Frans Kuipers

    Nice write up. I enjoy your writings every time :-)

    About Menu Overides: In drupal 6 (or 7) you would use hook_menu_alter();

  • http://www.whatwoulddrupaldo.org drawk

    It is a good policy – hacks to contrib modules, unless it is a relatively obscure and infrequently updated module, are only slightly less discomforting than hacks to core. Upgrades become difficult, changes must be continually ported, and even with source control there is a time cost with tracking the individual changes.

    I always have a sitename_custom.module, and do what you described above: leverage the various hooks and such in order to change functionality. In those rare cases where a module hack is absolutely required, I document the change externally (usually in a project tracker), make it a discrete commit to a subversion repository, put a clear identifier in the source code comments so that I can easily ‘grep’ for all custom changes, and – when applicable – submit a patch to the module maintainer in the hopes that the change or fix will be officially committed so that I won’t have to maintain my own fork going forward.