A common issue developers run into is that they create a small piece of custom functionality and, after the fact, realize it could be reused. Then they must decide if they’re willing to take the time to make it reusable, or if the client / project they’re doing the work for would be willing to cover the time. For developers who take a contrib first approach the answer is obvious - yes, the time is worth taking, but for people who are not completely comfortable with the processes it can seem daunting. To help folks get past this, here’s a process that should help.
(With thanks to Joshua Boltz for starting this discussion.)
"I have some Drupal code I extracted from something custom and would like to turn it into a contrib project. What do I do?"
- Decide on names for the project, both the short name and the verbose human-readable name.
- Make sure that the short name is succinct, under about 20 characters is preferred; the human readable name can be more verbose.
- Make sure the name isn’t being used by another project already - check to see if there’s something already at drupal.org/project/MYPROJECT.
- The name also must not include either the name of the site it was built for or the name of the company that built in, it should be generic. That said, it can of course include the name of a 3rd party system it integrates with if applicable.
- Set up the project with the necessary .info or .info.yml file as necessary, then copy over all of the other code so that it’s all present in one directory.
- Review the codebase to ensure the original site or company aren’t mentioned in class names, function names, variable names, comments, etc.
- Clean up existing code to follow the Drupal coding standards.
- Bonus points: use the Coder module to review the code.
- Look for possible security flaws:
- Look for where arguments are passed directly from form input or query strings and either stored in the database or submitted to API keys.
- Always use the proper arguments with db_query(), db_select(), etc to avoid SQL injection errors.
- All custom strings should be passed through the translation system first, i.e. pass them through t().
- Any custom text should be passed through one of the security functions, e.g. on D7 use check_plain(), filter_xss(), filter_xss_admin().
- See https://www.drupal.org/writing-secure-code and https://www.drupal.org/node/2489544 for more details.
- Everything must be licensed under the GPL-2, so make sure there isn’t any copyrighted files included, e.g. fonts, logos, etc.
- A few extra things for Drupal 7 modules:
- Split out the menu paths into separate files by using the "file" attribute of hook_menu(), especially for settings page.
- Make sure there’s a hook_uninstall implementation in a MYMODULE.install file that removes any variables used.
- Drupal 8 projects really should include a composer.json file, especially if there are dependencies on other projects or libraries.
- Add a README.txt file and provide some details on what the project does, how it works, etc.
- Add a CHANGELOG.txt file to keep track of changes made to each release.
- If any hooks or APIs are made available by the project, create a file named PROJECTNAME.api.php and document them.
- Add a simple test to ensure the site doesn’t blow up when the module is enabled; a future blog post will explain how to do this.
- Ask a coworker to review the code.
- Create a new project by going to https://www.drupal.org/node/add and selecting either "module project" or "theme project" as appropriate.
- Fill in the human-readable name as the "name" field.
- The short project name is the "short name".
- Make sure to select "full project" for the project type.
- The "maintenance status" and "development status" will speak to how much effort you intend to put into maintaining the project. Note: it is completely acceptable to upload something with no current plans to do anything else with it, you’re under no obligation to put further effort.
- For modules, pick a suitable category, or something that’s close.
- Fill in a suitable description for the project.
- If the business that is funding the work has an organization page on drupal.org, use the "Organization" autocomplete field to find them, and then maybe explain how they’re supporting the work, e.g. "Funded initial development".
- Create a new repository for the project ("git init .") and make sure it’s on a suitably named branch corresponding to the release of Drupal core it is for, e.g. the first branch for a Drupal 7 project would be "7.x-1.x" while for Drupal 8 it would be "8.x-1.x".
- Upload the code to the new repository; see the "Version control" tab on the project page for details.
- Tag an alpha or beta release, depending upon how stable the project is.
- Push the tag up to the repository.
- On the project page click the "Add new release" link.
- In the selector choose the new tag that was just created and click "Next".
- Fill in a brief description for the new release - something as simple as "Initial release of the [whatever] project" is fine.
- Click the "save" button and then wait a few minutes for the release to be packaged up.
While that might seem like a lot of steps, with a little familiarity of the drupal.org project forms they can be completed pretty quickly. The project can now be downloaded from the Drupal website and via composer, reducing the amount of maintenance effort required on custom code for a site. And, most importantly, this piece of reusable functionality has been pulled from a site and made available to others, fulfilling a key reason to use an open source system (i.e. Drupal) in the first place.
Building from there
Some of the suggested next steps are:
- Identify what bugs should be fixed or improvements made to make a "1.0" release, then create issues for each idea and work through them methodically. Keep in mind some of the points from my blog post on porting modules to Drupal 8 and keep the focus small.
- Release a rudimentary v1.0 with the notion that improvements can be added later.
- Continue improving the tests and included documentation.
- Write documentation pages.
- Opt in for security coverage for the project.
- Collaborate with others to make it better.