Druga część zmagań z Drupalem, implementacja bloku z formularzem, wraz z obsługą błędów, zapisem do bazy i wyświetlaniem komunikatów. Formularz korzysta z AHAH i JQuery, jakkolwiek działa także bez wsparcia JavaScriptu. Całość nie jest dopieszczona wizualnie, ale działa bez zarzutu ;]
$(document).ready(function(){
$("input#edit-email-subscribtion").focus(function(){
$(this).val("");
});
});
Prosta funkcja JQuery umieszczona w pliku “custom_newsletter.js” w folderze modułu. Jeżeli po załadowaniu strony zostanie zaznaczone pole o id=”edit-email-subscribtion” jego wartość zostanie ustawiona na pustą. Pozostałe fragmenty kodu należą do “custom_newsletter.module”
function custom_newsletter_init() {
drupal_add_js(drupal_get_path('module', 'custom_newsletter') .'/custom_newsletter.js');
}
Funkcja dodaje do nagłówków stron, w których używany jest moduł, link do powyższego pliku z JavaScriptem.
function custom_newsletter_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$block[0] = array(
'info' => t('Subscription block'),
);
return $block;
case 'view': default:
if ($delta==0) {
$block['subject'] = t('Newsletter');
$block['content'] = custom_newsletter_contents();
}
return $block;
}
}
Funkcja deklarująca wyświetlany blok, część “list” odpowiada za wyświetlanie bloku w liście bloków, “view” determinuje jego wygląd na stronie. Więcej na: hook_block
function custom_newsletter_contents(){
$msgfield = "<div id=custom_subscribtion_msgfield>".
custom_newsletter_build_message(variable_get('subscribtion_state','')).
"</div>";
$contents = "<div>".$msgfield.drupal_get_form('custom_newsletter_form')."</div>";
variable_del('subscribtion_state');
return $contents;
}
Funkcja wywoływana w celu wypełnienia bloku zawartością. Zawiera diva wyświetlającego komunikaty, oraz wywołanie funkcji generującej formularz.
function custom_newsletter_build_message($message){
return '<hr />'.$message.'<hr />';
}
Poglądowa wersja tworzenia komunikatu na podstawie wartości zadeklarowanej zmiennej. Przydałoby się zróżnicowanie komunikatów pod kątem treści i wyglądu, może kiedyś ;]
function custom_newsletter_form(){
$form['email_subscribtion'] = array(
'#type' => 'textfield',
'#default_value' => t('enter email here'),
'#size' => 20,
'#maxlength' => 30,
'#element_validate' => array('subscribtion_form_validate'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Subscribe'),
'#submit' => array('custom_newsletter_form_submit'),
'#ahah' => array(
'path' => 'custom_newsletter/js',
'wrapper' => 'custom_subscribtion_msgfield',
'method' => 'replace',
'progress' => array('type' => 'throbber',),
),
);
return($form);
}
Deklaracja poszczególnych pól formularza, przypisanie im właściwości oraz funkcji wywoływanych w celu walidacji pól oraz po zatwierdzeniu formularza. Sekcja #ahah określa reakcję na zatwierdzenie bez przeładowywania strony, określa kolejno link pod którym są opisane owe reakcje, następnie element zmieniany podczas działania funkcji, metodę zmiany oraz animację. Więcej na: form API
function custom_newsletter_menu() {
$items = array();
$items['custom_newsletter/js'] = array(
'title' => 'Javascript Choice Form',
'page callback' => 'custom_newsletter_js',
'access arguments' => array('access custom_newsletter js'),
'type' => MENU_CALLBACK,
);
return $items;
}
Funkcja tworzy element menu (http://localhost/drupal/?q=custom_newsletter/js), pod którym znajduje się funkcja wywoływana po zatwierdzenia formularza z użyciem AHAH.
function custom_newsletter_js(){
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
// Get the form from the cache.
$form = form_get_cache($form_build_id, $form_state);
$args = $form['#parameters'];
$form_id = array_shift($args);
// We will run some of the submit handlers so we need to disable redirecting.
$form['#redirect'] = FALSE;
// We need to process the form, prepare for that by setting a few internals
// variables.
$form['#post'] = $_POST;
$form['#programmed'] = FALSE;
$form_state['post'] = $_POST;
// Build, validate and if possible, submit the form.
drupal_process_form($form_id, $form, $form_state);
// This call recreates the form relying solely on the form_state that the
// drupal_process_form set up.
$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
// Render the new output.
$coumunicate = custom_newsletter_build_message(variable_get('subscribtion_state',''));
variable_del('subscribtion_state');
return drupal_json(array('status' => TRUE, 'data' => $coumunicate));
}
Funkcja pełna kodu świadczącego o kulawości AHAH, formularz należy najpierw zrekonstruować z keszu aby go zwalidować i zatwierdzić. Brzydki kod który nie wiadomo jak się zachowa przy kilku formularzach. Zwraca element json modyfikujący element określony w deklaracji formularza w sekcji ahah.
function custom_newsletter_form_submit($form, &$form_state){
$form_values = $form_state['values'];
$email=$form_values['email_subscribtion'];
db_query("INSERT INTO {custom_newsletter} (id, email, hash) VALUES (%d, '%s', '%s')", 0, $email, substr(md5($email.time()),0,19));
}
Funkcja wymieniona w deklaracji formularza, poza przekazaniem wartości pola formularza, warto zwrócić uwagę na database abstraction layer.
function subscribtion_form_validate($element, &$form_state) {
if (empty($element['#value'])) {
variable_set('subscribtion_state', 'ERROR');
form_set_error('email_subscribtion', '', TRUE);
}else{
variable_set('subscribtion_state', 'OK');
}
}
Jest to funkcja wymieniona w deklaracji formularza, kod jest poglądowy, przydałoby się sprawdzanie regexem, oraz dublowania wpisów. Formularz nie zostaje zatwierdzony, jeżeli zostanie wywołana funkcja form_set_error. W tym przypadku pole wiadomości jest puste, żeby nie wyświetlać komunikatu błędu u góry strony.
Pisanie w Drupal API potrafi być dość przyjemne kiedy już wiadomo o co chodzi. Człowiek staje się wygodny, tym bardziej dziwi toporność AHAH. Standardowe komunikaty błędów pojawiające się na górze strony nie są domyślnie obsługiwane przez AHAH i ich ewentualne wyświetlenie następuje dopiero po ręcznym przeładowaniu strony. Ponadto jego używanie jest tak mało intuicyjne, że aż powstał moduł mający temu zaradzić (AHAH helper module).
Poza tym w kodzie widać moje pierwsze próby przekazywania zmiennych za pomocą funkcji variable_set(). Zmienne te są przechowywane w bazie i częste operacje na nich są zapewne mało efektywne, choć pewnie nie mają alternatywy w przypadku operacji AJAXowych.
Warto zaopatrzyć się w moduł Cache Disable, wyłącza keszowanie bloków i elementów menu. Spowalnia to działanie całości, ale ułatwia pracę nad modułami, brak reakcji na zmiany w kodzie nie musi być leczony ręcznym usuwaniem keszu w bazie ;]
Coming soon: Strony konfiguracyjne oraz zarządzające modulem. Stay tuned ;]