PHP

Ctools modalframe for node creation forms

Expanding on my calendar and module code here, this is what I used to open a calendar event creation form into a ctools modalframe popup.  The content type is, "public_event". ctools_modal_text_button() creates the button that loads the modalframe, which I've added to specific block content on my site through hook_block_view_alter.

<?php
 
/**
 * @file
 * Create Event module file
 *
 * This module allows anonymous users to create calendar events.
 */
 
/**
 * Implementation of hook_menu
 */
function mymodule_menu() {
  $items = array();
  $items['create/%ctools_js/public-event'] = array(
      'title' => 'Submit an event',
      'page callback' => 'modal_content',
      'page arguments' => array(1),
      'access callback' => TRUE,
      'type' => MENU_CALLBACK,
  );
 
  return $items;
}
 
 
/**
 * Function for creating ctools text button
 */
function mymodule_link() {
 
  global $user;
 
  ctools_include('ajax');
  ctools_include('modal');
  ctools_modal_add_js();
 
  $output = ctools_modal_text_button(t('Submit an event'), 'create/nojs/public-event', 'public-event');
 
  return $output;
}
 
 
/**
 * Implementation of hook_block_view_alter
 * Just adding ctools text button to a block on the calendar page
 */
function mymodule_block_view_alter(&$data, $block) {
  if (($block->delta == 'submit_event') && ($block->context == 'event_calendar')) {
      $string = $data['content'];
      $data['content'] = array();
      $data['content'] = array(
         '#type' => 'markup',
         '#markup' => $string.'<div class="modal">'.mymodule_link().'</div>',
         );
   }
}
 
/**
 * Callback function for loading events node/add form into a ctools modal window
 */
