Modulok fejlesztése

A Drupal számos modullal rendelkezik alapfunkcionalitásainak megvalósítására. Ezen kívül rendelkezésünkre áll a közösség által fejlesztett kiegészítők garmadája, melyekkel rendkívül változatos formában egészíthetjük ki webhelyünket. Előfordulhat ugyanakkor, hogy nem áll rendelkezésre olyan modul, amire nekünk szükségünk van, vagy a meglévők nem pontosan azt nyújták, és beállításokkal sem érhetjük el, amit szerertnénk. Ezen esetekben logikus lépés lehet, hogy saját modult fejlesztünk, vagy a meglévő modulokat módosítjuk saját igényünk szerint.

Annak eldöntése, hogy újabb modult érdemes fejleszteni, vagy egy meglévőt módosítani, nem mindig egyszerű kérdés. Az alapmotor vagy valamely kiegészítő modul módosításával olyan feladatot veszünk a nyakunkba, mely csak később jelentkezik. Amennyiben valamikor frissíteni szeretnénk a rendszer működtető kódokat, magunknak kell figyelnünk a változások fenntartására, a felülírások elkerülésére. Ebben nagyon sokat segíthet egy változáskövető rendszer (CVS, Subversion), ezeket azonban nem használják szélesebb körben. Ezért ha jelentősen eltérő funkcionalitásra van szükségünk, mint amit egy meglévő modul nyújt, akkor célszerű lehet annak lemásolása, és más néven történő továbbfejlesztése saját céljainkra.

Amennyiben olyan kiterjesztési ötletünk, javaslatunk van egy modulhoz, mely széles körben is érdeklődésre tarthat számot, akkor ezt célszerű a modul fejlesztőjének javasolni. Az alapmodulok, és a kiegészítők is rendelkeznek hibajelentő és javaslat beküldő felülettel, ahol ötleteinket megadhatjuk. Általában jó gyakorlat, és a teljes rendszer fejlődését előbbreviszi, ha saját általános célú fejlesztéseinket a nyilvánosság elé tárjuk, és erre a Drupal fejlesztői szervere lehetőséget is ad. Ezesetben természetesen kicsit körültekintőbben kell a fejlesztett modult elkészítenünk, de ez csak a minőségi kialakítást segíti, ezért kifejezetten hasznos is lehet.

20 Napi Drupal API - 1. nap: Új mezőtípus létrehozása CCK-val

Jelen írás egy fordított kivonat, melyet zserno készített. Az eredeti itt található:
http://www.trellon.com/content/blog/cck-creating-new-field-types

A CCK a legfontosabb rövidítés, amit ismernünk kell ha a Drupal tartalomkezeléséről beszélünk. Az eredeti rövidítés a Content Construction Kit szavakból áll össze, ami egy olyan keretrendszer, melynek segítségével egy webhely felhasználói különféle információkat küldhetnek be.

A többi Drupal kiegészítőhöz hasonlóan a CCK igazi szépsége sem csupán az általa kínált funkcionalitásban rejlik, hanem sokkal inkább a többi modul számára kínált kibővíthetőségében. A CCK API lehetővé teszi más modulok számára, hogy saját mezőtípusokat definiáljanak, melyek aztán tökéletesen illeszkednek a Drupal platformba.

A CCK sikerét jól jellemzi, hogy nagy részét már a Drupal 7-es alapcsomag is tartalmazza
A legismertebb CCK-ra épülő modulok a Filefield, Imagefield, Emfield, Link és Email, de összesen közel 300 CCK-val cimkézett Drupal 6 modul található a http://drupal.org-on. Függetlenül a felhasználási területtől, ezek mindegyike a CCK API-ját használva képes saját mezőtípusait definiálni, melyek azután bármelyik tartalomtípusban felhasználhatók.

Egy egyszerű példaként Matthias Hutterer Email modulját fogjuk megvizsgálni. Ez a modul a CCK API-t használva egy saját mezőtípust deklarál, amivel megfelelően formázott e-mail címeket tárolhatunk saját tartalomtípusainkban. Meg fogjuk vizsgálni a kulcsfontosságú függvényeket, amik az egyedi mezőtípus definiálásához szükségesek, és ahol szükséges, rövid magyarázattal szolgálunk a konfigurációs lehetőségekről.
Kezdjük a hozzávalókkal (a legfőbb hook-ok és eljárások, amiket használhatunk):

  • Telepítés:
    • content_notify
  • Mező beállítások (field settings):
    • hook_field_info
    • hook_field_settings
    • hook_field
  • Felületi elemtípus beállítások (widget settings):
    • hook_widget_info
    • hook_widget_settings
    • hook_widget
    • hook_elements
  • Sminkelés (theming):
    • hook_theme + theme_MY_ELEMENT
    • custom formatters

A lista hosszú, vágjunk is bele.

Telepítés

Első lépésként tudatnunk kell a CCK-val, hogy modulunk mikor áll használatra készen:

function email_install() {
  drupal_load('module', 'content');
  content_notify('install', 'email');
}

A *.install file-ban közölhetjük vele, amikor modulunk telepítése, engedélyezése, kikapcsolása vagy eltávolítása zajlik. Ezt a megfelelő hook-ban (pl. hook_install, hook_enable, stb), a megfelelő paraméterrel (install, enable, stb.) meghívott content_notify CCK eljárással tehetjük meg.

