After a Drupal site got hacked

Just like last year’s Drupal security issue, Drupal sites not updated are easily got hacked. To recover from that, it’s better to restore everything from a clean backup( files & db ).

But if you don’t have a clean backup, and the database has too much data you can’t simply rebuild the site. Then maybe here is something you can do. But remember, there is no 100% guarantee you can remove all the backdoors and malwares.


Look at this simple step to confirm that you were hacked: How to Check Your Drupal Site Security

As in above article mentioned, you need to notice the data table ‘menu_route’, searching for file_put_contents like below:

Also look at the users & users_roles tables. Here are some typical names that the hackers used:

  • drupaldev
  • megauser
  • system
  • admin122


Using some module to check the site:

There is a module called Drupalgeddon which was designed to look for back doors.

The module creators say very honestly that this module is not perfect. It may miss some exploits and it may produce some false positives, but it may also help you uncover some suspicious files.

There are other modules that may help including Hacked and Site Audit.

You can find modified files based on date: Linux / Unix: Find Files Modified On Specific Date

Update your site as soon as possible.

Finally, install some module to secure your site:

如何用Drupal Commerce实现定金功能




Drupal Commerce并没有原生的定金功能。展开地毯式搜索,得到几个相关模块如下,但没有一个是成熟模块。

  • commerce_deposit 业务逻辑和我的需求有些差异,放弃。但有兴趣的朋友可以看一下代码,还是比较简单的。
  • Commerce Partial Payment  这个思路不错,也是最后我采用的办法。

与几位熟悉Drupal Commerce的朋友交流,现有的思路一般是通过添加price component或者custom line item来实现。我也尝试了第二种方法,具体的代码可以参考Commerce discount的模块。问题不大,如果作为折扣,这个line item作为负值没有什么问题,但作为定金,一个负值就看起来比较奇怪。容易让用户很困惑,并且这个方法最后改变了订单的总额。而从需求出发,订单总额是不应该改变的。所以这种方式最后也放弃了。


参考Commerce Partial Payment ,基本实现了我需要的功能。我假设新建一个模块叫my_deposit。





然后在checkout的付款页面,我们用 hook_form_BASE_FORM_ID_alter() 来修改commerce_checkout_form。基本思路如下:

首先调用上面的函数,看该用户是否需要付定金,如果不需要,则把payment methods 都删掉,这样就直接可以跳过payment步骤了。同时,在checkout form里加一个message给用户,说明定金数量或者你不需要支付定金。具体细节可看代码注释。

修改payment method表单的submit callback

下面是我们自己的callback,大部分是从commerce payment模块里的commerce_payment_pane_checkout_form_submit()里copy来的,47-56行是我们自己的修改。因为没有hook,所以目前只能这么改了。

其实很简单,就是把提交给payment method的金额修改为我们自己的定金金额。

这样就算基本完工了,测试一下你可以看到信用卡付款的金额已经变成了定金金额。订单完成后,查看order,可以看到付款记录,order balance是扣除定金余下的部分。



Paypal等offsite payment method的问题




because in the paypal sandbox(for example) you must specify an IPN handler URL. Paypal is using this URL to call the php file on your website that handles the IPN call. So you cannot pass into that url localhost/yourwebsite/ipn_handler.php. You will need something like your_computer_public_ip/yourwebsite/ipn_handler.php



How to add class on views table row (Drupal 7)

For a views using table, if you want to add a custom class on <tr>, you can go to this view’s setting page, find the ‘Foramt’ section, click the ‘table’s ‘setting’.

Screen Shot 2015-04-24 at 12.09.35 pm


Then find ‘Row class’ field. Just add your class here. But if you want a dynamic class, you can use field tokens. Screen Shot 2015-04-24 at 12.08.51 pm

You don’t know what is the field token. But don’t worry, you can find the field tokens in here, click the field in the ‘Fields’ section.

Screen Shot 2015-04-24 at 12.12.28 pm

But if the field is not there, you can add the filed and tick “exclude from display”. Ignore the error message please 😀

Screen Shot 2015-04-24 at 5.02.51 pm

Then find the ‘Rewrite results’:

Screen Shot 2015-04-24 at 12.22.23 pm

Save and done.




How to add block in module (Drupal 7)

Using hook_block_info(), here is example:!block_example.module/7

A simple code example here(copied from here):

But all above is missing a lot details, like module configuration. Here is the details of hook_block_info block array, copied from Durpal docs:


