I have written a python script to autogenerate a set of files for module develpment for Drupal 7. This will create the, .info, .module, .install files and setup css, js, and includes subdirectories. Also a couple other files are created as well and entity functions if you are including entities in your module. This helps start a new module quickly by just typing a command. I use linux so I am not sure about this working in windows.

#!/usr/bin/python
# @Author = Morgan Massens
# @date = 2/17/2012
# @version = 1.0
#
# Creates a new Drupal 7 Module template using the following command:
# make_module.py <module_name> --path <path> <options>
# Options:
#  --description: Description of module
#  --path: Path where the module template will be created
#  --full-name: Full name of the module
#  --dependencies: List of dependencies
#  --controller: Should we implement a controller?
#  --package: The package this module belongs to
#  --entities: Comma separated list of entites, encapsulate in quotes, no spaces

import sys,os
import re
import urllib
from optparse import OptionParser
import datetime
import logging

module_name = ""
full_name = ""

##
#
# @param name - name of the function (string)
# @param desc - doxygen description of the function (string)
# @param params list of tuples of params as param (list of tuples)
# - param[0] = name of parameter
# - param[1] = Description of parameter
# - param[2] = default Value
# --EXAMPLE [("param1","Description","Default Value"),("param2","Desc","Default")]
# @param returns - the doxygen return parameter (string)
# @param details - the extra doxygen info (list)
# @code = the code list of lines of code to be the function body (list)
#
##
def writeFunction(name, desc, params=None, returns=None, details=None, code=None):
  global module_name
  global full_name

  # Set function name with module prefix
  name = module_name+"_"+name

  if params:
    if params == None:
      params = []
  else:
    params = []

  if details:
    if details == None:
      details == []
  else:
    details = []

  if code:
    if code == None:
      code = []
  else:
    code = []

  # Document Parameters
  output ="/**\n"
  output+=" * "+desc+"\n"
  output+=" * \n"
  param_count = 0
  for param in params:
    param_count+=1
    output+=" * @param "+param[0]+" " + param[2] + "\n"
  if param_count > 0:
    output+=" *\n"
  for detail in details:
    output+=" * "+detail+"\n"
  if returns:
    output+=" *\n * @returns "+returns+"\n"
  output+=" */\n"

  output+="function "+name+"("

  comma = False
  param_output = ""
  for param in params:
    if param_output <> "":
      param_output+=", "
    param_output+=param[0]
    if param[2] <> "":
      param_output+="="+param[2]

  output+=param_output+")\n"
  output+="{\n"
  for line in code:
    output+="  "+line+"\n"
  output+="\n}\n\n"

  return output

##
# Create the header of a php file for drupal.
#
# @param details the Doxygen description of the file.
#
# @returns string
##
def fileHeader(details):
  output = "<?php\n// $Id$\n"
  output+= "/**\n"
  output+= " * @file\n"
  output+= " * "+details+"\n"
  output+= " * \n"
  output+= " */\n\n"
  return output

##
# Opens the path as a file and writes the data as the file.
##
def createFile(path, data):
  f = open(path, 'w')
  f.write(data)
  f.close()

