Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Jan 29 2021
Jan 29

January 29, 2021

What is hook_update_N()?

Let’s say you are developing a Drupal module (custom or contrib) which tracks how many visitors landed on specific node pages, version 1 of your code might track visitors by nid (node id) in the database using a table like this:

nid visitors 1 4 13 22

Let’s set aside the debate over whether the above is a good idea or not, but once your code has been deployed live to production sites, that’s what the data will look like.

This module might work very well for a long time, and then you might have the need to track not only nodes but also, say, taxonomy term pages. You might reengineer to look like this:

type id visitors node 1 4 node 13 22 term 4 16

To achieve this change when the first version of your database is already out in the wild, you need to tell target environments to update the database schema. This is done using hook_update_N(), and you would replace the N() by incremental numbers, something like this:

 * Update database schema to allow for terms, not only nodes.
function hook_update_9001() {

If this case 9 is the major version (Drupal 9) and 001 because this is the first update to your code.

Each module tracks which version it’s using, so that if code introduces new hook_update_N() functions, it will know to run them only once. You can tell which schema version any installed module is using by running, for example:

drush ev "print(drupal_get_installed_schema_version('webform'))"

This might tell you, for example, that Webform’s current schema is 8621. This means that the latest update hook that was run is Webform’s hook_update_8621(). If the codebase introduces hook_update_8622(), say, or hook_update_8640() (you can skip numbers if need to), then the database will be marked as out of date and running drush updb will run the the new hook and update the installed schema version.

If you ever need to re-run an update hook (which happens rather rarely), you can update the schema, like this:

drush ev "drupal_set_installed_schema_version('webform', 8620)"

So what’s wrong with this?

This works well almost all the time, and you can automate your deployment process to update the database, making sure your schemas are always in sync. However as developers and site users it is important to be aware of certain drawbacks of hook_update_N(), which I’ll get to in detail:

  • hook_update_N() tightly couples the database to the code version;
  • it makes gradual-deployment on multi-container setups such as Kubernetes fragile (or impossible);
  • rollbacks are not possible;
  • it can add considerable compexity to deployment of configuration.

The shaky foundation of database-driven websites

The idea of version control is paramount to how we conceive of computer code. If you’re following the precepts of continuous deployment, then every version of your code needs to “work” (that is, tests need to pass, or, at the very least, it needs to be installable).

For example, let’s assume a bug makes it to your production envrionment for version 5 of your code, and you know this bug was not present on version 4 of your code, you should theoretically be able to check out version 4 and confirm it was working, then figure out what the difference it between version 4 and 5.

In fact this is exactly how things work on static sites such as Jekyll: all your data and your functionality (Javascript) are in your codebase. Each version of your code will be internally coherent, and not rely on an external unversioned database to do something useful.

On database-driven projects based on Drupal or Wordpress, if you check out version 4 of your codebase, it will probably not do anything useful without a database dump which was created using version 4 of your code.

Therefore, although we all use version control for our code, in a way we are fooling ourselves, because critical parts of our project are not version-controlled: the database dump, the ./sites/default/files folder, and the private files folder.

Although it makes sense for certain elements to be a database or on ./sites/default/files, for example, an encrypted user account password or a user’s avatar; for other elements such as your “About page” text, it would really make a lot more sense for this to be under version control.

In fact, the blog post you are reading right now is a file under version control on Jekyll, which you can see using this link, and not some collection of opaque, unversioned, entries in database tables with names like node__body, node__field_tags, node_field_revision, which can be changed at a moment’s notice by any module’s hook_update_N() functions.

Oh, did I mention that I love Drupal?

Tight code-database coupling

Let’s imagine a world where the database schema never changed. A world where hook_update_N() does not even exist.

In such a world, you could take any version of your code, and any version of your database dump (say, the latest version), combine the two on a test environment, and debug errors at will.

In the real world, every time any module updates the database schema, it makes the database more tightly coupled to the current version of the codebase.

Let’s take our “number of visitors per entity” code we had earlier: if I use an old codebase which expects my table to contain fields “nid” and “visitors”, but my only available database dump has fields “type” “id”, “visitors”, the history of my carefully version-controlled codebase will be useless, and old versions will fail with an error such as:

ERROR 1054 (42S22): Unknown column 'id' in 'field list'.

Gradual deployments

Mostly we think of Drupal sites as being on a server with one copy of the codebase, and one copy of the database. So the concept of keeping the database and code “in sync” makes sense.

But as more and more teams use containers and Kubernetes-type container-orchestration systems, high-traffic sites might have, say, one performance-optimized database, and then 5, 10 or 20 load-balanced copies of your PHP code.

Acquia uses such a setup behind the scenes for its cloud hosting, so it’s good to develop with this in mind. On Acquia’s setup, all PHP container use a single, shared database, as well as shared private and public files directories.

But the PHP containers do not share the /tmp directory. This means that every time you perform a web request on a server, the load balancer might direct you to a container with its own /tmp, whose contents differ from other containers’ /tmp.

It’s important to realize this if your code stuff such as building large files over several web requests, and can lead to hard-to-diagnose bugs such as:

But, in addition to providing you with headaches such as the above issues, multiple containers can also allow you to do gradual deployments of new code, reducing the cost of potential failure.

For example, let’s say you have 20 Drupal containers with 20 copies of your codebase, and each Drupal container is connected to a shared database, and shared files and private files directories. If you are deploying a risky update to your code, you might want to start by deploying it to 25% of the containers (5). Then if there are no adverse effects, scale up to 10 the next day, then the entire 20 the day after.

Code that uses hook_update_N() can break this workflow: because all containers share the database, if container 1 has the new version of your code and updates the database accordingly (so that the new database fields are “type” “id”, “visitors”); then container 10 (which uses the old version of your code) will fail when it looks up the database field “nid”.


Let’s forget about fancy container orchestration and just look at a typical Drupal website. A simple real-world site might have a “contact us” webform and some pages, plus some custom functionality.

Let’s say you are deploying a change to your codebase which triggers a hook_update_N(). No matter the amount of unit tests and testing on stage, there is always the possibility that a deployment to production might trigger unforseen issues. Let’s assume this is the case here.

A typical deployment-to-production scenario would be:

  • You backup your production database.
  • You install your new code.
  • You run drush updb which updates the database schema based on your hook_update_N().
  • A few hours pass. Several people fill in your contact form, which means now your database backup from step 1 is out of date.
  • You realize your newly-deployed code breaks something which was not caught by your stage testing or your automated tests.

In a situation like this, if you did not have hook_update_N()s in your code, you could simply roll back your codebase on production to the previous version.

However, this is no longer an option because your database will not work with previous versions of your codebase: there is no hook_downgrade_N(). You are now forced to live with the latest version of your code, and all the benefits of version-controlling your code are for naught.

Config management

Let us recall the elements which make up a Drupal website:

  • Versioned code.
  • Unversioned database and file directories.

If you are using configuration management and a dev-stage-production workflow, there is a third category:

  • Configuration, including the list enabled modules, defined node types and fields, which exist both in the database and in unversioned code.

It is worth recalling a typical workflow:

  • add field_new_field to the article node type on your local machine.
  • the field is now in your local development database but not in your codebase
  • drush config-export
  • the field is now in your local development database and also in your codebase
  • do all your testing and push your code to production

At this point your field is in your production codebase but not your production database.

You probably have a deployment script which includes a “drush updb” step. The question is: do you run “drush config-import” before or after “drush updb”?

It turns out this is not that easy a question to answer. (Drush also provides a drush deploy command which combines configuration import and database updates.)

Regardless of your deployment process, however, we need to take into account a more troubling possibility:

In addition to relatively benign database schema updates, hook_update_N() can modify configuration as well.

In such a case, if you are not careful to run hook_update_N() first on your development environment, then export the resulting configuration, then run your deployment, you may run into the following problem:

#3110362 If an update hook modifies configuration, then old configuration is imported, the changes made by the update hook are forever lost.

Let’s look at a real-world example using the Webform module. Let’s install a new Drupal 8 site with Webform 5.23, then export our configuration, then upgrade to Webform 6.x and import our old configuration. We’ll look at the kind of headache this can lead to (note to beginners: do not do this on a production site, it will completely erase your database).

composer require drupal/webform:5.23
drush site-install -y
drush en webform_ui -y
drush config-export -y

This puts your current site configuration into code. Among said configuration, let’s focus on a single piece of configuration from Webform:

drush config:get webform.settings settings.default_page_base_path
# 'webform.settings:settings.default_page_base_path': form

The base path for webforms is “form”. This tells Webform to build URLs with a structure such as https://example.com/form/whatever.

Let’s now update webform, and our database.

composer require drupal/webform:6
drush updb -y
drush config-import -y

In Webform’s webform_update_8602(), the config item webform.settings:settings.default_page_base_path is changed from “form” to “/form”.

But we are re-importing old config, which overwrites this change and reverts webform.settings:settings.default_page_base_path to “form”, not “/form”

To see the type of hard-to-diagnose error to which this might lead, you can now log into your Drupal site, visit /admin/structure/webform, create a webform named “test”, and click on the “View” tab.

Because the base path lack the expected leading prefix, you now get the “not found” URL /admin/structure/webform/manage/form/test, instead of the expected /form/test – a critical bug if you are on a production site.

In addition, this has a number of cascading effects including the creation of badly-formatted URL aliases which you can see at /admin/config/search/path.

If you find yourself in this situation on production, you need to revert your Webform schema version on your development environment, export your config, reimport it on production, and resave your forms, and potentially fix all your paths starting with “form” on /admin/config/search/path so that they start with “/form”.

To be fair, this is not the fault of the Webform maintainers. In my opinion it shows a fundamental frailty in hook_update_N() combined with lack of documentation on deployment best practices. However, if we strive for Drupal to be a robust framework, there should not be a single point of failure (in this case not strictly adhering to fickle, badly-documented deployment procedures) which can lead to major instability on production.

How do we fix hook_update_N()?

Here are a few approaches to avoid the potential damage done by hook_update_N():

Approach 1: don’t use hook_update_N()

When possible, you might consider not using hook_update_N() at all. Consider our “number of visitors per node” module from earlier.

Instead of a hook_udate_N(), your code could do something like this:

  • Do not change the field name from “nid” to “id”. Even though “id” makes more sense, the field is called “nid”, just leave it at that.
  • Do not expect there to be a “type” field. If your code needs it, for example if creating an entry for the first visitor to a non-node entity, your code can create it.
  • Assume an empty “type” means you are dealing with a node.

The above approach adds complexity to your code, which you can add to a “storage” abstraction class. Although not ideal, this does away with the need to use hook_update_N().

Approach 2: Don’t use hook_update_N() to update configuration

Updating configuration, as seen previously, is even more dangerous than updating non-configuration database tables. So if at all possible, avoid it.

In the Webform example given above, it might have been reasonable to consider keeping with the old non-leading-slash format for path prefixes, rather than update configuration.

When you absolutely must update configuration, you could consider the possibility that certain users might have reimported old configuration, and provide error-checking and hook_requirements() (displaying error messages on the /admin/reports/status page) accordingly.

Approach 3: Robust exception handling

Do not assume that your database schema, or your configuration structure, is up-to-date. If you decide to provide a hook_update_N() to update the schema from, for example, “nid” and “visitors” to “type”, “id”, “visitors”, when querying the database, you might want to consider the possibility that for whatever reason the database is not up-to-date. Here is some pseudo-code:

public function num_visitors_for_entity($id, $type = 'node') : int {
  try {
    return $this->query_database($type, $id);
  catch (\Exception $e) {
    return 0;

That way, if your database and code are not in sync, it’s not going to break your entire site, but rather log an exception and fail gracefully.

Approach 4: keep config changing logic idempotent and separate from update hooks

Let’s look again at Webform’s webform_update_8602(), the config item webform.settings:settings.default_page_base_path is changed from “form” to “/form”.

I would recommend having a separate function to update config, and call that function from the update hook. That way, if a development team makes the mistake of not updating their configuration before importing it into production, it will become easier to run, say “my_module_update_configuration()”.

Then, your hook_requirements() might perform some sanity checks to make sure your configuration is as expected (in this example, that the “webform.settings:settings.default_page_base_path” config item has a leading slash). If this smoke test fails, developers can be directed to run my_module_update_configuration() which will update all configuration to the required state.

In addition, my_module_update_configuration() can be made idempotent, meaning: no matter how often you run it, you will always end up with the desired state, and never get an error.


Please enable JavaScript to view the comments powered by Disqus.
Jan 20 2021
Jan 20

January 20, 2021

This post is aimed at web development teams and is not tied to a specific technology. We will aim to not get more technical than is needed, but rather to explore what Continuous integration (CI) is, and how it can help save teams money within a month of it being set up.

What is continuous integration?

Although several definitions of CI have been proposed, we will use the following definition in the context of this post:

Cotinuous integration (CI) is the practice of running any number of tests, automatically, on a project, periodically and/or whenever the code changes. For CI practitioners, the number one priority is for tests to always be passing.

A simple example, please

Here is the very simplest example I can think of:

Let’s say you’re maintaining an old-school HTML website (no fancy stuff like databases or PHP), your team may decide to set up CI to make sure a file called “index.html” exists in your codebase: if it exists, your test passes; if it is absent, your test fails.

Checks may be run every time your code is changed.

Your team might store code on GitHub, and link a cloud CI provider such as CircleCI to your codebase, having it trigger every time your code changes.

You will then define a script which is your definition of “what it means for your your codebase to pass”: checking for the existence of “index.html” is a one-line script.

A more complex example

Although the example above has value, it is very simple, and you may soon find yourself wanting to add higher-value tests to your script. This ability to add complexity over time is a powerful feature of CI: getting started is simple, and you can add as many tests as you want over time depending on your available resources.

Let’s say your team is maintaining a Drupal or Wordpress codebase with lots of complex code, your team may set up a CI server that:

  • checks for broken links on the live environment every so often;
  • checks every few minutes that the live environment is responding and has some expected keywords on its front page;
  • every so often, checks that the live environment adheres to certain Accessibility standards;
  • every so often, checks that the live environment is not reporting any errors;
  • on every code change, perform some static analysis on custom PHP code: for example, that a function which expects an array as an argument is never called with a string.
  • on every code change, make sure PHP code adheres to coding standards (for example, functions should have comments; and indenting should be correct).
  • on every code change, create a dummy Drupal or Wordpress site with a dummy database and make sure your site fires up, and run some end-to-end tests against it.
  • etc., etc.

A cloud-based tool such as CircleCI can work well to check the codebase when it is changed; and a hosted tool such as Jenkins might be a good fit for running periodic checks (such as a sanity check making sure the production environment works).

The above example corresponds to real-world checks I perform on lost of projects I maintain; and both CircleCI and Jenkins are tools I have been using for years.

So how much does all this cost?

“How much does this cost?” is actually the wrong question. “How much can I save?” is a better way of putting it. Consider the following graph, the horizontal axis is time, and the vertical axis is cumulative project cost.

  • The red line is business as usual: because we are not maintaining CI scripts or setting up tests, the up-front cost is low. But eventually you’ll lose control of your codebase and spend all your time putting out fires (I call this the “technical debt wall”).
  • The blue line is the CI approach, higher up-front cost to set things up, but eventually you’ll get less errors.
  • Where the two lines intersect, I call the “sweet spot”. That’s when you start saving money. Your “sweet spot” is not months or years away: I firmly believe it should happen within a month. If it takes longer than a month, you’re overengineering your CI system.

So what are these up-front costs?

The up-front costs are:

  • Creating a simple script which defines what it means for your code “to work”. If you find this intimidating, just have your script check for a file that must be present, as in the simple example presented earlier.
  • Make sure your code is tracked in GitHub or BitBucket.
  • Make sure your entire team accepts the principle that making tests pass is the number one priority. This is crucial. If you start accepting failing tests, then CI becomes a useless burden. This also means every member of your team must agree with every test that is performed. If a test is not important enough to warrant dropping everything when it fails, then you should not have that test in your codebase.
  • Integrate a simple, free CI cloud provider like CircleCI and make sure it works.

All of the above, together, can take between an hour and a day.

How about the ongoing costs?

Ongoing costs are closely relate to the complexity of your CI setup. If you are just testing for an “index.html” file, your ongoing costs are close to zero, but may include:

  • dealing with errors and updates in the CI script itself. Don’t forget the CI script is computer code, and like any computer code, it needs to be maintained.
  • updating the CI script to deal with API changes in the cloud CI provider.
  • fixing false negatives. For example, someone may change the filename from index.html to index.htm, which might require you to fix your test script to also test for index.htm in addition to index.html.
  • onboarding new team members to understand the importance of making sure tests always are passing.

If your tests are super simple (such as checking that an “index.html” file exists), the above costs are low, probably less than one hour a month.

If your tests are complex (as in our second example, above), you might set aside 5 to 10 hours a month for ongoing costs.

Obviously, if your ongoing costs are higher than your savings, then you are “over-testing”.

So what are the benefits?

The fundamental trick of CI is to keep your benefits higher than your costs. Let’s go back to our simple “index.html” example:

  • We have already established that there are minimal up-front and ongoing costs.
  • There are also ongoing savings: once you know that your index.html file is guaranteed to exist, your manual testing time decreases.
  • The cost in lost revenue, lost confidence, and debugging time in case someone accidentally deletes index.html from your website would be considerable high.

Based on the above, you can conclude whether it’s worth implementing CI.

Continuous improvement of your CI setup

Checking for “index.html” is probably of very low value, but once you’ve done that, you’ve also set up the foundation to improve your script. Every time you feel your CI script has a positive cost-benefit ratio, it is time to improve your CI script. In practice, I have found that in projects under active development, the CI setup gets constantly improved.

Specifically, any time a problem makes its way to production, it should be a gut reaction to introduce a fix, along with a test to make sure the problem never happens again.

The key is making incremental improvements, making sure your cost-benefit ratio is always positive.

Docker and containerization

Docker, and containerization generally, embed software and configuration in computer code along with your project code.

The widespread adoption of Docker and containerization in recent years has been crucial for CI. Without containerization, let’s say you want to run PHP static analysis, start a database with a Drupal site, run end-to-end tests, you need to install a bunch of software on your CI server (or your laptop), make sure the versions and configuration are in sync with your local development setups. This is simply too expensive.

Docker makes all this easy: simply put, Docker abstracts all the software and configuration, making software act the same on any computer that has Docker installed.

If you are not using Docker and you’d like to see how simple this makes things, install and launch Docker Desktop on your computer, give it 6Gb RAM instead of the default 2Gb in its preferences, then you’ll be able to run all tests on my Drupal Starterkit project, without any additional fiddling with configuration of software:

cd ~/Desktop && git clone https://github.com/dcycle/starterkit-drupal8site.git
cd starterkit-drupal8site

It should take about 10 minutes to run all tests and it will not add any software to your computer; everything is done on throwaway “containers”. (In general, tests become a lot more frustrating to developers as they take longer to run; which is why I have a policy of not accepting tests which take more than 20 minutes to run.)

The amount of software packages and configuration required to run all the tests in this example is enormous: database servers and configuration, passwords, permissions, PHPUnit, the right version of PHP and Apache or Nginx…; however it’s all defined in Docker files and in code, not on host computers.

Which is why you can run the tests in three lines of code!

This makes it possible to run these complex tests on your computer without installing any software other than Docker.

This also makes it possible to run these exact tests, sans extra configuration, on CircleCI or other CI providers which support virtual machines with Docker preinstalled. In fact, that’s exactly what we’re doing with the Drupal Starterkit. CircleCI even provides a cute badge to indicate whether tests are passing.

Click on the badge below to see test results on CircleCI, which should be identical to the results on your computer if you ran the the above script (you’ll need to log in with your GitHub or BitBucket account).



Whether you are using a cloud service such as CircleCI, or hosting your own CI server with Jenkins or other software, be aware that it adds a potential attack vector for hackers, especially because by design, CI software needs access to your codebase.

In early 2021, a vulnerability was discovered in JetBrains TeamCity (Widely Used Software Company May Be Entry Point for Huge U.S. Hacking, New York Times, January 6th, 2021) in relation to the major SolarWinds hack.

Make sure you have a solid security policy, including the Principle of Least Privilege (POLP) and other industry-standard security approaches; also make sure your codebase, even if it’s private, does not contain any sensitive data, including API keys.


With continuous integration (CI), you can let computers do the grunt work of looking for bugs in your codebase, liberating your developers to do more productive work, reducing the number of bugs that make it into production, and increasing the level of confidence of all stakeholders in your software, and deploying frequently.

And, above all, saving money.

CI can be as simple or as complex as you need: start small, then let your CI process grow as your team becomes more comfortable with it.

Please enable JavaScript to view the comments powered by Disqus.
Oct 03 2017
Oct 03

October 03, 2017

This article is about serving your Drupal Docker container, and/or any other container, via https with a valid Let’s encrypt SSL certificate.

Edit: if you’re having trouble with Docker-Compose, read this follow-up post.

Step one: make sure you have a public VM

To follow along, create a new virtual machine (VM) with Docker, for example using the “Docker” distribution in the “One-click apps” section of Digital Ocean.

This will not work on localhost, because in order to use Let’s Encrypt, you need to demonstrate ownership over your domain(s) to the outside world.

In this tutorial we will serve two different sites, one simple HTML site and one Drupal site, each using standard ports, on the same Docker host, using a reverse proxy, a container which sits in front of your other containers and directs traffic.

Step two: Set up two domains or subdomains you own and point them to your server

Start by making sure you have two domains which point to your server, in this example we’ll use:

  • test-one.example.com will be a simple HTML site.
  • test-two.example.com will be a Drupal site.

Step three: create your sites

We do not want to map our containers’ ports directly to our host ports using -p 80:80 -p 443:443 because we will have more than one app using the same port (the secure 443). Port mapping will be the responsibility of the reverse proxy (more on that later). Replace example.com with your own domain:

docker run -d \
  -e "VIRTUAL_HOST=test-one.$DOMAIN" \
  -e "LETSENCRYPT_HOST=test-one.$DOMAIN" \
  -e "[email protected]$DOMAIN" \
  --expose 80 --name test-one \
docker run -d \
  -e "VIRTUAL_HOST=test-two.$DOMAIN" \
  -e "LETSENCRYPT_HOST=test-two.$DOMAIN" \
  -e "[email protected]$DOMAIN" \
  --expose 80 --name test-two \

Now you have two running sites, but they’re not yet accessible to the outside world.

Step three: a reverse proxy and Let’s encrypt

The term “proxy” means something which represents something else. In our case we want to have a webserver container which represents our Drupal and html containers. The Drupal and html containers are effectively hidden in front of a proxy. Why “reverse”? The term “proxy” is already used and means that the web user is hidden from the server. If it is the web servers that are hidden (in this case Drupal or the html containers), we use the term “reverse proxy”.

Let’s encrypt is a free certificate authority which certifies that you are the owner of your domain.

We will use nginx-proxy as our reverse proxy. Because that does not take care of certificates, we will use LetsEncrypt companion container for nginx-proxy to set up and maintain Let’s Encrypt certificates.

Let’s start by creating an empty directory which will contain our certificates:

mkdir "$HOME"/certs

Now, following the instructions of the LetsEncrypt companion project, we can set up our reverse proxy:

docker run -d -p 80:80 -p 443:443 \
  --name nginx-proxy \
  -v "$HOME"/certs:/etc/nginx/certs:ro \
  -v /etc/nginx/vhost.d \
  -v /usr/share/nginx/html \
  -v /var/run/docker.sock:/tmp/docker.sock:ro \
  --label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
  --restart=always \

And, finally, start the LetEncrypt companion:

docker run -d \
  --name nginx-letsencrypt \
  -v "$HOME"/certs:/etc/nginx/certs:rw \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  --volumes-from nginx-proxy \
  --restart=always \

Wait a few minutes for "$HOME"/certs to be populated with your certificate files, and you should now be able to access your sites:

A note about renewals

Let’s Encrypt certificates last 3 months, so we generally want to renew every two months. LetsEncrypt companion container for nginx-proxy states that it automatically renews certificates which are set to expire in less than a month, and it checks this hourly, although there are some renewal-related issues in the issue queue.

It seems to also be possible to force renewals by running:

docker exec nginx-letsencrypt /app/force_renew

So it might be worth considering to be on the lookout for failed renewals and force them if necessary.

Edit: domain-specific configurations

I used this technique to create a Docker registry, and make it accessible securely:

docker run \
  --entrypoint htpasswd \
  registry:2 -Bbn username password > auth/htpasswd

docker run -d --expose 5000 \
  -e "VIRTUAL_HOST=mydomain.example.com" \
  -e "LETSENCRYPT_HOST=mydomain.example.com" \
  -e "[email protected]" \
  -e "REGISTRY_AUTH=htpasswd" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ 
  --restart=always -v "$PWD"/auth:/auth \
  --name registry registry:2

But when trying to push an image, I was getting “413 Request Entity Too Large”. This is an error with the nginx-proxy, not the Docker registry. To fix this, you can set domain-specific configurations, in this example we are allowing a maximum of 600M to be passed but only to the Docker registry at mydomain.example.com:

docker exec nginx-proxy /bin/bash -c 'cp /etc/nginx/vhost.d/default /etc/nginx/vhost.d/mydomain.example.com'
docker exec nginx-proxy /bin/bash -c 'echo "client_max_body_size 600M;" >> /etc/nginx/vhost.d/mydomain.example.com'
docker restart nginx-proxy

Edit: Reverse proxy on Drupal 8 or 9

Thanks to @wells on this issue and nitin.k on this issue for pointing me in the right direction on how Drupal can know its base url should be HTTPS. In order to use modules such as social_auth_google and metatag which require Drupal to know its public URL even if it is behind a reverse proxy, you need to figure out the reverse proxy IP.

To do so temporarily install devel_php on your site, and then go to /devel/php and enter dpm($_SERVER['REMOTE_ADDR']);. This will give you a result such as It is not the same IP as what you get when you ping your URL, or when you inspect the headers the reverse proxy sends to Drupal.

Then add this to your settings, and clear your cache:

$settings['reverse_proxy'] = TRUE;
$settings['reverse_proxy_addresses'] = [''];


You can now bask in the knowledge that your cooking blog will not be man-in-the-middled.

Please enable JavaScript to view the comments powered by Disqus.
Jun 04 2014
Jun 04

Currently Drupal core does not offer any hook to do actions after a node/entity is inserted/updated/deleted in Database. So for example you can not send an email mentioning the node after the node is inserted because Drupal uses SQL transactions and the node is not yet fully written to database when hook node presave is called so if for any reason the transaction is rolled back, users will receive a false mail.

So Hook Post Action module introduces several new Drupal hooks to overcome this limitation
  - hook_entity_postsave
  - hook_entity_postinsert
  - hook_entity_postupdate
  - hook_entity_postdelete
  - hook_node_postsave
  - hook_node_postinsert
  - hook_node_postupdate
  - hook_node_postdelete

Feb 18 2010
Feb 18

Acording to A Survey of 2,368 Drupal Sites a large number of websites are still running Drupal5 so i think it's a perfect time and perhaps the last change to start planning for safely upgrading our good old Drupal 5 websites to Drupal 6 (If any remained), Drupal 5 will be deprecated as soon  Drupal 7 final comes out. Some module maintainers already removed Drupal 5 releases from their projects which is a trouble for website running Drupal 5. Drupal 7 final release is not out yet and i guess that it takes about 2 years until Drupal 7 becomes as solid as Drupal 6 and also most modules upgrade to this new version that's because too many awesome new features included in Drupal 7 and they require quite some polishing.

Why not wait for Drupal 7 and directly upgrade? . As long as i know it's not possible to skip Drupal 6. You'll have to upgrade to Drupal 6 and then Drupal 7 if you want Drupal to do it automatically for you. Besides if your site is critical and you want an stable site i guess you'll have to wait for about 2 years until everything is ready (Including contributed modules port). And also in my case there are certain features i should implement that are not available in Drupal 5, and reimplementing them requires much more time than upgrading the website. i only have one website left with Drupal 5 , it didn't worth upgrading before but today after i checked upgrade status to see the status of the contributed modules i used on the website, they've  been either upgraded , included in Drupal 6 or replaced by better alternatives.

Upgrading Drupal has never been easy, not because of Drupal itself but because of the fact that Drupal's strength relies mostly on its modules and we usually install dozens of them on each website (I have websites with over 90 modules installed, you may have even more :) ). And not all of these modules are well maintained. Switching from one module to another is not easy , specially for complex modules. that's what makes upgrading sometimes very time consuming, not to mentioned the testing process!!

As i mentioned before upgrading is time consuming and with lots of module i've installed on this site it's going to take even more. since my time is limited i should pick the fastest way so i decided to divide the modules into two groups, Critical and Non Critical (Like some administration modules). This way i can launch the new version of the website and then upgrade the remaining modules one by one.


Perhaps the most important part of the upgrade process is planning. As you may already know, Drupal is also a Framework which means that on each release both users and developers have to deal with some fundamental changes. It's actually Drupal's great advantage. So upgrading Drupal is not only about updating it's also about improving, changing, customizing and also sometimes simplifying. For this reason planning is even more important. You might be able to find far better alternatives for your current modules, get rid of many core/module patches, simplify the modules you've developed or customized, by benefiting from new core and contributed modules' features.

The first part of planing is reading more about the new Drupal version and its new features. And the next part is checking all contributed modules and finding better alternatives which i'm going to describe here. So we need to install and use the awesome upgrade status module.

Check the status of contributed modules


I usually patch both modules and Drupal core specially when i find a bug. and almost always contribute this patches back to the community. It's important to keep track of this changes otherwise i may break the site with every core or module update! There is a young module called patchdoq by @doq which is going to simplify patching , right now it needs our love :)

Most of the time i use version control system and follow these steps :

  • Making the patch
  • Testing it
  • Submit the patch to the modules' issue queue and keep the issue link
  • Commit the patch and use patch keyword , simple description of what it does and a link to the issue on issue queued.

Before we start lets have a look at the contributed modules and see how difficult it's going be be , i can simply check the history of the module using VCS and see whether there is any patch keyword  on history, if i find anything since i already have a link i can check whether its been committed or not. If you're not using VCS you can use Drupal.org's advanced search and enter your username in "Participant" or "Submitted by" field and fill in the project name field.

Core patches : i've had plenty of them like this one Very expensive calls drupal_lookup_path but they're no longer required for Drupal 6


Critical (Check list)

Here is the list of modules i used on this particular website and their status, i'm sure that it will come handy for anyone willing to upgrade. i mentioned alternatives for deprecated module as well.

I sorted the modules by their dependency because it's important to upgrade all the module's dependencies first.

Note : I usually use Devel Macro module to repeat the configuration on the production site.

Views by @merlinofchaos , @dereine , @dww and many others : The Views module provides a flexible method for Drupal site designers to control how lists and tables of content (nodes in Views 1, almost anything in Views 2) are presented. Traditionally, Drupal has hard-coded most of this, particularly in how taxonomy and tracker lists are formatte ...

  • Ported : Yes (Can't imagine Drupal without this one :) )
  • Installed version : 5.x-1.6
  • Patched : No
  • Latest Drupal 5's version :  Uptodate 
  • Latest Drupal 6's stable version :   6.x-2.8
  • Dependency for the other modules : Yes
  • Better Alternatives : You kidding?
  • Special note for upgrade : Old views should be converted via admin/build/views/tools/convert.

Token by @eaton , @greggles and many others : Tokens are small bits of text that can be placed into larger documents via simple placeholders, like %site-name or [user]. The Token module provides a central API for modules to use these tokens, and expose their own token values ...

  • Ported : Yes
  • Installed version : 5.x-1.13
  • Patched : No
  • Latest Drupal 5's version : Uptodate
  • Latest Drupal 6's stable version : 6.x-1.12
  • Dependency for the other modules : Yes
  • Better  Alternatives : No
  • Special note for upgrade : No

jQuery Update by @jjeff , @webchick and the others : This module facilitates an upgrade of jQuery in Drupal core and allows other contrib modules to rely on a newer jQuery version ...

  • Ported : Yes
  • Installed version : 5.x-2.0 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.1
  • Dependency for the other modules : Yes
  • Better  Alternatives : No
  • Special note for upgrade : No

Pathauto @greggles , @Freso , @mikeryan and the others : The Pathauto module automatically generates path aliases for various kinds of content (nodes, categories, users) without requiring the user to manually specify the path alias. This allows you to get aliases like /category/my-node-title.html instead of /node/123. The aliases are based upon a "pattern" system which the administrator can control ...

  • Ported : Yes
  • Installed version : 5.x-2.3
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.2
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade : No

 Content Construction Kit (CCK) by @yched , @markus_petrux , @KarenS and many others :  The Content Construction Kit allows you to add custom fields to nodes using a web browser ...

  • Ported : of course it's ported :) , Who can live without CCK?!
  • Installed version : 5.x-1.10
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.6
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade : No

Javascript Tools by @nedjo , @ray007 , @m3avrck and the others : Javascript Tools provides a common set of methods extending those available in Drupal core (drupal.js) for Javascript and AJAX module development in Drupal ...

  • Ported : Yes
  • Installed version : 5.x-1.2
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.0
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade :No

Date by @KarenS , @hass and many others : The date module is a flexible date/time field type for the cck content module which requires the CCK content.module and the Date API module ...

  • Ported : Yes
  • Installed version : 5.x-2.8
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.4
  • Dependency for the other modules : Yes
  • Better Alternatives : No
  • Special note for upgrade :No

 Boost by  @Arto , @wulff , @mikeytown2   Boost provides static page caching for Drupal enabling a very significant performance and scalability boost for sites that receive mostly anonymous traffic ...

  • Ported : Yes, Not only ported but thanks to its recent very active maintainer @mikeytown2 it's in a very good shape
  • Installed version : 5.x-1.0
  • Patched : Yes, but all the patches committed at least to 6.x version by the maintainers Remove symlink creation. Let each path have own file
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.18
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : New version of boost are quite different, complete uninstall/reinstall is easier. Also make sure that you've updated .htaccess and create cache folder when moving to production site. admin/settings/performance/boost set cache folder to temp/boost

Comment Info by @Cainan , @mfer : This module allows for anonymous guests to keep persistent comment info between comment postings ...

  • Ported : Yes, Couldn't be better, Ported to core.
  • Installed version : 5.x-3.1
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : In Core
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : No, it just works

CAPTCHA by @wundo , @soxofaan , @Rob Loach and lots of others : A CAPTCHA is a challenge-response test most often placed within web forms to determine whether the user is human. The purpose of CAPTCHA is to block form submissions by spambots, which are automated scripts that post spam content everywhere they can ...

  • Ported : Yes,  Ported and also has a very active maintainer @soxofaan
  • Installed version : 5.x-3.2 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.1
  • Dependency for the other modules : Yes
  • Better Alternatives : There are several alternative like Mollom and recaptcha (check out module's page for more alternatives) but since i need something simple i prefer the bundled captcha image. 
  • Special note for upgrade : Currently this module has some serious issue with caching modules please join us to fix this issue. Other than this issue it work fine

Calendar by @KarenS and the others : This module will display any Views date field in calendar formats, including CCK date fields, node created or updated dates, etc. Switch between year, month, and day views. Back and next navigation is provided for all views ...

  • Ported : Yes
  • Installed version : 5.x-2.7 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.2
  • Dependency for the other modules : No
  • Better Alternatives : Not that i'm aware of
  • Special note for upgrade : New Calendar module benefits from Views argument feature, therefore simply converting its views does not work. Arguments should be added. Calendar's default views is a good example. The difference between old calendar views and the new is much so i found it easier to use views export feature and use the default views as a template

Lazy image loader by @sinasalek : Websites with lots of images on a single page might take a long time to load, and it sometimes annoys visitors. for fixing this problem this module loads only the images that are visible to or requested by visitors and the other images loads only when visitor scroll downs to them (This is the default behavior there are other options available live mouseover, mouseclick etc) ...

  • Ported : Yes
  • Installed version : Custom Module 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.1
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : No, default configuration works just fine

Voting API by @eaton , and the others: VotingAPI helps developers who want to use a standardized API and schema for storing, retrieving, and tabulating votes for Drupal content ...

  • Ported : Yes
  • Installed version : 5.x-1.6 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.3  
  • Dependency for the other modules : Yes
  • Better Alternatives : Yes
  • Special note for upgrade : No

Fivestar by @quicksketch , @ezra-g and the others:

  • Ported : Yes
  • Installed version : 5.x-1.16 
  • Patched : Yes, but all the patches committed by the maintainers
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.19  
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : It has some strange issue warning: Division by zero which has been reported several types, but it seems that it still exists. it's not critical bug however
    admin/settings/fivestar select oxygon theme

Image Picker by @hutch and @notsleepy: The Image Picker module is an image upload feature modeled after the image upload currently built into Wordpress but with a few improvements ...

  • Ported : Yes
  • Installed version : 5.x-2.2
  • Patched : Yes, Imageinsert does not work and no javascript error! Not sure wether it's applied or not
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-2.6
  • Dependency for the other modules : No
  • Better Alternatives : Yes , the combination of filefield, imagefield, insert, imagecache module
  • Special note for upgrade : No

 Javascript Aggregator by @derjochenmeyer , @Rob Loach : The aim of the JavaScript Aggregator module is to improve performance of your site with less server requests and bandwidth per page. In Drupal 5, all the JavaScript files will be aggregated into one file and optionally minified. JavaScript aggregation was brought into core with Drupal 6, so the Drupal 6 version of this module goes one step further to minify that file. ...

  • Ported : Yes
  • Installed version : 5.x-1.5
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version :  6.x-1.3
  • Dependency for the other modules : No
  • Better Alternatives : Support File cache but it's too much
  • Special note for upgrade : Configure it here admin/settings/performance

Nodewords by @kiamlaluno , @Robrecht Jacques , @hass and the others : This project allows you to set some meta tags for each Drupal page.

  • Ported : Yes
  • Installed version : 5.x-1.13 
  • Patched : No
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version :  6.x-1.11
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : Make sure to enable at least one of the sub modules that come with Nodewords

Notify by @matt2000 , @beginner , @RobRoy and the others : The notify module allows users to subscribe to periodic emails which include all new or revised content and/or comments much like the daily news letters sent by some websites ...

  • Ported : Yes
  • Installed version : 5.x-1.3
  • Patched : Yes, "limiting number of result in notify module" and it's not committed and also notification of unapprouved comments... which is not committed
  • Latest Drupal 5's version :  Uptodate
  • Latest Drupal 6's stable version : 6.x-1.0
  • Dependency for the other modules : No
  • Better Alternatives : No
  • Special note for upgrade : Check the configuration admin/settings/notify and also users' configuration user/5/notify

Page Title by @nicholasThompson , @JohnAlbin , @robertDouglass and the others: The word "title" is a bit overloaded. Every piece of content in Drupal has a title, and so does every page. The page title is the one found in the HTML head inside the

tag. It is also used on SERPs (Search Engine Result Pages) and can greatly enhance your websites SEO (Search Engine Optimization) ...</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-2.3</span></li><li><span class="existing-version">Patched :</span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version :</span> <a href="http://drupal.org/node/640996">6.x-2.3</a></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/recent_blocks">Recent Blocks</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/71463">@cotto</a> , <a title="View user profile." href="http://drupal.org/user/216048">@Frank Ralf</a><span class="existing-version"> and <a href="http://drupal.org/node/42872/committers">the others</a>: No ported but it's easily achieve using Views and Views UI</span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.3</span></li><li><span class="existing-version">Patched :</span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : Non but the maintainer say </span><a href="http://drupal.org/node/587106">6.x-1.x-dev</a> should work</li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, i'm going to use views instead</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/recent_changes">Recent Changes</a> by </span><a title="View user profile." href="http://drupal.org/user/73064">@rötzi</a><span class="project-title"> , </span><a title="View user profile." href="http://drupal.org/user/19502">@toemaz</a><span class="project-title"> : </span>The recent changes module let's you track all editing on your site. A page is provided with a list of all revisions and comments in chronological order.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.2</span></li><li><span class="existing-version">Patched : </span><span class="existing-version">No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : None but </span><a href="http://drupal.org/node/224958">6.x-1.x-dev</a> exist, don't know whether it works or not.</li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, i'm going to use views instead</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/supernav">Super Nav</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/166383">@chrisshattuck</a> <span class="existing-version"> </span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version :<span class="project-title"> </span><span class="existing-version"> 5.x-1.0</span></li><li><span class="existing-version">Patched : Yes and all committed, </span><a href="http://drupal.org/node/267413">"Don't force frame" and "Show URL in right frame" optoins issues</a> and <a href="http://drupal.org/node/245054">Some images link is not valid when has i18n module enabled or is inside admin</a></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/241062">6.x-1.0</a></li><li>Dependency for the other modules : No</li><li><p>Better Alternatives : Yes, <span class="existing-version"> there is a better alternative by the same developer </span><span class="existing-version"> </span><a href="http://drupal.org/project/navigate">Navigate</a>. This new module unlike Super Nav does not use Frames and it's also more customizable. Drupal 6 also has lots of new administration modules like <a href="http://drupal.org/project/teleport" rel="nofollow">Teleport</a> , <a href="http://drupal.org/project/admin">Admin </a>, <a href="http://drupal.org/project/admin_menu">Admin Menu</a>, etc</p></li><li>Special note for upgrade : No</li></ul><p><span class="project-title"> </span><a href="http://drupal.org/project/scheduler">Scheduler</a> by <a title="View user profile." href="http://drupal.org/user/20786">@Eric Schaefer</a> , <a title="View user profile." href="http://drupal.org/user/265179">@skiminki</a> , <a title="View user profile." href="http://drupal.org/user/39030">@AjK</a> and<a href="http://drupal.org/node/3292/committers"> the others</a> : This module allows nodes to be published and unpublished on specified dates.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.18</span></li><li><span class="existing-version">Patched : Yes, commited<br /></span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span> <a href="http://drupal.org/node/607892">6.x-1.6</a> <span class="version-date"> </span></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : This module used to have some critical issues with publishing nodes incorrectly , so testing it again is very important</li></ul><p><span class="project-title"><a href="http://drupal.org/project/excerpt">Excerpt</a> by </span><a title="View user profile." href="http://drupal.org/user/56015">@hayesr</a> , <a title="View user profile." href="http://drupal.org/user/23157">@hanoii</a> and <a href="http://drupal.org/node/10811/committers">the others</a> <span class="project-title">: </span>Excerpt module allows you to enter a separate excerpt/summary/teaser for a node, which does not have to be a cut off version of the body.</p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <a href="http://drupal.org/node/239311">5.x-1.3</a><span class="existing-version"> </span></li><li><span class="existing-version">Patched : No<br /></span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/607496">6.x-1.1</a></li><li>Dependency for the other modules : No</li><li>Better Alternatives : No</li><li>Special note for upgrade : No</li></ul><p><span class="project-title"><a href="http://drupal.org/project/tinytinymce">Tiny Tiny MCE</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/90065">@Steve Lockwood</a> <span class="existing-version">: </span>Tiny Tiny MCE (TinyTinyMCE) is a Drupal 6 supported module supporting the wysiwyg editor tinymce.<span class="existing-version"> </span></p><ul><li>Ported :<span class="project-title"> Yes</span></li><li>Installed version : <span class="existing-version">5.x-1.5</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : </span><a href="http://drupal.org/node/357053">6.x-1.12</a> <span class="version-date"> </span></li><li>Dependency for the other modules : No</li><li>Better Alternatives : Yes, T<span class="existing-version">here is a perfect alternative <a href="http://drupal.org/project/WYSIWYG">WYSIWYG</a></span></li><li>Special note for upgrade : Should be set here admin/settings/wysiwyg</li></ul><p><span class="project-title">theme_switcher</span><span class="existing-version"> : A custom module i created for separating view and admin theme more accurately<br /></span></p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : Custom</li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No<br /></span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> Yes, Drupal 6 has its functionality build-in there is also two other modules for more customization <a href="http://drupal.org/project/admin_theme">Administration theme</a> and <a href="http://drupal.org/project/system_theme">System Theme++</a></span></li><li>Special note for upgrade : You can set it here admin/settings/admin</li></ul><p><span class="project-title"> </span><span class="project-title"><a href="http://drupal.org/project/default_filter">Default Filter</a> by </span><a title="View user profile." href="http://drupal.org/user/39382">@dllh</a><span class="project-title"> : </span>This module allows you to set default filter formats per node type per role, preventing users from having to tick a checkbox every time they add a new node. This is useful for sites that set the global default to a less rich format to prevent anonymous users from using (for example) full HTML.</p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : <span class="existing-version">5.x-2.1</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No</span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> Yes, </span><span class="project-title"> much better alternative is available <a href="http://drupal.org/project/better_formats">Better Formats</a></span></li><li>Special note for upgrade : Select co-author as well here admin/settings/filters/4<br />Select smart content for both webmaster and co-atuhor  admin/settings/filters/defaults </li></ul><p><span class="project-title"><a href="http://drupal.org/project/smartlinebreakconverter">Smart Line Break Converter</a></span><span class="existing-version"> by </span><a title="View user profile." href="http://drupal.org/user/182824">@nschelly</a> <span class="existing-version">: </span>The smartlinebreakconverter will selectively apply the line break converter filter (in the core filter module) based on whether it thinks it's necessary.</p><ul><li>Ported :<span class="project-title"> No<br /> </span></li><li>Installed version : <span class="existing-version">5.x-1.1</span></li><li><span class="existing-version">Patched : No</span></li><li><span class="existing-version">Latest Drupal 5's version : </span><span class="existing-version"> </span>Uptodate</li><li><span class="existing-version">Latest Drupal 6's stable version : No</span></li><li>Dependency for the other modules : No</li><li>Better Alternatives :<span class="existing-version"> No</span></li><li>Special note for upgrade : No</li></ul><p><em>In the next article i'm going to check the remianing non critical modules and the prepare the development copy for upgrade and finally actuallay doing the upgrade</em></p><div id="nuan_ria_plugin"><object id="plugin0" style="position: absolute; z-index: 1000;" type="application/x-dgnria" height="0" width="0"><param name="tabId" value="ff-tab-332" /><param name="counter" value="1215" /></object></div><form action="/content/upgrading-drupal-5-drupal-6-part-1" accept-charset="UTF-8" method="post" id="fivestar-form-node-1319" class="fivestar-widget"> <div><div class="fivestar-form-vote-1319 clear-block"><input type="hidden" name="content_type" id="edit-content-type" value="node" /> <input type="hidden" name="content_id" id="edit-content-id" value="1319" /> <div class="fivestar-form-item fivestar-combo-text fivestar-average-stars fivestar-labels-hover"><div class="form-item" id="edit-vote-wrapper"> <input type="hidden" name="vote_count" id="edit-vote-count" value="91" /> <input type="hidden" name="vote_average" id="edit-vote-average" value="20.4396" /> <input type="hidden" name="auto_submit_path" id="edit-auto-submit-path" value="/fivestar/vote/node/1319/vote" class="fivestar-path" /> <select name="vote" class="form-select" id="edit-vote-1" ><option value="-">Select rating</option><option value="20">Poor</option><option value="40" selected="selected">Okay</option><option value="60">Good</option><option value="80">Great</option><option value="100">Awesome</option></select><input type="hidden" name="auto_submit_token" id="edit-auto-submit-token" value="b6fe1d78e8866209bd25b6b9e20d03ca" class="fivestar-token" /> <div class="description"><div class="fivestar-summary fivestar-summary-combo fivestar-feedback-enabled"><span class="user-rating">Your rating: <span>None</span></span> <span class="average-rating">Average: <span>1</span></span> <span class="total-votes">(<span>91</span> votes)</span></div></div> </div> </div><input type="hidden" name="destination" id="edit-destination" value="node/1319" /> <input type="submit" name="op" id="edit-fivestar-submit" value="Rate" class="form-submit fivestar-submit" /> <input type="hidden" name="form_build_id" id="form-dvq4M6HKDVFbh0oYv5Ianm12LAKpsROMOHbwyLHQ7PA" value="form-dvq4M6HKDVFbh0oYv5Ianm12LAKpsROMOHbwyLHQ7PA" /> <input type="hidden" name="form_id" id="edit-fivestar-form-node-1319" value="fivestar_form_node_1319" /> </div> </div></form> </div> </div> <div class="links"> <ul class="links inline"><li class="comment_forbidden first"><span><a href="https://sina.salek.ws/user/login?destination=comment%2Freply%2F1319%23co...">Login</a> to post comments</span></li> <li class="addtoany last"><span> <a class="da2a_button" href="http://www.addtoany.com/share_save?linkurl=https%3A%2F%2Fsina.salek.ws%2..." id="da2a_1"><img src="https://sina.salek.ws/sites/all/modules/addtoany/images/share_save_171_1..." width="171" height="16" alt="Share this"/></a> <script type="text/javascript"> <!--//--><![CDATA[//><!-- da2a.script_load(); //--><!]]> </script> </span></li> </ul> </div> </div><!-- /inner --> </div><!-- /node-1319 --> </div><!-- /content-content --> </div><!-- /content-inner-inner --> </div><!-- /content-inner --> </div><!-- /content-region-inner --> </div><!-- /content-region --> </div><!-- /content-group-inner --> </div><!-- /content-group --> </div><!-- /main-content-inner --> </div><!-- /main-content --> </div><!-- /main-group-inner --> </div><!-- /main-group --> </div><!-- /main-inner --> </div><!-- /main --> </div><!-- /main-wrapper --> <!-- postscript-bottom row: width = grid_width --> <!-- footer row: width = grid_width --> <!-- footer-message row: width = grid_width --> <div id="footer-message-wrapper" class="footer-message-wrapper full-width"> <div id="footer-message" class="footer-message row grid16-16"> <div id="footer-message-inner" class="footer-message-inner inner clearfix"> <div id="footer-message-text" class="footer-message-text block"> <div id="footer-message-text-inner" class="footer-message-text-inner inner clearfix"> <table border="0"><tbody><tr><td><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img class="mceItem" src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" alt="Creative Commons License"></a></td><td valign="top" align="left">Except where otherwise <span class="subfoot">noted (Projects section)</span>, content on this site is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a></td></tr></tbody></table></div><!-- /footer-message-text-inner --> </div><!-- /footer-message-text --> </div><!-- /footer-message-inner --> </div><!-- /footer-message --> </div><!-- /footer-message-wrapper --> </div><!-- /page-inner --> </div><!-- /page --> <!--[if (IE 6)]> <script type="text/javascript"> var IE6UPDATE_OPTIONS = { icons_path: "https://sina.salek.ws/sites/all/modules/ie6update/images/", message: "Internet Explorer is missing updates required to view this site. Click here to update... ", url: "http://www.microsoft.com/windows/internet-explorer/default.aspx" } </script> <script type="text/javascript" src="https://sina.salek.ws/sites/all/modules/ie6update/ie6update.js"></script> <![endif]--> <script type="text/javascript" src="https://sina.salek.ws/sites/all/modules/syntaxhighlighter/syntaxhighligh..."></script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- if (Drupal.jsEnabled) { $(document).ready(function () { $('a').Tooltip({ delay: 0, track: true, showURL: false, showBody: "; " }); }); } //--><!]]> </script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- da2a.targets=[ {title:'Upgrading Drupal 5 to Drupal 6, Part 1 | Sina Salek Official Site',url:'https://sina.salek.ws/content/upgrading-drupal-5-drupal-6-part-1'}]; da2a.html_done=true;if(da2a.script_ready&&!da2a.done)da2a.init();da2a.script_load(); //--><!]]> </script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-898205-3', 'auto'); ga('send', 'pageview'); </script> </body> </html> <!-- Page cached by Boost @ 2021-08-30 16:27:25, expires @ 2021-12-20 15:27:25 -->

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