---
title: "Creating Custom Module in Drupal"  
description: "A module is a collection of functions that link into Drupal, providing additional functionality for your Drupal installation or Drupal Website."  
author: "Anonymous User"  
published: 2012-04-09  
updated: 2019-09-07  
canonical: https://www.mindstick.com/articles/911/creating-custom-module-in-drupal  
category: "drupal"  
tags: ["drupal"]  
reading_time: 9 minutes  

---

# Creating Custom Module in Drupal

A module is a collection of functions that link into Drupal, providing additional [functionality](https://www.mindstick.com/blog/136/using-watermark-functionality-in-textbox-by-jquery) for your Drupal [installation](https://www.mindstick.com/articles/711/php-installation-on-windows) or Drupal Website.\

Generally module has three types Core, Contributed and Custom. Let’s take a look on these components.

1. **Core:** It is a module that ship with Drupal and are approved by core developers and community.

2. **Contributed:** It is a module that written by the Drupal community and shared under the same GNU public license as drupal.

3. **Custom:** It is module that written or created by the developer of website.

To create a custom module in Drupal [content management system](https://www.mindstick.com/blog/12586/list-of-the-five-best-content-management-systems-in-2019), first of all you’ve to understand about module anatomy.

##### Anatomy of a Module (Custom module):

There are the [two files](https://www.mindstick.com/forum/161621/how-can-you-compare-two-files-byte-by-byte-to-check-if-they-are-identical) which play an important role in building custom module; required file and optional file.

##### Required File:

.info file: This is the information file which describes about the module such module name, module version etc.

.module file: This file contains php coding and defines what functionality will be increase of drupal after installing in drupal site.

##### Optional File:

.install file: This file defines the install/uninstall scripts of custom module.

.css file: This is [cascading style](https://www.mindstick.com/forum/490/cascading-style-sheets) sheet (CSS) file which manages look and feel of drupal website.

.inc file: This file defines the additional included file which widely used in our product.

Now after the anatomy of a Module; here [one question](https://answers.mindstick.com/qa/36489/what-one-question-tells-you-the-most-about-someone) arises, where do module files reside? The answer of this question is; for core module it will be **/module/** subdirectory and for contrib and custom module **/sites/all/modules.**

Here I’m creating a module to count the downloaded software from [your website](https://yourviews.mindstick.com/view/81591/semrush-tutorial-and-review-2020-how-to-use-it-to-20x-your-website-traffic). Let’s have a look a brief idea about it.

##### .info File Code:

```
name = Download Countdescription = Tracks file downloads for Drupal private core file fields.core = 7.xdependencies[] = fielddependencies[] = file ; Information added by mindstick.com packaging script on 2012-04-07version = "7.x-3.x-dev"core = "7.x"project = "Mod_Download_Count"datestamp = "1331640715"
```

\

.module File Code:

```
<?php /** * @file * Tracks file downloads for files stored in the drupal files table using the private files setting or custom private filefield. */ // Load all field module hooks.module_load_include('inc', 'download_count', 'includes/download_count.field'); /** * Implements hook_help(). */function download_count_help($path, $arg) {  switch ($path) {    case 'admin/help#download_count':      return '<p>' . t('Counts file downloads for private core file fields and logs a message to the watchdog table.') . '</p>';  }} /** * Implements hook_permission(). */function download_count_permission() {  $perms = array(    'view download counts' => array(      'title' => t('view download counts'),    ),    'skip download counts' => array(      'title' => t('skip download counts'),      'description' => t('Don\'t count downloads for users with this role.'),    ),    'reset download counts' => array(      'title' => t('reset download counts'),    ),    'export download counts' => array(      'title' => t('export download counts'),    ),  );  return $perms;} /** * Implements hook_menu(). */function download_count_menu() {  $items = array();   $items['admin/config/media/download-count'] = array(    'title' => 'Download count',    'description' => 'Tracks file downloads for files stored in private core file fields.',    'page callback' => 'drupal_get_form',    'page arguments' => array('download_count_admin_settings'),    'access arguments' => array('administer site configuration'),    'file' => 'includes/download_count.settings.inc',  );  $items['admin/config/media/download-count/clear'] = array(    'title' => 'Clear Cache',    'page callback' => 'drupal_get_form',    'page arguments' => array('download_count_clear_confirm'),    'access arguments' => array('administer site configuration'),    'type' => MENU_CALLBACK,    'file' => 'includes/download_count.settings.inc',  );  $items['admin/config/media/download-count/settings'] = array(    'title' => 'Settings',    'weight' => 1,    'type' => MENU_DEFAULT_LOCAL_TASK,  );  $items['admin/reports/download-count'] = array(    'title' => 'Download Counts',    'description' => 'Download history of files attached to private core file fields.',    'page callback' => 'download_count_view_page',    'page arguments' => array('download_count'),    'access arguments' => array('view download counts'),    'type' => MENU_NORMAL_ITEM,    'file' => 'includes/download_count.pages.inc',  );  $items['admin/reports/download-count/%download_count_entry/details'] = array(    'title' => 'Download Count Details',    'page callback' => 'download_count_view_details',    'page arguments' => array(3),    'access arguments' => array('view download counts'),    'type' => MENU_CALLBACK,    'file' => 'includes/download_count.pages.inc',  );  $items['admin/reports/download-count/%download_count_entry/reset'] = array(    'title' => 'Download Count Reset',    'page callback' => 'drupal_get_form',    'page arguments' => array('download_count_reset_form', 3),    'access arguments' => array('reset download counts'),    'type' => MENU_CALLBACK,    'file' => 'includes/download_count.pages.inc',  );  $items['admin/reports/download-count/%download_count_entry/export'] = array(    'title' => 'Download Count Export CSV',    'page callback' => 'drupal_get_form',    'page arguments' => array('download_count_export_form', 3),    'access arguments' => array('export download counts'),    'type' => MENU_CALLBACK,    'file' => 'includes/download_count.export.inc',  );   return $items;} /** * Implements hook_views_api(). */function download_count_views_api() {  return array(    'api' => 3.0,    'path' => drupal_get_path('module', 'download_count') . '/includes',  );} /** * Menu wildcard loader. */function download_count_entry_load($dcid) {  return $dcid == 'all' ? $dcid : db_query('SELECT dc.dcid, dc.fid, dc.uid, dc.type, dc.id, dc.ip_address, dc.referrer, dc.timestamp, f.filename, f.uri, f.filemime, f.filesize FROM {download_count} dc JOIN {file_managed} f ON dc.fid = f.fid WHERE dcid = :dcid', array(':dcid' => $dcid))->fetchObject();} /** * Implements hook_library(). */function download_count_library() {  if (module_exists('libraries')) {    $path = libraries_get_path('jquery.sparkline', FALSE) . '/jquery.sparkline.min.js';  }  else {    $path = drupal_get_path('module', 'download_count') . '/jquery.sparkline.min.js';  }   $libraries['jquery.sparkline'] = array(    'title' => 'jquery sparkline',    'website' => 'http://www.omnipotent.net/jquery.sparkline/',    'version' => '1.6',    'js' => array(      $path => array(),    ),  );  return $libraries;} /** * Implements hook_file_download_access_alter(). */function download_count_file_download_access_alter(&$grants, $file, $entity_type, $entity) {  $time =  REQUEST_TIME;   //if role should be skipped, return.  if (user_access('skip download counts')) {    return;  }   //if no access, simply return.  if (!in_array(TRUE, $grants)) {    return;  }   //check flood control  $flood_limit = variable_get('download_count_flood_limit', 0);  if ($flood_limit > 0) {    $flood_window = variable_get('download_count_flood_window', 5);    if (!flood_is_allowed('download_count-fid_' . $file['fid'], $flood_limit, $flood_window)) {      return;    }  }   //validate file has extension that should be counted, if not return.  $extensions = explode(' ', drupal_strtolower(trim(variable_get('download_count_excluded_file_extensions', 'jpg jpeg gif png'))));  if (count($extensions)) {    $extension = drupal_strtolower(pathinfo($file['filename'], PATHINFO_EXTENSION));    if (in_array($extension, $extensions)) {      return;    }  }   // Count the download.  global $user;  $entity_info = entity_get_info($entity_type);  $ip = ip_address();  $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : NULL;   $id = db_insert('download_count')  ->fields(array(    'fid' => $file['fid'],    'uid' => $user->uid,    'type' => $entity_type,    'id' => $entity->$entity_info['entity keys']['id'],    'ip_address' => $ip,    'referrer' => $referrer,    'timestamp' => $time,  ))  ->execute();  flood_register_event('download_count-fid_' . $file['fid'], 3600);  watchdog('download_count', '%file was downloaded by user #%uid from %ip', array('%file' => $file['filename'], '%uid' => $user->uid, '%ip' => $ip), WATCHDOG_NOTICE);   if (module_exists('rules')) {    rules_invoke_event('download_count_file_download', (object) $file, $user);  }} /** * Implements hook_cron_queue_info(). */function download_count_cron_queue_info() {  $queues['download_count'] = array(    'worker callback' => 'download_count_cache_processor',    'time' => 60,  );  return $queues;} /** * Implements hook_cron(). * * Daily file download counts are cached for convenience. */function download_count_cron() {  $time = REQUEST_TIME;  $status = 0;  $count = 0;  $last_cron = variable_get('download_count_last_cron', 0);   $result = db_query('SELECT fid, type, id, UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(timestamp))) as date, COUNT(dcid) as count FROM {download_count} WHERE timestamp > :last_cron GROUP BY type, id, fid, DATE(FROM_UNIXTIME(timestamp))', array(':last_cron' => $last_cron));  $queue = DrupalQueue::get('download_count');  foreach ($result as $record) {    $queue->createItem($record);    $count++;  }  if ($count > 0) {    watchdog('download_count', 'Download count queued %count new entries for caching.',  array('%count' => $count), WATCHDOG_NOTICE);  }  variable_set('download_count_last_cron', $time);} /** * Adds download count data to the daily cache table. * * @param $record *   An object containing the download to cache. */function download_count_cache_processor($record) {  db_merge('download_count_cache')    ->key(array('type' => $record->type, 'id' => $record->id, 'fid' => $record->fid, 'date' => $record->date))    ->fields(array(        'count' => $record->count,      ))    ->expression('count', 'count + :inc', array(':inc' => $record->count))    ->execute();} /** * Implements hook_block_info(). */function download_count_block_info() {  $blocks['top_files']['info'] = t('Top Downloaded Files');  $blocks['recent_files']['info'] = t('Recently Downloaded Files');  return $blocks;} /** * Implements hook_block_configure(). */function download_count_block_configure($delta) {  $form['download_count_' . $delta . '_block_limit'] = array(    '#type' => 'textfield',    '#title' => t('Number of items to display'),    '#size' => 5,    '#default_value' => variable_get('download_count_' . $delta . '_block_limit', 10),  );  return $form;} /** * Implements hook_block_save(). */function download_count_block_save($delta, $edit) {  variable_set('download_count_' . $delta . '_block_limit', $edit['download_count_' . $delta . '_block_limit']);} /** * Implements hook_block_view(). */function download_count_block_view($delta) {  switch ($delta) {    case 'top_files':      $blocks['subject'] = t('Top Downloaded Files');      $blocks['content'] = _download_count_block_contents('top_files');      break;     case 'recent_files':      $blocks['subject'] = t('Recently Downloaded Files');      $blocks['content'] = _download_count_block_contents('recent_files');      break;  }   return $blocks;} /** * Generate block contents based on delta. */function _download_count_block_contents($block) {  $limit = (int) variable_get('download_count_' . $block . '_block_limit', 10);  $rows = array();  if ($block == 'top_files') {    $sql = 'SELECT dcc.fid, dcc.count, f.filename, f.filesize FROM {download_count_cache} dcc JOIN {file_managed} f ON dcc.fid = f.fid ORDER BY dcc.count DESC';  }  else {    $sql = 'SELECT dc.fid, MAX(dc.timestamp) as date, f.filename, f.filesize FROM {download_count} dc JOIN {file_managed} f ON dc.fid = f.fid GROUP BY dc.fid ORDER BY date DESC';  }   $header[] = array(    'data' => t('Name'),    'class' => 'filename',  );  $header[] = array(    'data' => t('Size'),    'class' => 'size',  );  $header[] = array(    'data' => $block == 'top_files' ? t('Count') : t('Last Downloaded'),    'class' => $block == 'top_files' ? 'count' : 'last',  );   $result = db_query_range($sql, 0, $limit);  foreach ($result as $file) {    $row = array();    $row[] = check_plain($file->filename);    $row[] = format_size($file->filesize);    $row[] = $block == 'top_files' ? $file->count : t('%time ago', array('%time' => format_interval(REQUEST_TIME - $file->date)));    $rows[] = $row;  }   if (count($rows)) {    return theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => FALSE));  }} /** * Implements hook_theme(). */function download_count_theme() {  $theme= array(    'download_count_file_field_formatter' => array(    'variables' => array('file' => NULL),    'file' => 'includes/download_count.field.inc',    ),  );  return $theme;}
```

\

##### .install File Code:

```
<?php /** * @file * Installation code for the download_count module. */ /** * Implements hook_schema(). */function download_count_schema() {  $schema['download_count'] = array(    'fields' => array(      'dcid' => array(        'description' => 'Primary Key: Unique download count id.',        'type' => 'serial',        'unsigned' => TRUE,        'not null' => TRUE,      ),      'fid' => array(        'description' => 'The id from the drupal file_managed table of the file downloaded.',        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,      ),      'uid' => array(        'description' => 'The uid of the user that downloaded the file.',        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,      ),      'type' => array(        'description' => 'The name of the entity type to which the file was attached when downloaded.',        'type' => 'varchar',        'length' => 64,        'not null' => TRUE,        'default' => '',      ),      'id' => array(        'description' => 'The primary key of the entity to which the file was attached when downloaded.',        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),      'ip_address' => array(        'description' => "The ip address of the downloading user.",        'type' => 'varchar',        'length' => 128,        'not null' => TRUE,        'default' => 0,      ),      'referrer' => array(        'description' => 'Referrer URI.',        'type' => 'varchar',        'length' => 255,        'not null' => TRUE,        'default' => '',      ),      'timestamp' => array(        'description' => 'The date-time the file was downloaded.',        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),    ),    'primary key' => array('dcid'),    'indexes' => array(      'dc_fid' => array('fid'),      'dc_uid' => array('uid'),      'dc_type' => array('type'),      'dc_id' => array('id'),      'dc_ip' => array('ip_address'),      'dc_timestamp' => array('timestamp'),      'dc_fid_type_id' => array('fid', 'type', 'id'),    ),  );   $schema['download_count_cache'] = array(    'fields' => array(      'dcc_id' => array(        'description' => 'Primary Key: Unique download count cache id.',        'type' => 'serial',        'unsigned' => TRUE,        'not null' => TRUE,      ),      'fid' => array(        'description' => t('The id from the drupal files table of the file downloaded.'),        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),      'type' => array(        'description' => 'The name of the entity type to which the file was attached when downloaded.',        'type' => 'varchar',        'length' => 64,        'not null' => TRUE,        'default' => '',      ),      'id' => array(        'description' => 'The primary key of the entity to which the file was attached when downloaded.',        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),      'date' => array(        'description' => t('The date the file was downloaded.'),        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),      'count' => array(        'description' => t('Number of times a file was downloaded in one day.'),        'type' => 'int',        'unsigned' => TRUE,        'not null' => TRUE,        'default' => 0,      ),    ),    'primary key' => array('dcc_id'),    'indexes' => array(      'dcc_fid' => array('fid'),      'dcc_type' => array('type'),      'dcc_id' => array('id'),      'dcc_timestamp' => array('date'),      'dcc_fid_type_id' => array('fid', 'type', 'id', 'date'),    ),  );   return $schema;} /** * Implements hook_uninstall(). */function download_count_uninstall() {  variable_del('download_count_excluded_file_extensions');  variable_del('download_count_view_page_limit');  variable_del('download_count_view_page_items');  variable_del('download_count_view_page_title');  variable_del('download_count_view_page_header');  variable_del('download_count_view_page_footer');  variable_del('download_count_export_range');  variable_del('download_count_sparklines');  variable_del('download_count_sparkline_min');  variable_del('download_count_sparkline_height');  variable_del('download_count_sparkline_width');  variable_del('download_count_last_cron');  variable_del('download_count_details_daily_limit');  variable_del('download_count_details_weekly_limit');  variable_del('download_count_details_monthly_limit');  variable_del('download_count_details_yearly_limit');  variable_del('download_count_flood_limit');  variable_del('download_count_flood_window');  drupal_set_message(t('The download_count module has been uninstalled.'));}
```

\

\

##### .css File Code:

```
#download-count-table a {  display: inline-block;  line-height: .9em;}#download-count-table a + a {  border-left: 1px solid;  margin-left: 0.5em;  padding-left: 0.5em;}table.download-count-table th {  font-weight: bold}table.download-count-table caption {  font-weight: bold;  font-size: 1.3em;  padding: 2em 0 1em 0;}table.download-count-details {  width: 50%;}table.download-count-details th.number {  width: 10%;}table.download-count-details th.range {  width: 45%;}table.download-count-details th.count {  width: 45%;}table.download-count-details td,table.download-count-details th {  text-align: center;}#download-count-total-bottom {  padding-top: 2em;}
```

\

\

Now put all these file in **‘Mod_Download_Count’** directory and create zip folder of this directory.

![Creating Custom Module in Drupal](https://www.mindstick.com/mindstickarticle/c9dd9b64-641c-48c4-9657-52fa10f5ad25/images/525ce3ca-1aad-4488-9762-a4afade49728.png)

Now login in **Drupal Administrator Panel** and go to **Modules** option.

![Creating Custom Module in Drupal](https://www.mindstick.com/mindstickarticle/c9dd9b64-641c-48c4-9657-52fa10f5ad25/images/41026c96-7261-4d5a-91a6-2cac1063daac.png)

Now click on **‘Install new module’** link and select appropriate module file either from local computer or from web.

![Creating Custom Module in Drupal](https://www.mindstick.com/mindstickarticle/c9dd9b64-641c-48c4-9657-52fa10f5ad25/images/57e3325e-1bd3-4552-9792-1b395b0f2e67.png)

After selecting appropriate file directory click on ‘install’ button.

![Creating Custom Module in Drupal](https://www.mindstick.com/mindstickarticle/c9dd9b64-641c-48c4-9657-52fa10f5ad25/images/acac1da0-0212-4e1f-861e-3e45439d67a1.png)

Now to see your module functionality clicks on **‘Enable newly added module’** link. After selecting this link you will see following window.

![Creating Custom Module in Drupal](https://www.mindstick.com/mindstickarticle/c9dd9b64-641c-48c4-9657-52fa10f5ad25/images/66d14209-2190-4425-8493-80b809997bdf.png)

Now select the Enable checkbox option and click on **‘Save configuration’** option.

This is the complete description about Drupal Custom Module. I hope you will enjoy it.

\

---

Original Source: https://www.mindstick.com/articles/911/creating-custom-module-in-drupal

Copyright © MindStick Software Pvt. Ltd. This Markdown version is provided for developers, AI systems, and offline reading.