Mező beállítások

Az email.module file-ban látható, ahogy a mező formát ölt.

function email_field_info() {
  return array(
    'email' => array(
      'label' => 'Email',
//...

A fenti kóddal regisztráltuk a mezőnket, az "email" belső azonosítóval és egy "Email" címkével.
function email_field_settings($op, $field) {
  switch ($op) {
    case 'database columns':
      $columns['email'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE);
      return $columns;
  }
}

Itt az Email modul arra kéri a CCK-t, hogy kezelje ő a szükséges adatbázissal kapcsolatos teendőit. Egy külön oszlopot kér az őt használó tartalomtípusok tábláiban. Az oszlop elnevezése a majdan létrehozott mezők nevétől függően mindig "field_MEZŐNÉV_email" alakú lesz, beállításait pedig a $columns['email'] tömbben megadott jellemzők határozzák meg. Az $op változó a "database columns" értéken kívül még a "form" szöveget is tartalmazhatja, amivel egyedi űrlap készíthető a további szükséges beállításoknak (ld. a CCK Text modulját).
Végül nézzük hogyan tudjuk validálni a mezőnk tartalmát.
function email_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
    case 'validate':
      if (is_array($items)) {
        foreach ($items as $delta => $item) {
          if ($item['email'] != '' && !valid_email_address(trim($item['email']))) {
            form_set_error($field['field_name'],t('"%mail" is not a valid email address',array('%mail' => $item['email'])));
          }
        }
     }
     break;
//...

Ha a mezőnek van értéke, akkor a fenti kód a Drupal valid_email_address eljárásával ellenőrzi, hogy az valódi e-mail cím-e.

Felületi elemtípus beállítások

Miután felvázoltuk a mezőtípusunk logikai összetevőit, jöhet a felületi elemtípus (ún. widget) űrlapjának definiálása, hogy a végleges HTML űrlapelem megkaphassa a megfelelő attribútumokat. A hook_field* függvényekhez hasonlóan a hook_widget* függvényeknek is van _info és _settings változata.

function email_widget_info() {
  return array(
    'email_textfield' => array(
      'label' => t('Text field'),
      'field types' => array('email'),
      'multiple values' => CONTENT_HANDLE_CORE,
      'callbacks' => array(
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}
//…
function email_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 60;
      $form['size'] = array(
        '#type' => 'textfield',
        '#title' => t('Size of textfield'),
        '#default_value' => $size,
        '#element_validate' => array('_email_widget_settings_size_validate'),
        '#required' => TRUE,
      );
      return $form;
 
    case 'save':
      return array('size');
  }
}

Vegyük észre hogy a hook_widget_info a hook_field_info-val hasonló struktúrát használ (name és label kulcsok), azonban kiegészíti a felhasználható mezőtípusok listájával ('field types' => array('email')).
A hook_field_settings-hez hasonlóan, a hook_widget_settings-ben is lehetőség van finomítani a beállítások űrlapján (kettőjük végeredményét látjuk az "admin/content/node-type/[TARTALOMTÍPUS NEVE]/fields/[MEZŐNÉV]" útvonalon).
Az Email modul (akárcsak a Text modul) ezt arra használja, hogy a "size" mezőnek (mely a bevihető szöveg maximális hosszát határozza meg) biztosan legyen értéke.
Ahhoz, hogy az űrlapelemünk megjelenjen az űrlapon, a hook_widget-et kell használnunk:
function email_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
  );
  return $element;
}

Tehát amikor a CCK hozzáadja a mezőnket az űrlaphoz, tudatjuk vele, hogy állítsa be a megfelelő widget típust (ez esetünkben az "email_textfield") és az alapértelmezett értéket, ha az létezik. Ha a modulunk több widget típust is használna, akkor itt a widget típus szerinti feltételes elágazással tudnánk a megfelelő attribútumokat beállítani. A legtöbb esetben azonban a fenti függvény elegendő (bővebben: nodereference.module).
Miután minden szükséges információt tudattunk a CCK-val, még a Drupal Form API-jának is el kell magyarázni, hogy miként bánjon a mezőnkkel. Ezt a hook_elements függvénnyel tehetjük meg:
function email_elements() {
  return array(
    'email_textfield' => array(
      '#input' => TRUE,
      '#columns' => array('email'),
      '#delta' => 0,
      '#process' => array('email_textfield_process'),
    ),
  );
}

Vegyük észre, hogy itt az "email" azonosító helyett az "email_textfield"-et használjuk, mert a Fom API az új widget-re kíváncsi, amit még a hook_widget_info-ban a mezőnkhöz kapcsoltunk. Továbbá azt is megmondjuk, hogy hol találja az új űrlapelem feldolgozásakor használandó kódot: '#process' => array('email_textfield_process').
Az eddig tárgyalt függvények mind az új mezőnk létrehozásával foglalkoztak: definiálták annak beállításait és létrehozták a szükséges widget-et. Egyikük sem foglalkozott azonban azzal, hogy a Drupal hogyan is fogja a widget-ünket szabályos HTML űrlapelemként megjeleníteni egy node szerkesztő form-on. Ennek leírása a (hook_elements-ben már említett) "#process" attribútumban megadott callback függvénnyel tehető meg:
function email_textfield_process($element, $edit, $form_state, $form) {
  $field = $form['#field_info'][$element['#field_name']];
  $field_key = $element['#columns'][0];
  $delta = $element['#delta'];
  $element[$field_key] = array(
    '#type' => 'textfield',
    '#title' => $element['#title'],
    '#description' => content_filter_xss($field['widget']['description']),
    '#required' => $element['#required'],
    '#maxlength' => 255,
    '#size' => !empty($field['widget']['size']) ? $field['widget']['size'] : 60,
    '#attributes' => array('class' => 'text', 'dir' => 'ltr'),
    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
  );
  return $element;
}

A Form API-t ismerőknek ez a kódrészlet már első ránézésre sokat elárul. A mezőnk egy szöveges beviteli mező (textfield) lesz, melyhez felhasználtuk a korábban definiált mező- és widget beállításokat ('widget' kulcs a fenti kódban), valamint a CCK-tól kapott információkat (ilyen pl. az $element['#title'], ami a "Mezők kezelése" oldalon egy új mezőcímke beküldésekor kerül beállításra).
Most hogy a CCK már tud az új mezőtípusunkról és a FAPI is tudja, hogy miként jelenítse meg azt a node szerkesztő űrlapon, nincs más hátra, mint az összegyűjtött adatok sminkelése.

Sminkelés

A hook_elements meghívásakor a Drupal feltételezi, hogy léteznek a deklarált elemekhez azok sminkfüggvényei is. Így pl. az Email modul esetében mivel az egyetlen deklarált elemünk az "email_textfield" volt, ezért létre kell hoznunk egy "theme_ email_textfield" nevű sminkfüggvényt. Habár a Drupal automatikusan összekapcsolja az űrlapelemet a hozzá tartozó sminkfüggvénnyel, szükség van a szokásos hook_theme függvény megírására is, hogy tudja: ez egy sminkelhető függvény.

function email_theme() {
  return array(
    'email_textfield' => array(
      'arguments' => array('element' => NULL),
    ),
// More theme functions declared here…
  );
}
 
function theme_email_textfield($element) {
  return $element['#children'];
}

A theme_email_textfield eljárás nem csinál semmi különöset, mindössze a Drupal Form API-ja által generált HTML értéket adja vissza (ahogy az az 'includes/form.inc'-ben látható). Érdemes tudni, hogy lehetne ennél komolyabb sminkfüggvényt is írni az element['field_name'] és $element['delta'] értékek felhasználásával (ezek az űrlapelem nevét és pozícióját tárolják).
Utolsó simításként készíthetünk a felhasználók által kezelhető ún. egyedi mező formázókat is, melyek az adott tartalomtípus "Megjelenítési beállítások" oldalán érhetők el (admin/content/node-type/[TARTALOMTÍPUS NEVE]/display).
Példa: az Email modul a hook_field_formatter_info segítségével definiálja saját mező formázóit, majd a hook_theme-ben deklarálja az azokat előállító callback függvényeket, végül megvalósítja az így deklarált theme_EGYEDI_FORMÁZÓ függvényeit, melyekben összeállítja a végső HTML kimeneteket.
---
A cikk hossza ellenére még csak a felszínét súroltuk a CCK lehetőségeinek. Az említett függvényekről bővebben a CCK csomagban található kiegészítő modulokban olvashatunk (pl. text, nodereference), melyek gazdag dokumentációt tartalmaznak.

Drupal kódolási stílus

Behúzások és sortörések

Egy behúzás két szóköz méretű, példa:


if (empty($valami)) {
echo "Teljesen be vagyok húzva."
}

A sorok végen ne legyen szóköz, utolsó karakter után legyen új sor. Minden fájl végén legyen egy üres sor: ez azért van, hogyha patchet készítesz, akkor ne kerüljön bele a "\ No newline at end of file" figyelmeztetés, illetve a patch maga olvashatóbb legyen.

Operátorok

Minden bináris operátort (két érték közé helyezendő operátorok), mint például a +, -, =, !=, ==, > stb. egy-egy szóközzel közre kell fogni, hogy olvashatóbb legyen.

Példa: $foo = $bar; (Rosszul írva: $foo=$bar;)

Az egy értékre ható operátorok (mint például az inkrementáló operátor, ++) közvetlenül a változó mellé írandók (pl: $foo++, vagy ++$foo).

Típuskonverzió

Mindig rakj egy szóközt a típus és a változó közé: (int) $mynumber.

Vezérlési szerkezetek (if, for, while, switch, stb.)

Hogy ne legyenek függvényhívásokkal összekeverhetőek az ilyen kódcsoportok, a nyitó parancs (if, else, stb.) után egy szóköz kötelező.

Kapcsos zárójelek használata minden esetben javasolt, még akkor is, ha a kód maga értelmes lenne nélkülük (egy soros mag). Ettől javul az olvashatóság, és valamelyest csökkenti annak esélyét, hogy a kódmag bővítése során logikai hibák kerüljenek be.


if (condition1 || condition2) {
action1;
}
elseif (condition3 && condition4) {
action2;
}
else {
defaultaction;
}

Függvényhívások és függvénydefiníciók

Függvények hívásakor a függvény neve és az argumentumok nyitó zárójele közé nem szabad szóközt tenni, illetve – mint minden felsorolás jellegű kódrészletben – a vessző és a következő paraméter közé kell a szóköz. Az utolsó paraméter után közvetlenül jön a zárójel és a kódsor végét jelző pontosvessző.


$var = foo($bar, $baz, $quux);

Ahogy látszik, az egyenlőségjel két oldalán van egy-egy szóköz, ahogy a függvény visszatérési értékét hozzárendeljük a változóhoz. Egymás után több érték változóhoz rendelése esetén több szóközt is beszúrhatunk a kód jobb olvashatósága érdekében:


$short = foo($bar);
$long_variable = foo($baz);

Azok az argumentumok, amelyeknek van alapértelmezett értékük, az argumentum-lista végére kell, hogy kerüljenek. Törekedj értelmes visszatérési érték meghatározására, ha lehetséges.

Tömbök

Tömbelemek egymástól a szokásos felsorolás stílusban (egy vessző, utána szóköz) írandóak. Amennyiben kulcs hozzárendelést alkalmazunk (=>), akkor az operátor elé és mögé is írandó szóköz:

$some_array = array('hello', 'world', 'foo' => 'bar');

Ha egy ilyen tömb-sor hosszabb lenne 80 karakternél (ami elég gyakran előfordul, például űrlap, illetve menü definícióknál), akkor a tömbelemek soronként írandóak, egy behúzással (azaz két szóközzel):


$form['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#size' => 60,
'#maxlength' => 128,
'#description' => t('The title of your node.'),
);

Érdemes megfigyelni, hogy az utolsó sor végén is van egy vessző. Ez nem elírás, ez a jelölésmód segít későbbi feldolgozási hibák elkerülésében, ha bővitjuk a listát.

Idézetek

A kettős, vagy szimpla idézőjelek használatáról nincsen kőbe vésett szabály a Drupalban. Ahol lehetséges, próbálj törekedni az adott modul írásmódjával egybeesően írni: tiszteld a többi programozó egyeni stílusát.
Ugyanakkor érdemes fejben tartani, hogy a szimpla idézőjeleket gyorsabban értelmezi a PHP, mert nem próbál változókat keresni a sorban (például: "

$header

"

). Ezenkívül fordítandó szöveg esetén is ajánlott kettős idézőjelet használni, ha maga a szöveg tartalmazna szimpla idézőjelet (ekkor ugyanis egy az idézőjel elé egy visszaperjel írandó, amit a .pot készítő nem biztos, hogy meg tud oldani).

Sztring összefűzés

Mindig rakj szóközt a pont és az összefűzendő változók, értékek közé, ez nagyban javítja az olvashatóságot.

$string = 'Foo' . $bar;
$string = $bar . 'foo';
$string = bar() . 'foo';
$string = 'foo' . 'bar';
?>

Egyszerű változók esetén szabad dupla idézőjelet használni, és a változót közvetlenül az idézeten belül kiírni:

$string = "Foo $bar";
?>

Ha az összefűzés a saját operátora által ('.=') történik, akkor (mint ahogy korábban írtuk az operátorok esetén) mindkét oldalról egy szóközzel kell közrevenni:

$string .= 'Foo';
$string .= $bar;
$string .= baz();
?>

Megjegyzések

Sorbeli forrás dokumentáció a Doxygen formázási szabályokat követi.
Ajánlott nem dokumentáció jellegű megjegyzésekkel bővíteni a kódot. Nagy általánosságban az olyan programrészletek, amik megírása után az ember legszívesebben zacskót húzna a fejére és elmenekülne dél-Indiába, azonnal dokumentálandóak, mielőtt elfelejti a szerző, hogy mit és hogyan és miért úgy csinált.

Nem dokumentálás jellegű megjegyzéseknek is nyelvtanilag helyes, teljes mondatoknak kell lenniük (nagy betűvel kezdődően, mondat végén pont, mondatok egy szóközzel elválasztva). Csupa nagybetűt csak akkor használunk, ha állandókra (például TRUE) hivatkozunk. Megjegyzések külön sorba írandóak, közvetlenül a vonatkozó kódrészlet elé, például:


// Unselect all other contact categories.
db_query('UPDATE {contact} SET selected = 0');

Ha egy lista elemeihez fűznénk magyarázatot, akkor írhatunk ugyanabba a sorba, sőt be is húzhatjuk, hogy olvashatóbbak legyenek a megjegyzések.
Mind a C-ben használt megjegyzés stílus (/* */) és a C++ féle (//) elfogadott, Perl, illetve shell típusú # megjegyzések kerülendőek.

Kód include-olása

Ahol feltétel nélkül kell egy PHP kódot tartalmazó fájlt meghívni, ott használj require_once()-t. Ahol ez feltételhez van kötve, ott include_once() használatos. Ez a két utasítás közös fájllistát kezel, vagyis ugyanaz a fájl nem lesz kétszer meghívva.

Miután az include_once() es a require_once() nem függvény, hanem nyelvi elem (statement), ezért nem kell a fájlnév köré zárójelet tenni.

Ha ugyanabból a könyvtárból, vagy alkönyvtárból hívunk fájlt, az elérési utat kezdjük "."-vel: include_once ./includes/mymodule_formatting.inc. Drupal 7.x alatt használjuk a DRUPAL_ROOT állandót: require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');

PHP kód jelölők

Mindig a tageket használd PHP kód írásakor, a rövidebb ?> helyett. Ez nem csak Drupal standard miatt van így, hanem a kód hordozhatósága miatt is nagyon fontos, eltérő szerver konfigurációk esetenként tilthatják a rövidebb jelölést.

Pontosvesszők

A PHP nyelv szintaxisa megköveteli a sor végi pontosvesszőt, viszont megengedi annak elhagyását kód blokkok esetén. A Drupal kódolási stílusa viszont még ott is megköveteli:
Helyes:
Helytelen:

Forrás: http://drupal.org/coding-standards
Ez a fordítás közösségi munka eredménye, ha bármi hibát találsz, vagy szebben meg tudnád fogalmazni, akkor segíts javítani.

Legyen saját Drupal API oldalad!

Az új Drupal stabil kiadásához közeledve hasznos lehet, ha az ember újragondolja, átnézi a fejlesztéseit, hiszen a legtöbb esetben mindenképpen módosítani kell az általa kifejlesztett, egyedi funkcionalítást adó modulon, hogy használható legyen a Drupal 6 alatt is. Ilyenkor nagy segítséget jelent az api.drupal.org, ahol könnyen lehet keresni a használható függvények között, megnézni a paraméterezését, vagy a működését.
Viszont nem mindenkinek áll a rendelkezésére az Internet, mint ahogy jómagam is ebben a helyzetben voltam évekig, így szeretném bemutatni az ilyenkor használható hűséges segítőtársamat: az API modult, amelyet teljesen offline működésre fogunk bírni.

A megoldást Linux operációs rendszer alatt teszteltem, és ott is használom, így azokról az esetleges eltérésekről, amelyek a Microsoft Windows alatt jelentkezhetnek (pl. a fájlrendszer elérése), nem tudok nyilatkozni.
Amire szükségünk lesz:

  • Egy feltelepített Drupal rendszerre, mivel erről a kézikönyv is hosszasan ír, ezért inkább itt nem taglalnám
  • Az API modulra, ugyan a modul nem rendelkezik még magyar fordítással, de enélkül is könnyen érthető a felülete
  • Kell még a PHP függvények rövid leírása, amelyet itt fogunk megtalálni, mentsük le funcsummary.txt néven (a download linkre kell kattintani)
  • A minél teljesebb dokumentációhoz szükséges még néhány dokumentáció (pl. Form API), és egyéb példakódok, ezeket a következő paranccsal tudjuk kinyerni a Drupal tárolóból:
    cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib checkout -r DRUPAL-5 contributions/docs/developer
  • Végül pedig szükséges még a PHP dokumentáció, itt fontos, hogy a több fájlból álló változatot válasszuk, amely innen tölthető le magyar nyelven.

Ha ezek megvannak, akkor minden szükséges dolgot letöltöttünk, vigyük haza, és lássunk munkához!

Először is, a webszerver által használt gyökérkönyvtárban hozzunk létre egy alkönyvtárat phpdoc néven, és oda tömörítsük ki a PHP dokumentációt, majd ebben a könyvtárban hozzunk létre egy fájlt .htaccess névvel, amelynek a tartalma legyen a következő:

<IfModule mod_rewrite.c>
    RewriteEngine on
 
    RewriteBase /phpdoc/html
 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} ^(.*)_(.*)$
    RewriteRule ^(.*)_(.*)$ $1-$2
 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^([0-9A-Za-z\-]*)$ function.$1.html [L]
</IfModule>

Ezzel elértük azt, hogy a http://localhost/phpdoc/html/is_array címen megjelenik az is_array PHP függvény leírása, ez amúgy többféle módon is megoldható, érdemes elolvasni a Weblabor kapcsolódó cikkét. Szintén ebbe a phpdoc könyvtárba másoljuk be a korábban lementett funcsummary.txt állományt.

Az api modul bekapcsolása után az adminisztrációs oldal "webhely beállítása" részében megjelent egy új menüpont "API reference" néven, itt tudjuk a modul működését szabályozni.
Az első megadandó dolog a "Short name", ezt majd a címek generálásakor használja a Drupal, így nem tartalmazhat szóközt, vagy olyan karaktert, amely nem megengedett az internet-címekben.
A következő a "Long name", ez lesz a blokk látható neve, fontos szerepe van akkor, ha egyszerre több Drupal dokumentációját, mondjuk a stabil és a fejlesztői változatét kívánjuk elkészíteni.
A "Directory name" helyen kell megadnunk az indexelni kívánt Drupal abszolút elérési útvonalát, akár egyszerre többet is, kettősponttal elválasztva.
A "Maximun files to scan per cron run" helyen állíthatjuk be, hogy az időzített feladatok futása közben egy-egy alkalommal hány fájlt dolgozzon fel. Ezt érdemes az alapértelmezett 10 értéken hagyni, így nem futnak ki folyamatok az engedélyezett időből.
A "Save changes" gombra kattintva elmenthetjük az itt megadott adatokat, így lehetőség nyílik másik telepítés leindexelésére is, majd ha többet is beállítottunk, a "Default branch" rádiógomb alatt lehetséges az alapértelmezettet kiválasztani. Fontos, hogy miután megjelent a "Changes saved." felirat, akkor mégegyszer nyomjuk meg ezt a gombot, mert ekkor menti el a "Default branch" tartalmát, ennek hiánya esetlegesen galibát okozhat.

A "PHP Manual" résznél az első helyre írjuk be a funcsummary.txt elérési helyét: http://localhost/phpdoc/html/funcsummary.txt
A második résznél pedig megadhatjuk PHP dokumentáció helyét a gépünkön, a "!function" karaktersorozattal kiegészítve (ide kerül majd a függvény neve), tehát:
http://localhost/phpdoc/html/!function

Majd ha ezek megvannak, kattintás az "Index PHP manual pages" gombra, és egy kis várakozás. Ha végzett, és mindent helyesen állítottunk be, akkor a "Reindex" gombra kattintva megkezdődik a megadott Drupal rendszerek forrásának feldolgozása.
Most nincs más dolgunk, mint szorgosan futtatni az időzített feladatokat, akár böngészőből:
http://localhost/ahol_van_az_oldalunk/cron.php,

akár konzolból a
wget -O - -q http://localhost/ahol_van_az_oldalunk/cron.php

parancsot futtatva.
Annyiszor kell ezt végrehajtani, amíg a hozzáférési naplóban az api modulhoz tartozóan a "Parsing" kezdetű üzenetek el nem fogynak.
Ezek után már csak egy lépés választ el minket a sikertől:
Az adminisztrációs oldal "Blokkok" részében kapcsoljuk be az "API navigation" és az "API search" blokkokat, majd a főoldalra lépve legyünk boldogok, és válljék egészségünkre a gépünkön üzemelő Drupal API dokumentációs rendszer.

Frissítve (2010.06.08):
Drupal 6 hoz a következő CVS paranccsal lehet kinyerni a dokumentációt:
cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib checkout -r DRUPAL-6--1 contributions/docs/developer

Modulok helye, elnevezése és a hurkok

A Drupal moduljai a modules könyvtár alatt találhatóak. Lehetőség van arra, hogy minden modul közvetlenül ebben a mappában helyezkedjen el, de több fájlból álló modulok esetén célszerű külön alkönyvtárakat létrehozni, hiszen a rendszer azokban is megtalálja a kiegészítőket. A külön alkönyvtár létrehozását egyes kiegészítő modulok telepítési utasításai kifejezetten ajánlják. A későbbi karbantarthatóság érdekében néhányan azt az utat követik, hogy csak az alapmodulokat tartják meg közvetlenül a modules mappában, és a kiegészítőket mindig alkönyvtárakban helyezik el. Így jobban azonosítható a modulok származási helye. Arra is lehetőség van, hogy egy-egy alkönyvtárba egyszerre több modult helyezzünk, a részmodulokból álló kiegészítések is ezt a megközelítést alkalmazzák.

Előkövetelmények

Lássuk, mi szükséges a modul fejlesztés megkezdéséhez:

  • Természetesen nem érdemes saját modul fejlesztésébe fogni, ha nem rendelkezünk adminisztrátori jogosultságokkal egy telepített Drupal rendszeren. Ahhoz, hogy modulokat tudjunk engedélyezni, vagy egy modul beállításait módosítani tudjuk, ilyen jogosultságra szükségünk lesz. Szintén elengedhetetlen, hogy fájlrendszer elérésünk legyen a Drupal telepítéshez, hiszen új illetve módosított modul állományunkat valahogy a rendszer modules mappájába kell juttatnunk.
  • A Drupal moduljai PHP szkriptek, melyeket a rendszer a megfelelő időben betölt, ezért fejlesztésükhöz PHP tudás szükséges. A rendszer fejlettségéhez képest meglepő lehet, de a modulok (és sminkek) készítéséhez objektum-orientált programozási tapasztalat nem szükséges, a Drupal csak adathordozóként használja az objektumokat, viselkedéssel nem ruházza fel azokat. A függvények használata a fejlesztés során természetesen előnyökkel és hátrányokkal is jár. A legfontosabb előnyök: gyors program végrehajtás, könnyű bekapcsolódás a fejlesztésbe, a funkcionalitás egyszerű megosztása különböző fájlok között. A legkomolyabb hátrány a különböző függvényhívások között megőrizhető adatok lehetőségének hiánya, mely problémát azért meg lehet kerülni, és ezt a megoldást a Drupal rendszerben is többször kihasználják.
  • Amennyiben modulunk valamilyen adatbázis kezelési műveletet is igényel, SQL tudásra is szükségünk lehet. Ha szélesebb körben használható modult fejlesztünk, akkor oda kell figyelnünk a szabványos SQL használatára.

Elnevezési szabályok

A modulok állhatnak több fájlból is, de mindenképpen szükséges egy a modul neve szerint elnevezett .module kiterjesztésű fájl, ugyanis ezeket ismeri fel a Drupal modulokként. Amennyiben eseteként betöltendő kiegészítő funkcionalitást is szeretnénk a modulba építeni, akkor azt .inc állományokba helyezhetjük. Példák: story.module, bbcode.module, bbcode-filter.inc. A Drupal telepítésekor beállított .htaccess állomány Apache szerver esetén biztosítja azt, hogy a speciális .module és .inc kiterjesztés védett legyen a webes kiszolgálás szempontjából, azaz, hogy ne tudják böngészőből lekérdezni a webhelyünk forráskódját.

Moduljainkban főleg az állomány kiterjesztés előtti neve alapján elnevezett függvényeket definiálunk, mint story_help, story_page, bbcode_filter, stb. Ezért nagyon fontos, hogy az állomány nevét úgy válasszuk meg, hogy az csak PHP függvénynévben használható karaktereket tartalmazzon. Nem szabad kötőjelet vagy más speciális karaktert használni a modul fájl nevében, mert ilyenek függvénynévben nem szerepelhetnek.

A Drupal 4.7-ben bevezetett speciális fájlok az .install kiterjesztésű telepítést kezelő állományok. Ezek segítségével lehet a modulunkhoz telepítés során lefutó kódot csatolni, így létrehozva a kapcsolódó táblákat, felvéve bizonyos beállításokat.

Hurkok és más függvények

Nagyon ritkán fordulhat az elő, hogy a Drupal forráskódját módosítanunk kell, hiszen a rendszer számos ponton lehetővé teszi a folyamataiba történő beavatkozást az úgynevezett hurkokon (hook) keresztül. Ezek olyan speciálisan elnevezett függvények, melyek szükség esetén meghívódnak. Nekünk nem kell törődni azzal, hogy meghívásra kerüljenek, erről a Drupal gondoskodik. A hurkoknak nem csak a neve, hanem a paraméterezése is kötött, a hurok definiálója adja meg, hogy mi az elvárt paraméter lista. Saját hurok megvalósításainkat is ennek megfelelően kell létrehoznunk.

A hurokfüggvények a dokumentációban hook_menu, hook_block, hook_perm, stb. néven vannak definiálva. A saját hurok megvalósításunknál a nevekben a hook előtagot a modulunk nevére kell cserélni. A fenti példákkal élve story_help és bbcode_perm egy-egy hurok megvalósítás. Néhány fontosabb hurok:

hook_help($section)
A modul leírásának megadására és általában a Drupal rendszer oldalain megjelenő súgók definiálására szolgál. Az adminisztrációs felületek segítő leírásai ezzel adhatóak meg.
hook_menu($may_cache)
A rendszermenübe történő menüpont felvételt teszi lehetővé, valamint a menütől függetlenül biztosítja webcímek függvényekhez rendelhetőségét. A különböző oldalakon megjelenő fülek definiálására is ezt használhatjuk.
hook_block($op = 'list', $delta = 0, $edit = array())
Az oldal blokkjainak összeállításakor hívódik meg, lehetővé téve modulunk által definiált blokk illetve blokkok létrehozását. Az ilyen blokkok teljes értékűek a rendszerben, azaz a hagyományos blokk műveletek elvégezhetőek ratjuk.

Ezek a példák csak ízelítőt adnak a hurkok lehetőségeiből, hiszen sokkal több hurok áll rendelkezésre, melyeket felhasználhatunk, sőt saját modulunk is definiálhat hurkot, melyet más kiegészítések implementálhatnak.

Mivel számos hurok lehetséges, azokat a függvényeket, melyeket nem hurok megvalósításnak szántunk, körültekintően kell elnevezni. Gyakori például a modulneve_page függvény, mely a modul HTML oldalait állítja elő, ez azonban nem hurok, és nem is alkalmazza minden modul ezt az elnevezést az oldalát előállító függvény definiálására. Annak érdekében, hogy elkerülhessük az esetleges név ütközéseket, célszerű a csak belsőleg használt függvények neve elé egy aláhúzást tenni, mint: _story_getcontent vagy _bbcode_filter_process.

A szabályok mentén elnevezett függvényeknek még egy csoportja van, ezek pedig a smink függvények. Nem kötelező smink függvényt definiálnunk, ezek segítségével azonban lehetővé tehetjük, hogy modulunk kimenetének valamely részét sminkeljék – kinézetét megváltoztassák. A smink függvények nevének előtagja meghatározott, mégpedig minden esetben theme_modulneve_. Smink függvény név példák: theme_story_display, theme_bbcode_list.

Forrás dokumentáció

Kézikönyvünkben nem vállalkozhatunk arra, hogy minden egyes hurkot dokumentáljuk, különösen, hogy felhasználásuk egy mintát követ, ezért egyik-másik hurok megismerése után újabbak elsajátítása már igen egyszerű. A Drupal jelentősebb verzióról-verzióra változtatja a hurkok paraméterezését, elvárt működését vagy akár nevét is. Éppen ezért a Drupal forrása gazdagon tűzdelt dokumentációs megjegyzésekkel, melyek a phpdoc formát követik. Ennek keresztreferenciás megtekintéséhez egy külön API modult fejlesztettek ki, melyet mi is telepíthetjük magunknak, hogy közelről böngészhessük az elérhető függvényeket és hurkokat. Azoknak, akik nem szeretnék telepíteni ezt a modult, egy nyilvános felület is elérhető az api.drupal.org címen.

A saját munkánkat is le tudjuk egyszerűsíteni, ha szükség esetén a Drupal által kezelt adatokat a rendszer lekérdező függvényei segítségével szeretnénk megkapni. Az adatbázis függetlenítő, felhasználó kezelő, űrlap generáló, smink kezelő stb. függvényeken túl az egyes modulok is rendelkeznek olyan függvényekkel, amelyeket újra tudunk hasznosítani, meg tudunk hívni, ha adatokra van szükségünk. A hurkokkal lehetőségünk van kiterjesztést fejleszteni, és beépülő komponenseket készíteni, de a hurkokon felül a Drupal függvénykészletének ismerete jelentősen megkönnyíti, hogy gyors, biztonságos és átlátható kódot készítsünk. Az említett forrás dokumentáció ebben is nagy segítségünkre lehet.

Fordítható felület készítése

Saját modul fejlesztésekor hamar felmerülhet a kérdés, hogy egy gyorsan saját célra összerakott kiterjesztést készítünk, avagy szeretnénk azt szélesebb körben is publikálni, visszaadva a nyílt forrású közösségnek valamit abból, amit ajándékba kaptunk. Ha más nyelvi környezetben is használhatóvá szeretnénk tenni a modulunkat, akkor nem árt, ha felkészítjük arra, hogy több nyelven is beszéljen. Ugyanez a probléma merül fel akkor is, ha saját oldalunkon szeretnénk több nyelvet támogatni.

Fordítható felület kialakítására a Drupal két egyszerű függvényt biztosít, és ezeket elegendő ismernünk ahhoz, hogy több nyelvet támogató felületet készítsünk. A t() függvény karaktersorozatok lefordítására szolgál, míg a format_plural() egyes- és többesszámban álló alakok kiírására. Lássunk néhány rövid példát:

// Felület fordítást nem támogatjuk
$message = 'Nem sikerült betölteni a fájlt.';
// Felület fordítást támogatjuk
$message = t('Unable to load file.');
// Fájl nevét ki szeretnénk írni (de itt hibásan) [1]
$message = t("Unable to load file: $filename");
// Fájl nevét ki szeretnénk írni (de itt is hibásan) [2]
$message = t('Unable to load file:') . ' ' . $filename;
// Fájl nevének kiírása helyesen [3]
$message = t('Unable to load file: %filename', array('%filename' => $filename));
?>

A t() tehát egy adott karaktersorozat lefordítására szolgál. Arról a Drupal gondoskodik, hogy a megfelelő nyelvre történjen lefordításra az adott karaktersorozat, ha az adatbázisban rendelkezésre áll hozzá tartozó fordítás. Néhány általános szabály t() fordítások írásához:

  • HTML használatát célszerű kerülni a karaktersorozatokban – kivéve a hosszabb súgó szövegeket, ahol ez elkerülhetetlen.
  • Dinamikusan betöltendő tartalmakat nem karaktersorozat befűzéssel [2] kell megoldani, hiszen ez nem teszi lehetővé a befűzendő tartalom áthelyezését a fordításban, ami szórendi okok miatt szükséges lehet. Változóhelyettesítést használni szintén teljesen céltalan [1], hiszen akkor annyi fordítandó karaktersorozat lesz, amennyi behelyettesített érték előáll, a fenti esetben a lehetőségek száma gyakorlatilag végtelen. Ilyen esetekben tehát a megoldás egy helykitöltő használata [3], mely általában a százalékjellel kezdődik, és aztán néhány karakterben leírja, hogy mi kerül arra a helyre. A t() második paraméterében megadott tömbben kell az egyes helykitöltőkhöz tartozó értékeket megadni. Így lehet a fordítás például: "%filename betöltése nem sikerült"

A format_plural() akkor használatos, ha valamilyen mennyiséget szeretnénk kiírni, melynél a kísérő szavakat másképp kell írni a mennyiségtől függően. Magyar nyelvben csak egy változat van az '1 meggymag', '243 meggymag', stb. megfogalmazására, mennyiségtől függetlenül. Angol nyelvben az egyes szám és többesszám különválik, és vannak olyan nyelvek, ahol sokkal többféle formát használnak. A megfelelő fordítások előállítása persze az adott fordítói csapat dolga lesz, nekünk csak a format_plural() használatával kell tisztában lennünk.

// Megfelelő fordítás előállítása
$seeds = format_plural($number, '1 cherry seed', '%count cherry seeds');
?>

Az első paraméter egy egész szám, a következő kettő pedig az egyes és többesszámú alak. A többesszámú alakban a %count helykitöltőt használhatjuk, ami a szükséges időben a $number értékével helyettesítődik majd – miután a megfelelő fordítást sikerült megtalálni.

Természetesen vannak további függvények, melyek hátterében felület fordítás is áll. Ilyenek például a format_interval() és a format_size(), ezekkel viszont nem kell feltétlenül törődnünk, hiszen a nyelvi szükségleteiket saját hatáskörben megoldják.

Drupal tartalomkezelő magyarországi közössége

Miért válasszam a Drupalt?

Használjon Drupalt a személyes blogtól a vállalati alkalmazásfejlesztésig! Több ezer modulból és megjelenésből válogathat a saját igényei megvalósításához.

A Drupal szabadon felhasználható, megbízható rendszer, melyet több százezren fejlesztenek. Kapcsolódjon hozzánk!

Hogyan induljak el?

Nézze meg a jó példákat, olvassa el a GYIK-ot, böngéssze át a kézikönyvet és a fogalomtárat!

Kapcsolódjon a közösséghez, fogalmazza meg kérdését, és keresse meg rá a választ, vagy írja meg a fórumban!

Hogyan kapcsolódjak?

A Drupal egyik legnagyobb ereje a körülötte lévő közösségben rejlik. E közösség nyitott mindenki számára.

Számos ponton kapcsolódhat. Segíthet a hibák felderítésében, jelzésében, fordításokat készíthet és javíthat, segítséget adhat és kaphat a fórumon.