function modal_content($js = FALSE) {
 
  global $user;
 
  ctools_include('node.pages', 'node', '');
  ctools_include('modal');
  ctools_include('ajax');
 
  $type = 'public_event';
  $node = (object) array(
    'uid' => $user->uid,
    'name' => (isset($user->name) ? $user->name : ''),
    'type' => $type,
    'language' => LANGUAGE_NONE);
 
  if (!$js) {
    return drupal_get_form($type . '_node_form', $node);
  }
 
  $form_state = array(
    'title' => t('Submit an event'),
    'ajax' => TRUE,
  );
 
  $form_state['build_info']['args'] = array($node);
  form_load_include($form_state, 'inc', 'node', 'node.pages');
  $output = ctools_modal_form_wrapper($type . '_node_form', $form_state);
 
  if (!empty($form_state['executed'])) {
    $output = array();
    $output[] = ctools_modal_command_display(
      t('Successful.'),
      '<div class="modal-message">
          <h3>Thanks for submitting your event to us.</h3>
          <p>Your submission will be approved by an editor before it will appear on the site.</p></div>');
  }
  print ajax_render($output);
  exit;  
}
 
 
 /**
 * Implementation of hook_form_alter
 * Alter public_event creation form
 */
function mymodule_form_alter(&$form, &$form_state, $form_id)  {
 
  if ($form_id == 'public_event_node_form')  {
 
        $form['author']['#access'] = FALSE;
        $form['comment_settings']['#access'] = FALSE;
        $form['locations']['#weight'] = 3;
        $form['locations']['#title'] = t('Location or venue');
        $form['actions']['submit']['#value'] = t('Submit your event for review');
 
        $message = t('<p><strong>Create a public event. All submissions will be reviewed
        and approved by an editor before
        appearing on the site. </strong></p>When entering events:</ br>
        <ul>
            <li>Enter the event at least 2 weeks prior to event date.</li>
            <li>Fill out all information required as completely as possible.</li> </ul>');
        drupal_set_message($message, $type = 'status', $repeat = FALSE);
    }
}
∗ Permalink

Calendar

The above image is a partial screenshot for a calendar in Drupal 7 I've been working on. Working with the calendar and better exposed filters modules - I added a taxonomy vocabulary for the colored categories.

Full page layout

The first thing I wanted to do was remove sidebars on the calendar page and prevent any from being added because they would mess with my layout. This is a 'fusion theme so I needed to remove helper classes which determine the layout.

/**
 * Implementation of preprocess_page in template.php
 */
function awesometheme_preprocess_page(&$variables) {
 
    //Unset sidebar on calendar page, they get in the way of the calendar
   if (arg(0) == 'calendar') {
       unset($variables['page']['sidebar_second']);
   }
}
 
/**
 * Implementation of preprocess_html
 */
function awesometheme_preprocess_html(&$variables) {
 
  //We unset sidebar in preprocess_page(), now we remove helper classes
  //and add one new one ('no-sidebars')
   if (arg(0) == 'calendar') {
      foreach ( $variables['classes_array'] as $key => $item ) {
          if ( ($item == 'one-sidebar sidebar-second')) {
              unset($variables['classes_array'][$key]);
            }
        }
      $variables['classes_array'][] = 'no-sidebars';
    }
}

Popup tooltip

For the tooltip I used the beautytips module. In the readme.txt is some nice api stuff. I knew I wanted to load the full node (the page linked to in the calendar item) so I could create the content for the tooltip. I knew I'd use node_load() which requires the node_id as a parameter. I added the node_id as a field in the calendar to do this. Now I needed to write some logic that made all this happen so I turned on the theme developer module which told me all the calendar date items load inside the template "calendar-item.tpl.php". I copy / pasted it into my theme then opened up template.php to create my variables.

/**
 * Implementation of preprocess_calendar_item
 */
function awesometheme_preprocess_calendar_item(&$variables) {
 
   //Create tooltip variables for calendar-item.tpl.php
   if (arg(0) == 'calendar') {
      $nid = $variables['rendered_fields'][1];
      $fullnode = node_load($nid, NULL, TRUE);
 
      $variables['term'] = $fullnode->field_eventgenre[$fullnode->language][0]['tid'];
      $variables['body'] = $fullnode->body[$fullnode->language][0]['value'];
      $variables['title'] = check_plain($fullnode->title);
 
  if ( $fullnode->type == 'event' ) {
      $unixtimestamp = strtotime($fullnode->field_event_date[$fullnode->language][0]['value']);
      $drupaldate = format_date($unixtimestamp,'custom','M, j D - g:i a');
      $variables['public_date'] = $drupaldate;
      $variables['location'] = check_plain($fullnode->locations[0]['name']);
      $variables['address'] = check_plain($fullnode->locations[0]['street']);
      $variables['address'] .= ' ' .check_plain($fullnode->locations[0]['additional']);
      $variables['city'] = check_plain($fullnode->locations[0]['city']);
      $variables['state'] = check_plain($fullnode->locations[0]['province']);
   }
 
      //Remove node_id view field since we're done with it
      unset($variables['rendered_fields'][1]);
      //kpr($fullnode);
   }
}

Now that my variables were created I could build the tooltip in calendar-item.tpl.php where I also included the beautytips api so it would load into a nice html5 canvas popup.

  <!-- BeautyTips module API garnered from readme.txt -->
  <?php
    if (arg(0) == 'calendar') {
       $options['calendar_tooltip'] = array(
         'cssSelect' => '.view-item .calendar a',
         'contentSelector' => '$(this).next()',
         'trigger' => array('mouseover', 'click'),
         'width' => 350,
       );
      beautytips_add_beautytips($options);
   }
  ?>
 
   <!-- Tooltip popup for calendar, variables created in preprocess_calendar_item in template.php -->
   <?php if (arg(0) == 'calendar') { ?>
      <div class="tooltip" style="display:none;">
         //Should include 'isset' statements, removed for readability
         <h1 class="title"> <?php print $title ?> </h1>
         <h2 class="date-time"> <label>Date/Time:</label> <?php print $public_date; ?></h2>
         <h2 class="location"> <label> Location: </label> <?php print $location; ?>; <?php print $address; ?>, <?php print $city; ?>, <?php print $state; ?></h2>
         <span class="body"> <?php print $body; ?></span>
      </div><!-- /tooltip -->
    <?php } ?>

Anonymous create event form

The client also wanted anyone visiting the site to be able to submit calendar events. I created a new content type called "public event" because they had certain requirements for the fields and such. I needed to open a node/add form for this content type for anonymous users inside the same theme. I decided to create a module and load the form inside a function callback using hook_menu(). Which I cleaned up with hook_form_alter().

<?php
 
/**
 * @file
 * Create Event module file
 *
 * This module allows anonymous users to create calendar events.
 */
 
 
/**
 *  Implementation of hook_menu()
 */
function public_calendar_events_menu() {
    $items = array();
    $items['create-calendar-event'] = array(
      'type' => MENU_CALLBACK,
      'page callback' => 'public_calendar_events_page',
      'access arguments' => array('access content'),
    );
 
  return $items;
}
 
/**
 * Page callback to display the calendar public_event node creation form
 */
function public_calendar_events_page() {
 
    global $user;
    $type = 'public_event';
    $node = (object) array(
      'uid' => $user->uid,
      'name' => (isset($user->name) ? $user->name : ''),
      'type' => $type,
      'language' => LANGUAGE_NONE,
    );
 
    $form_state['build_info']['args'] = array($node);
    form_load_include($form_state, 'inc', 'node', 'node.pages');
    $form = drupal_build_form($type . '_node_form', $form_state);
 
    return $form;
}
 
 
/**
 * Alter public_event creation form
 */
 
function public_calendar_events_form_alter(&$form, $form_state, $form_id)  {
 
  if ( ($form_id == 'public_event_node_form') && (arg(0) == 'create-calendar-event') )  {
 
        unset($form['author']);
        unset($form['comment_settings']);
        unset($form['menu']);
        unset($form['path']);
        $form['locations']['#collapsed'] = 0;
        $form['locations']['#weight'] = 3;
        $form['locations']['#title'] = t('Location or venue');
        $form['actions']['preview']['#value'] = t('Preview form before submitting');
        $form['actions']['submit']['#value'] = t('Submit your event for review');
    }
}
∗ Permalink

Theme forms

I've been working on a project in Drupal 6 Commons which involves theming the user page, which is kind've a rats nest of information and options. The client wanted something with much less.

The first thing I wanted to do was remove a bunch of stuff and change the text on labels. This is a Drupal 6 project so I needed to use hook_theme() in template.php so Drupal would use my form hook.

/**
* Implementation of hook_theme.
*/
function mytheme_theme() {
  return array(
    'user_profile_form' => array(
      'arguments' => array('form' => NULL),
    ),
  );
}

The form uses the "value" of user-profile-form which I found using firebug at the bottom of the form. That's what you need to use in hook_theme() and elsewhere.

Now I could use the following form hook to unset much of the excess and change text.

/**
* Theme override for user page
*/
function mytheme_user_profile_form($form) {
    unset($form['account']['mail']['#description']);
    unset($form['account']['name']['#description']);
    unset($form['account']['pass']['#prefix']);
    $form['submit']['#value'] = 't(Save All Changes)';
    $form['account']['pass']['pass1']['#title'] = t('Reset your password');
    $form['account']['pass']['pass2']['#title'] = t('Confirm new password');
 
  //dpm($form); //uncomment to look at contents of form
  return (drupal_render($form));
}

This is a quick way of just removing elements that are already there and working with what's left. If I wanted though I could rebuild the form in a template file.

/**
* Implementation of hook_theme.
*/
function mytheme_theme() {
  return array(
    'user_profile_form' => array(
        'arguments' => array('form' => NULL),
        'path' => drupal_get_path('theme','mytheme') . '/templates',
        'template' => 'user-profile-form',
     ),
  );
}

This implementation of hook_theme() tells Drupal to look for the template file 'user-profile-form.tpl.php' inside /templates in my theme folder. From there I could use a preprocess function to rebuild the form using drupal_render().

function mytheme_preprocess_user_profile_form(&$vars) {
    $vars['name'] = drupal_render($vars['form']['account']['name']);
    $vars['mail'] = drupal_render($vars['form']['account']['mail']);
    $vars['pass'] = drupal_render($vars['form']['account']['pass']);
}

Then inside user-profile-form.tpl.php I could print the following;

     <?php
       print $name;
       print $mail;
       print $pass;
       //print drupal_render($form); uncomment to load rest of form
     ?>

If this were a Drupal 7 project I could use hook_form_alter() inside template.php.

/**
* Implementation of hook_form_FORM_ID_alter().
*/
function hook_form_user_profile_form_alter(&$form, &$form_state, $form_id) {
    //dpm($form);
}

hook_form_alter() in Drupal 6

In Drupal 6 if there are required fields you won't be able to just unset them, you'll need to use hook_form_alter() which needs to be inside a module. You would create a file like so called maybe, "themeforms.info" and put this inside it,

name = themeforms
description = themeforms implements hook_form_alter to theme tha forms
core = 6.x
package = "themeforms"

And a file called, "themeforms.module"

/**
  * Implementation of hook_form_alter() in Drupal 6
  */
 function themeforms_form_alter(&$form, $form_state, $form_id) {
 
    switch ($form_id) {
      case 'user_profile_page':
           unset($form['name']);
           unset($form['message']);
           // $output .= dsm($form);
        break;
 
          $output .= drupal_render($form);
      return $output;
    }
  }
∗ Permalink
Subscribe to PHP