The Node, Views and Panels modules can (and should!) handle most pages you need on a site, but sometimes you need complete control of a “hard coded” content area. You could create a new Page node with a Full HTML text format. Instead, how about putting the whole thing in code? Your work will be in version control and much more straightforward to maintain. Imagine editing HTML in a text editor rather than in the Drupal admin! So much better. Additionally, this method allows you to work more easily in a git-based workflow, such as those used by Pantheon or Acquia, without worrying about database changes.

Warnings for the less experienced Drupal site builder: This is specifically written to enforce the theme and logic separation functionality Drupal provides; no logic in the tpl files! If you can make a page in Views, it should be in Views. If you can make it in Panels, it should be in Panels. Do not use this method to recreate the wheel. If you find yourself writing large amounts of SQL queries in the page callback, then you are probably doing something wrong. Do it the “Drupal Way.” Your life will be less stressful in the long run. /rant

Ok! Let’s get to the code. First thing, make an info file for new module. Be sure to change the “variables” below to match your site and the page/app name. sitename_pagename.info:

name = Site Name - Page Name
description = Creates the Page Name for the Site Name
core = 7.x
package = Site Name
version = 7.x-1.0

Then make the module with hook_menu(), a page callback, hook_theme(), and a preprocess function. sitename_pagename.module:

<?php
/**
 * Implements hook_menu()
 */
function sitename_pagename_menu() {
  $items['page-path'] = array(
    'title' => 'Page Name',
    'type' => MENU_NORMAL_ITEM,
    'page callback' => 'sitename_pagename_page_main',
    'access arguments' => array('access content'),
  );
  return $items;
}

/**
 * Page callback sets up and returns the content for the Page Name page
 */
function sitename_pagename_page_main() {
  $module_path = drupal_get_path('module', 'sitename_pagename');
  // Add any required jQuery libraries JavaScript. DELETE IF UNNECESSARY!
  drupal_add_js($module_path . '/library.min.js');
  // Add the module path to Drupal.extend. DELETE IF UNNECESSARY!
  $data = array(
    'sitename_pagename' => array(
      'path' => $module_path,
    ),
  );
  drupal_add_js($data, 'setting');
  // Add the custom JavaScript. DELETE IF UNNECESSARY!
  drupal_add_js($module_path. '/sitename_pagename.js');
  // Add the page CSS. DELETE IF UNNECESSARY!
  drupal_add_css($module_path . '/sitename_pagename.css');

  // Render the theme function for this page
  // NOTE: Pass any required variables to theme function using array, see Drupal API.
  return theme('sitename_pagename_main', array('hello' => t('Hello')));
}

/**
 * Implements hook_theme.
 */
function sitename_pagename_theme() {
  return array(
    'sitename_pagename_main' => array(
      'variables' => array(
        // Specifies both available variables.
        'hello' => NULL,
        'world' => NULL,
      ),
      // This allows you to create a tpl file named sitename_pagename_main.tpl.php.
      // It will be automatically loaded by Drupal.
      'template' => 'sitename_pagename_main',
    ),
  );
}

/**
 * Implements hook_preprocess_THEME().
 *
 * This Example preprocesses some variables to pass to the theme function/file.
 * DELETE IF UNNECESSARY! You probably won't need it, but it is here as an example
 */
function sitename_pagename_preprocess_sitename_pagename_main(&$variables) {
  // Use view_embed_view or related here.

  // As an example, this fills in the world variable sitename_pagename_page_main() didn't.
  $variables['world'] = t("World!");
}

Finally, make the template file. sitename_pagename_main.tpl.php:

<strong><?php print $hello;?> <?php print $world;?></strong>

Put the module and code in $DRUPAL_ROOT/sites/all/module/custom/sitename_pagename/. Enable your new module and check the new page at: sitename.com/page-path/

Update 7/5/13: New article using this method to implement a template file for a custom block with an admin page to manage it: Instead, how about a Drupal block with a custom settings form?.

Comments

(Statically copied from previous site)

Jonathan-David … replied on June 5, 2013 - 4:34pm

Thanks man !!

Saxon replied on October 24, 2013 - 9:07am

Thanks a lot! Really helped out!

edgar-alexander replied on February 10, 2014 - 9:10am

Gracias, informaciĆ³n muy buena. Thanks, information very good.

Tarun Nagpal replied on April 13, 2014 - 11:03pm

Thanks.

nikhilanant replied on February 20, 2015 - 12:54am

Error: syntax error, unexpected ‘(’, expecting ‘)’ in

brad replied on March 20, 2015 - 11:22am

What is the rest of this error message?

sunil kumar replied on April 10, 2015 - 11:26am

good hope, thankyou.

Colin replied on September 17, 2015 - 4:30am

:Hi, :Thanks for this great tutorial. :Wondering, how would one load an existing template within the current theme? :i.e page.tpl.php or page–front.tpl.php

I have a url which passes variables to the front page, which is why I am loading the page again but on a different url.

Thx in advance

brad replied on October 26, 2015 - 10:41pm

I don’t understand why you would reload the page to access variables… Can you explain further (if you haven’t already solved this issue)