I have the following code that set a button to download a file using display suite code field:

global $user;

if (!isset($_SESSION['downinc'])) $_SESSION['downinc'] = 1;

if ($user->uid) $_SESSION['downinc'] = 0;

<form id="form1" name="form1" method="post" action="/downloadcode">
    <input type="hidden" name="download_code" id="download_code" value="[node:field_code_file]" />  
    <input type="hidden" name="uri" id="uri" value="[node:url:relative]" />
    <input type="submit" class="downloadButton" value="Download Code">

When the Download Code button is clicked, it will call the module that I have created as follows:

function custom_download_menu() {
    $items['downloadcode'] = array(
      'page callback' => 'custom_download_view',
      'access arguments' => array('access content'),
      'access callback' => TRUE,
    return $items;

function custom_download_view() {
    $js = "var time_left = 5;
        var cinterval;

        function time_dec(){
        document.getElementById('countdown').innerHTML = time_left;
        if(time_left == 0){

    cinterval = setInterval('time_dec()', 1000);";
    drupal_add_js($js, 'inline');

    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache"); // HTTP/1.0
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past

    $tracker = array(
        'database' => 'c1tracker',
        'username' => 'c1tracker',
        'password' => 'mypass',
        'host' => 'localhost',
        'driver' => 'mysql',

    Database::addConnectionInfo('tracker', 'default', $tracker);

    $current_ip = $_SERVER['REMOTE_ADDR'];
    $referrer = $_SERVER['HTTP_REFERER'];
    $user_agent = $_SERVER['HTTP_USER_AGENT'];

    global $user;

    if (!isset($_SESSION['downinc'])) {
        //set session here since it was not set from the referrer page because of caching.
        $_SESSION['downinc'] = 1;

        $error = "Invalid download. No Session.";

        $sql = "INSERT INTO `download_log` (`id`, `error`, `ip_address`, `referrer`, `user_agent`, `date_time`) VALUES (NULL, '$error', '$current_ip', '$referrer', '$user_agent', now())";


    if (!isset($_POST['download_code'])){
        $error = "Invalid download. File not set.";

        echo "<p>$error</p><p>Please clear your browser's cache.</p><h4><a href=\"http://www.mysite.com/user/register\">Sign up</a> now or <a href=\"http://www.mysite.com/user/login\">login</a> if you are an existing member.</h4>";

        $sql = "INSERT INTO `download_log` (`id`, `error`, `ip_address`, `referrer`, `user_agent`, `date_time`) VALUES (NULL, '$error', '$current_ip', '$referrer', '$user_agent', now())";

        $file = $_POST['download_code'];

        if ( $_SESSION['downinc'] < 4 || $user->uid ) {
            header("Refresh: 5; URL=$file");

            $_SESSION['downinc'] = $_SESSION['downinc'] + 1;

            echo "<p>Your download will start in <span id=\"countdown\">5</span> seconds...</p>";
            $uri = substr($_POST['uri'],1);

            echo "<p>You have downloaded code 3 times.</p><p>To continue downloading, please login or register.</p><h3><a href=\"http://www.mysite.com/user/register?destination=$uri\">Sign up</a> now or <a href=\"http://www.mysite.com/user/login?destination=$uri\">login</a> if you are an existing member.</h3>";


    echo "<p>Problems with the download? Please go back to previous <a href=\"$referrer\">page.</a></p><p>If you have problem downloading this file, kindly <a href=\"http://www.mysite.com/contact\">contact us</a>.</p>";

I know that this is not a perfect module and needs to be rewrite. Since I am new to creating a module in Drupal 7, I am trying hard to fix this and make it standard.

Currently I can download the file without problem. What is missing here is when it redirects to www.mysite.com/downloadcode, the only content I see is this:

Your download will start in 5 seconds...

Problems with the download? Please go back to previous page.

If you have problem downloading this file, kindly contact us.

The template is gone totally. No header, sidebar, footer, no themes at all. And also the countdown javascript did not work. Anyone know how to fix this?

  • You need to return from menu callbacks, you cannot echo directly from them. I suggest you start here: drupal.org/documentation/understand
    – Sam Becker
    Commented Aug 6, 2013 at 17:05
  • 1
    That is not exact: You can print something directly from the menu callback; you just don't need to be surprised if Drupal doesn't output the rest of the page (e.g. blocks).
    – apaderno
    Commented Aug 6, 2013 at 18:52

1 Answer 1


The reason why you don't get anything you normally see in a Drupal page is that you are printing the output, which is what you would do (for example) when the menu callback needs to only return a XML string, without anything else. (Think of the XML sitemap module when it returns the XML sitemap of the site.)
When you need to see the output of the menu callback put in a Drupal page, the menu callback needs to return:

  • A string, which can contain HTML tags
  • An array as accepted by drupal_render()

The reason of this is evident in the code of drupal_deliver_html_page().

  // Menu status constants are integers; page content is a string or array.
  if (is_int($page_callback_result)) {
    // …
  elseif (isset($page_callback_result)) {
    // Print anything besides a menu constant, assuming it's not NULL or
    // undefined.
    print drupal_render_page($page_callback_result);

  // Perform end-of-request tasks.

If your menu callback doesn't return anything, neither branches of the control statement will be executed, and Drupal will not call drupal_render_page(), which is the function that renders a page.

As for what other is wrong in your code:

  • drupal_add_http_header() should be used instead of header().
  • The function to insert a new row in a database is db_insert(), while db_update() is used to update an existing row.
  • A Drupal module should never access $_POST.
  • If you set the access callback to TRUE, the access arguments are not used.
  • Most importantly, you should not create a form without using the form API. If the code is trying to let the users upload a file, image_example_style_form(), image_example_style_form_validate(), and image_example_style_form_submit() are perfect example of how to handle a file upload with the form API, although the code is for uploading an image.

    // Use the #managed_file FAPI element to upload an image file.
    $form['image_example_image_fid'] = array(
      '#title' => t('Image'),
      '#type' => 'managed_file',
      '#description' => t('The uploaded image will be displayed on this page using the image style choosen below.'),
      '#default_value' => variable_get('image_example_image_fid', ''),
      '#upload_location' => 'public://image_example_images/',

    If the purpose of the code is letting users download a file, that can still be done with a form API form, since hidden is a form API's element. Send a CSV file to the user on FAPI form submit explains how to allow users to download a file with code inside a form submission handler.
    Notice that you can always use drupal_get_form() to render a form from PHP code, supposing that Drupal has been bootstrapped, which is what happens when a module requires you to enter code that is passed to php_eval().

  • I have already change some of the code.try to look at here. Btw I'm not trying yo use the form for upload. It's a form that hide values of files that's needed by my visitor to download the file. Just wondering how to create that form using module if it is really necessary.
    – jaypabs
    Commented Aug 7, 2013 at 10:09
  • I have updated my answer.
    – apaderno
    Commented Aug 7, 2013 at 13:58
  • Thanks for the reply. But I don't still get what you mean. If you just followed my module as I link it above, where should I put your code? In the module or in the display suite code field? To avoid confusion, can you give the sample in a file field instead of image field?
    – jaypabs
    Commented Aug 8, 2013 at 0:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.