An associative array whose keys define the delta for each block and whose values contain the block descriptions. Each block description is itself an associative array, with the following key-value pairs:

  • info: (required) The human-readable administrative name of the block. This is used to identify the block on administration screens, and is not displayed to non-administrative users.
  • cache: (optional) A bitmask describing what kind of caching is appropriate for the block. Drupal provides the following bitmask constants for defining cache granularity:
  • properties: (optional) Array of additional metadata to add to the block. Common properties include:
    • administrative: Boolean that categorizes this block as usable in an administrative context. This might include blocks that help an administrator approve/deny comments, or view recently created user accounts.
  • weight: (optional) Initial value for the ordering weight of this block. Most modules do not provide an initial value, and any value provided can be modified by a user on the block configuration screen.
  • status: (optional) Initial value for block enabled status. (1 = enabled, 0 = disabled). Most modules do not provide an initial value, and any value provided can be modified by a user on the block configuration screen.
  • region: (optional) Initial value for theme region within which this block is set. Most modules do not provide an initial value, and any value provided can be modified by a user on the block configuration screen. Note: If you set a region that isn’t available in the currently enabled theme, the block will be disabled.
  • visibility: (optional) Initial value for the visibility flag, which tells how to interpret the ‘pages’ value. Possible values are:
    • BLOCK_VISIBILITY_NOTLISTED: Show on all pages except listed pages. ‘pages’ lists the paths where the block should not be shown.
    • BLOCK_VISIBILITY_LISTED: Show only on listed pages. ‘pages’ lists the paths where the block should be shown.
    • BLOCK_VISIBILITY_PHP: Use custom PHP code to determine visibility. ‘pages’ gives the PHP code to use.

    Most modules do not provide an initial value for ‘visibility’ or ‘pages’, and any value provided can be modified by a user on the block configuration screen.

  • pages: (optional) See ‘visibility’ above. A string that contains one or more page paths separated by “\n”, “\r”, or “\r\n” when ‘visibility’ is set to BLOCK_VISIBILITY_NOTLISTED or BLOCK_VISIBILITY_LISTED (example: “<front>\nnode/1”), or custom PHP code when ‘visibility’ is set to BLOCK_VISIBILITY_PHP. Paths may use ‘*’ as a wildcard (matching any number of characters); ‘<front>’ designates the site’s front page. For BLOCK_VISIBILITY_PHP, the PHP code’s return value should be TRUE if the block is to be made visible or FALSE if the block should not be visible.


Drush notes

Update Drupal core

How to update Drupal core with Drush

(Drush is a command line tool for Drupal).

1. Backup everything (all files, databases, etc)

2. Put your site in maintenance mode

Either from Drupal interface or with drush:
(commands for Drupal 7)
drush vset --always-set maintenance_mode 0
drush cache-clear all

3. Save your .htaccess-file, robots.txt and favicon.ico somewhere

…If you have modified them

4. Update Drupal with drush

drush up drupal

5. Check that everything works

Go around your site and test everyhting!

6. Put your site online again

Take your site away from maintenance mode either from Drupal interface or with drush (command for Drupal 7)
drush vset --always-set maintenance_mode 0
drush cache-clear all



Below copied from


Here I will try to maintain a drush cheat sheet with the list of drush commands I use most often. Full and short version of commands are used interchangeably.

See main information and status of the site

Clear the cache

Clear the cache, even if Drupal is broken

sql-query executes sql queries in the database where Drupal is installed.

Download the last recommended release of 2 modules

Download a -dev version of a module

Seems that adding --package-handler=git_drupalorg is not needed anymore.

Download the git HEAD version of a module

Short answer: drush does not do that.
Long answer: See

Enable a module

-y skips the confirmation question. Some drush commands may miss the -y part, a workaround for this that always works is putting it before:

If the module is missing and its name matches a project name, drush will automatically download the module from

Disable a module

Disable a module, even if Drupal is broken


Uninstall a module

See if a module is available

See if a module is enabled

See all contrib and custom modules that are enabled

Update a module

Update Drupal

Update all contrib modules and Drupal

Run update.php

Send an e-mail

Delete a field

Delete an instance of a field

Manually delete a field and all its data (not recommended)

Set a password for a user

Block a user

Log all users out

Log out a specific user

Get a one-time login link for the Administrator user

Run cron

Run Ultimate Cron

Set a variable

Get the value of a variable

See last logged events (watchdog)

See logged events in real time

Run a php script with Drupal bootstrapped

Use --uri to specify the host name. Use --root to specify the site directory (in cron, for example):

Run custom code

Repopulate database tables used by menu functions


Rescan all code in modules or includes directories, storing the location of each interface or class in the database

Useful when moving installed modules. See
If does not work because Drupal cannot bootstrap, do it that way:

Update a Feature with database changes

Revert a Feature, update the database to match the code

Revert all features

Useful in deployment scripts.

Change error level

Show only errors and warnings

Show all