def main():
  global module_name
  global full_name
  # Main function here
  #
  # Validate that the required arguments are set
  usage = "usage: %prog module-name [options]"
  parser = OptionParser(usage=usage)

  parser.add_option('--description', help='Description of module')
  parser.add_option('--path', help='Path where the module template will be created')
  parser.add_option('--full-name', help='Full name of the module')
  parser.add_option('--dependencies', help='List of dependencies')
  parser.add_option('--controller', help='Should we implement a controller?')
  parser.add_option('--package', help='The package this module belongs to')
  parser.add_option('--entities', help='Comma separated list of entites, encapsulate in quotes, no spaces')

  # Set command line options and args from the parser
  (options, args) = parser.parse_args()

  # Set required command line options
  required_options = ['path']
  for option in required_options:
    if not options.__dict__[option]:
      print 'missing required option', option
      print
      parser.print_help()
      exit(1)
  if not args:
    print 'missing module-name'
    print
    parser.print_help()
    exit(1)

  # Get module_name  and other values from command line args
  module_name = args[0]
  full_name = options.full_name if options.full_name else module_name
  description = options.description if options.description else ""

  #TODO Add Logging
  #if options.log:

  #Create files
  install_dir = options.path+"/"
  include_dir = install_dir+"includes/"

  install_file = install_dir+module_name+".install"
  info_file = install_dir+module_name+".info"
  module_file = install_dir+module_name+".module"
  include_file = include_dir+module_name+".inc"
  form_file = include_dir+module_name+".form.inc"
  #TODO Create a separate controller file for each controller
  controller_file = include_dir+module_name+".controller.inc"
  js_file  = install_dir+"js/"+module_name+".js"
  js_form_file = install_dir+"js/"+module_name+".form.js"
  css_file = install_dir+"css/"+module_name+".css"
  css_form_file = install_dir+"css/"+module_name+".form.css"

  # Create Directories if theyt do not exist
  if not os.path.exists(install_file):
    #TODO Add error handling here
    os.makedirs(install_dir)
    os.makedirs(include_dir)
    os.makedirs(install_dir+'css/')
    os.makedirs(install_dir+'js/')

  # Create Info File
  php_code = ";Id$\nname = \""+full_name+"\"\n"
  php_code+= "description = \""+description+"\"\n"
  php_code+= "core = 7.x\n"
  php_code+= "php = 5.1\n"
  if options.package:
    php_code+= "package = "+options.package+"\n"
  php_code+="\n"
  php_code+="files[] = "+module_name+".module\n"
  php_code+="files[] = "+module_name+".install\n"
  php_code+="files[] = includes/"+module_name+".inc\n"
  php_code+="files[] = includes/"+module_name+".form.inc\n"
  if options.controller:
    php_code+="files[] = includes/"+module_name+".controller.inc\n"
  createFile(info_file, php_code)


  # Create Module File
  # # Menu Hook
  php_code = fileHeader(module_name+" Module.")
  code =["$items = array();","$items[] = array();","","return $items;"]
  php_code+= writeFunction("menu", "Implements hook_menu().", code=code)
  # # Create entity hook if entities exist
  code = []
  if options.entities:
    code.append("$return = array();")
    for entity in options.entities.split(','):
      code.append("$return['"+entity+"'] = array(")
      code.append("  'label' => t(''),")
      code.append("  'entity class' => t(''),")
      code.append("  'controller class' => t(''),")
      code.append("  'base table' => t(''),")
      code.append("  'revision table' => t(''),")
      code.append("  'uri callback' => t(''),")
      code.append("  'label callback' => t(''),")
      code.append("  'fieldable' => t(''),")
      code.append("  'entity keys' => array(")
      code.append("    'id' => '',")
      code.append("    'revision' => '',")
      code.append("    'label' => '',")
      code.append("  ),")
      code.append("  'static cache' => TRUE,")
      code.append("  'view modes' => array(")
      code.append("    'full' => array(")
      code.append("      'label' => t('Full'),")
      code.append("      'custom setting' => TRUE,")
      code.append("    ),")
      code.append("    'teaser' => array(")
      code.append("      'label' => t(''),")
      code.append("      'custom setting' => FALSE,")
      code.append("    ),")
      code.append("  ),")
      code.append(");")

    code.append('')
    code.append('return $return;')

    php_code+= writeFunction("entity_info", "Implements hook_entity_info().",code=code)
  createFile(module_file, php_code)

  # Create Install File
  php_code = fileHeader(module_name+" Install File.")
  # # Install Hook
  code =["db_query(\"Update {system} SET weight = 104 WHERE name = '"+module_name+"'\");"]
  php_code+= writeFunction(
          "install",
          "Implements hook_install().",
          code = code)
  # # Schema Hook
  code =["$schema = array();","","return $schema"]
  php_code+= writeFunction(
          "schema",
          "Implements hook_schema().",
          code = code
      )
  createFile(install_file, php_code)

  # Create Include File
  php_code = fileHeader(module_name+" Includes Function.")
  createFile(include_file, php_code)

  if options.entities:
    options.controller = True

  # Create the controller file if controller was set
  if options.controller:
    #@TODO Add an options.entities property of comma separated entites.  Create controler for each.
    #@TODO <module_name>.<entity_name>.controller.inc
    if options.entities:
      entities = options.entities.split(',')
    else:
      entities = [module_name]
    # Create Controller File
    php_code = ""
    file_head = ""

    for entity in entities:
      file_head+="\n * - "+entity+ " Controller"
      entity_name = entity if entity <> module_name else ""
      entity_fnc_name = entity+"_" if entity <> module_name else ""
      mod_ent_fnc_name = module_name +"_"+ entity_fnc_name if entity_fnc_name <> "" else module_name+"_"
      entity_title = entity.title() if entity == module_name else module_name.title() + " " + entity_name.title()

      php_code+= "// *****************************************************************************\n"
      php_code+= "// BEGIN Entity "+entity.title()+"\n"
      php_code+= "// *****************************************************************************\n"
      # # Create Function
      code = []
      code.append("$entity = entity_get_controller('"+entity+"')->create();")
      code.append("")
      code.append("return $entity;")
      php_code+= writeFunction(entity_fnc_name+"create","Creates a new "+entity+" object.", code = code)
      # # Load Function
      code = []
      code.append("$ids = isset($id) ? array($id) : array();")
      code.append("$conditions = (isset($vid) ? array('vid' => $vid) : array());")
      code.append("$entity = "+mod_ent_fnc_name+"load_multiple($ids, $conditions, $reset);")
      code.append("return $entity ? reset($entity) : FALSE;")
      php_code+= writeFunction(
            entity_fnc_name+"load",
            "Loads a new "+entity_title+" object.",
            params = [('$id','integer','NULL'),('$vid','integer','NULL'),('$reset', '', 'FALSE')],
            returns = "\"object\"",
            code = code)

      # # Load Multiple Function
      code = []
      code.append("$entities = entity_load('"+entity+"', $ids, $conditions, $reset);")
      code.append("return $entities;")
      php_code+= writeFunction(
            entity_fnc_name+"load_multiple",
            "Loads multiple "+entity+" objects.",
            params = [('$ids','array of integers','NULL'),
                      ('$conditions','array of key=>value pairs','array()'),
                      ('$reset','boolean','FALSE')],
            returns = "array of objects",
            code = code)

      # # Save function
      code = []
      code.append("$save = entity_get_controller('"+entity+"')->save($"+entity+", $transaction);")
      code.append("return $save;")
      php_code+= writeFunction(
            entity_fnc_name+"save",
            "Save a "+entity_name+" object.",
            params = [("&$"+entity_name,entity_title+" Entity Object",""),("$transaction","","NULL")],
            code = code)

      # # Delete function
      code = []
      code.append("return "+mod_ent_fnc_name+"delete_multiple(array($id), $transaction);")
      php_code+= writeFunction(
            entity_fnc_name+"delete",
            "Delete a "+entity_name+" object.",
            params = [("$id",entity_title+" Entity ID",""),("$transaction","","NULL")],
            code = code)
      # # Delete Multiple Function
      code = []
      code.append("$transaction = (!is_null($transaction))? $transaction : db_transaction();")
      code.append("")
      code.append("return entity_get_controller('"+entity+"')->delete($ids, $transaction)")
      php_code+= writeFunction(
            entity_fnc_name+"delete_multiple",
            " Delete Multiple "+entity+" objects.",
            params = [("$ids",entity_title+" Entity IDS",""),("$transaction","","NULL")],
            code = code)

    php_code = fileHeader(module_name+" Controller Function."+file_head) + php_code
    createFile(controller_file, php_code)

  # Create Form File
  # Set the file header
  php_code = fileHeader(module_name+" Form Functions.")
  #TODO Create form file functions here (per entity?)
  # code = []
  # code.append()
  # php_code+=writeFunction(
  #        entity_fnc_name+"form",
  #        " Edit the entity",
  #        .....
  # )
  createFile(form_file, php_code)


  # Create JS file
  createFile(js_file, "")
  createFile(js_form_file, "")

  # Create CSS files
  createFile(css_file, "")
  createFile(css_form_file, "")
main()

Comments

modimanz’s picture

You can safely remove the urllib, re, datetime and logging libraries from this python script, I was going to get really fancy with this script, but for now this will do.

john_b’s picture

Thanks! Should save a lot of time.

Digit Professionals specialising in Drupal, WordPress & CiviCRM support for publishers in non-profit and related sectors

vdupom’s picture

Using this script mi module.install file was created without a semicolon in the hook_schema funcion.

I believe that line 260 should be:
code =["$schema = array();","","return $schema;"]

Thanks for this useful script.

---Luca

wranvaud’s picture

Would be nice if the template files included more sample code, eventually in a --sample TRUE option. I may do that and post here.

john_b’s picture

How is that different from Examples module?

Digit Professionals specialising in Drupal, WordPress & CiviCRM support for publishers in non-profit and related sectors