Adding a custom element type & expanding elements
The start and end date selection boxes proved to be a particular challenge in my upgrade of eventrepeat module. I handled it by creating my own form element type.
Hook_elements
First, use hook_elements() to declare the new type: 'eventrepeat_date' is the name of the new type. Also, if you want any default values, be sure to declare them either here or in the process function:
<?php
function eventrepeat_elements() {
$type['eventrepeat_date'] = array('#input' => TRUE);
return $type;
}
?>Dynamic expand function
Normally, #process would be declared as a "the" expand function in the element type declaration (hook_elements()), but since I needed to pass $edit and $node as function arguments in this case, I declare it for each instance of the element type. Hence a dynamic #process handler.
A normal expand function is more limited, because you can provide less context. Sending $user as a parameter would be possible though.
Here's how the element type is invoked when in a $form array. Notice in particular how the #process attribute is declared here as an array. The key is the name of the callback used to expand the type into multiple elements (you may not need a function such as this if your new type is simple). The value is an array of arguments to be passed to the callback function.
<?php
$form['end_controls']['end_date'] = array(
'#type' => 'eventrepeat_date',
'#title' => t('Repeat end date'),
'#process' => array('_eventrepeat_form_date' => array($edit, $node, 'eventrepeat_end')),
);
?>The expand function
Next, write the expand function. The first argument is always the element that is being passed for processing and the other arguments are the optional arguments I added in the #process attribute. Notice that I'm adding to $element and returning it:
<?php
function _eventrepeat_form_date($element, $edit = NULL, $node = NULL, $prefix = NULL) {
// Get current year, and drop next 10 years into an array.
$date = getdate(time());
$curyear = $date['year'];
$years = array(0 => '--'. t('Select') .'--');
while ($i < 10) {
$years[$curyear + $i] = $curyear + $i;
$i++;
}
// Months array.
$months = array(
'--'. t('Select') .'--',
t('January'),
t('February'),
t('March'),
t('April'),
t('May'),
t('June'),
t('July'),
t('August'),
t('September'),
t('October'),
t('November'),
t('December')
);
// Days array.
$days = array(0 => '--'. t('Select') .'--');
for ($i = 1; $i <= 31; $i++) {
$days[$i] = $i;
}
// Compose the select boxes, and add the exception editor button if necessary.
$element[$prefix .'month'] = array(
'#type' => 'select',
'#default_value' => $edit ? $edit[$prefix.'month'] : ($node->{$prefix.'month'} ? $node->{$prefix.'month'} : 0),
'#options' => $months,
);
$element[$prefix .'day'] = array(
'#type' => 'select',
'#default_value' => $edit ? $edit[$prefix.'day'] : ($node->{$prefix.'day'} ? $node->{$prefix.'day'} : 0),
'#options' => $days,
);
$element['comma'] = array(
'#type' => 'markup',
'#value' => ', ',
);
$element[$prefix .'year'] = array(
'#type' => 'select',
'#default_value' => $edit ? $edit[$prefix.'year'] : ($node->{$prefix.'year'} ? $node->{$prefix.'year'} : 0),
'#options' => $years,
);
if ($prefix == 'eventrepeat_EXDATE_edit') {
$element['exception_button'] = array(
'#type' => 'submit',
'#value' => t('Add/Delete Exception'),
);
}
return $element;
}
?>The theming function
Then, a theming function to pretty it up. This is basically a theming function for a form item, but I put an inline container and $element['#children'] (which is the constructed select boxes, etc. from my expand function) into it. Naming convention for the theme function is theme_elementname.
Without a theming function, you won't get any output.
<?php
function theme_eventrepeat_date($element) {
return theme(
'form_element',
array(
'#title' => $element['#title'],
'#description' => $element['#description'],
'#id' => $element['#id'],
'#required' => $element['#required'],
'#error' => $element['#error'],
),
'<div class="container-inline">'. $element['#children'] .'</div>'
);
}
?>