Hide everything

Enable maintenance mode

Open a MySQL console logged in

Import a backup of the database

Export a backup of the database

Remove all database tables (empty the database)

Useful before importing a database backup.

See all drush aliases

Use an alias

Flush image styles

Install Drupal

Delete all content of specific content types

Note that you need Devel and Devel Generate modules. Alternatives are drush migrate-wipe andDelete all module.

Delete content type

Generate random content

Create a boilerplate for a new module

See Module Builder.


A more general and exhaustive list can be found here:
Full documentation – Contrib modules not included – is available at
And check, is awesome!



Build a store locator in Drupal 7

Create Store Nodes

Using Location module to create store nodes which can be content managed, also can be bulk imported by Feeds module.

Location now includes the following modules, I enabled some of them which are highlighted in green:

  • Location: provides the Location API; handles address storage and geocoding. Allows users to attach locations to nodes.
  • User Locations: allows admins to associate locations with users.
  • Location Phone and Location Fax: add phone and fax fields to Location’s address fieldset.
  • Location Search: Integrate locations and proximity searching into the core Drupal search functionality
  • Location CCK: provide a Location behavior as a CCK field.
  • Location Generate: Programmatically generate locations for testing (integrates with the Devel package).
  • Location Add Another: Allows you to quickly add new locations to a node.
  • Location Entity: #1186868: [Needs Tests]Integration with Entity API, Search API
  • Location Taxonomy:
  • Location Email:#1257716: Add Location: Email (alongside Phone and Fax)
  • Location Autofill: since 7.x-3.6-rc1. Allows you generate lat&lon data for all location without them.

After enabled them, you can create a content type for example “store” and add “location” field on it.

Learn more on Location module handbook.

If you have a lot address need added, like you have a csv file. You can use feeds and location feeds module to import them. If you want do some automatic job like alter the fields in csv before it imported, you can have a look at Feeds Tamper module.

Create Store Locator Search by Views

After you created stores, we need create a store locator page. This can be done by using Views Module. Check the following article, it’s for Drupal 6 but Drupal 7 is similar.

The most important thing is add filter “Location: Distance / Proximity” in the view.

If you still confused how to do it, please search Youtube, there are lot of tutorials about location module.

All done. Now you should can search stores by postcode. But there is one more thing I need is a map with marks of these result.

Map and Marks

This can be done by using “Gmap” module. But it has some limitation. You can search on Youtube to find how to using Gmap. But it only give a map result which has no store list. Here is a solution to solve this but I don’t like it: GMap views – listing clickable nodes with a GMap

So I gave up using Gmap. Just put my custom code based on location views we created.

We’ve already have the store search result. Directly use Google map API is easy.

Create map:
Add marks:

Here is my sample JS code:

Simply add above JS into the Views template, then we will get a result list and marked them on map.

You may need more JS like move to map mark by click the store result list.But I would like not to discuss this in here.

Have fun!




Payment模块:一个简单的Drupal Commerce替代方案

最近在OSTraining我们做了针对Drupal Commerce的全面的攻略。你可以观看30节课程的视频教程和下载“Building E-commerce Sites with Drupal Commerce”电子书。

然而,Drupal Commerce是一个企业级的解决方案,但是大多数OSTraining的学员们可能需要的只是一个简单的电子商务方案。


Payment模块支持差不多六个支付网关(PayPal, Stripe, iDEAL,, Ogone, Rabo OmniKassa)。

在这个教程里,我们将展示如何使用Payment模块,并且启用Paypal支付。 Continue reading Payment模块:一个简单的Drupal Commerce替代方案

How to check which CMS a site is using

There are many way you can check a site to see which CMS it is using if you are familiar with those CMS like Drupal or WordPress.

But if you don’t, it still can be very easy to get it. Here is a fancy site will help you to check the CMS. It will tell you even about the version, plugin and theme information.

WordPress check

Drupal check

Joomla check

Drupal taxonomy hide empty terms

Hide empty terms from menu

Go to the admin/structure/taxonomy

Find the vocabulary, chose ‘edit vocabulary’.

Then in the ‘Taxonomy menu’ section, expand ‘Options’. You will see a ‘Hide empty terms’ checkbox.

Tick it.


Hide empty terms in a taxonomy view

For hide empty terms in a taxonomy view, here is a solution:

The goal is to create a views that will list all terms of a certain vocabulary. I have tried a lot but actually its quite easy.

You have th create a new view for taxonomy terms – see screenshot:

The view will show all Terms to prevent empty terms of showing up you have to create a relationship. Click add relation ship, filter for terms and choose “Taxonomy term: Representative node“. Check the “require this relationship” and thats it.

Above Screenshot “Add Relationship”