Tippek és trükkök

Ezeken az oldalakon hosszabb-rövidebb tippeket osztunk meg olvasóinkkal, amik a Drupal megismerését, az arra való fejlesztést segíthetik.

48 nélkülözhetetlen Drupal fejlesztői tipp a Lullabot-tól

Az alábbi tippeket Justin Emond gyűjtötte egy négynapos Lullabot tréningen, melynek fő témái a sminkelés, form API, menu API, modulfejlesztés és jQuery voltak.

  1. Mindig nyomtassuk ki a $body_classes változót a body elem class attribútumaként a sminkünkben. Ezzel számos hasznos osztály válik elérhetővé CSS-ből, pl. "front", "not-front", "logged-in", stb.
  2. Az /admin/build/block oldal az egyetlen olyan admin oldal, ami nem használja az adminisztrációs sminket.
  3. A két leggyakrabban kifelejtett sminkváltozó a page.tpl.php-ben a $closure és a $tabs.
  4. Alapvető sminkelési módszer, hogy a módosítani kívánt sablon file-t előbb átmásoljuk a sminkünkbe az őt definiáló modul könvytárából, majd a másolatot szerkesztjük, pl. node.tpl.php.
  5. A fordítható szövegekben használjunk paramétereket (ún. placeholder tokeneket), mert a fordításban lehet, hogy más lesz a szórend. Pl. 


     $variables['submitted'] = t('On @date', array('@date'=>format_date($variables['created'],'custom','F jS')));
  6. Főverziók közötti frissítéskor legjobb, ha újraírjuk az összes smink függvényt, amiket korábban felülírtunk, így biztosan nem hagyunk ki fontos kódváltoztatást.
  7. Ha nem szeretnénk használni a teljes $content változót a node.tpl.php file-okban, akkor nyugodtan hagyjuk ki. Helyette nyomtassuk ki a szükséges mezőket egyenként.
  8. Teljesítmény: nézeteknél használjuk a "Mezők" sor stílust a "Tartalom" helyett, mivel utóbbi egy-egy node_load() hívást végez a nézet minden egyes sorára, ami egyenként több mint 50 lekérdezés is lehet. A "Mezők" stílus ezzel szemben csak a valóban szükséges adatokat gyűjti össze.
  9. A dpm() függvény a – CakePHP rendkívül hasznos pr() függvényéhez hasonlóan – komplex adatok rendezett megjelenítésére való, mely főként a hibakeresésben nyújt hasznos segítséget.
  10. Saját moduljainkat tegyük egy külön csomagba, így a modul listában jól elkülönülnek majd, továbbá ezzel a későbbi fejlesztők munkáját is megkönnyítjük.
  11. Használjuk a coder modul-t a Drupal 6 és 7 közötti API változások kiderítéséhez.
  12. Hasznos szokás, ha a "$user" változónevet az aktuálisan (tehát a kód futtatásának pillanatában) az oldalra bejelentkezett felhasználó jelölésére használjuk. Az egyéb okból betöltött felhasználót tartalmazó objektum jelölésére használjuk az "$account" változónevet.
  13. Teljesítmény: A teljes variable tábla előre betöltésre kerül minden oldallekéréskor, ezért ügyeljünk rá, hogy mekkora adatot tárolunk benne.
  14. Teljesítmény: A variable_get() hívása nem kerül semmibe (nem nyúl az adatbázishoz), hiszen minden Drupal változó már a memóriában van.
  15. Ne használjuk a t() függvényt a menük "title" és "description" elemein, mert azokat a Drupal cache-ből olvassa be, így a gyorstár felépítésekori nyelv lesz úgyis a mérvadó.
  16. Használjuk a MENU_LOCAL_TASK konstanst a hook_menu()-ben az ún. fülek (tabok)
    létrehozásához, ahogy az a /node vagy /user oldalon is látható.
  17. Teljesítmény: a terjedelmes menü callback függvényeinket tegyük külön .inc file-okba a menü tömb "file" kulcsának használatával. Így hatékonyabb lesz a memóriakezelés, mivel a Drupal a .module file-okat minden oldalletöltéskor memóriába tölti, viszont a .inc file-okat csak amikor szükséges.
  18. A hook_menu()-ben létrehozott útvonalakban a %user és %node jelölők (ún. magic handler-ek) használata esetén a Drupal automatikusan meghívja a node_load() ill. user_load() eljárásait a jelölők helyén álló aktuális ID-vel. Pl. $items['node/%node/edit'] menü útvonal esetén nem kell külön betölteni a $node objektumot a callback függvényünkben.
  19. Az előzőhöz hasonlóan lehetőség van saját magic handlerek használatára is. Pl. %yourown jelölő esetén a Drupal automatikusan meghívja a yourown_load() eljárásunkat mielőtt a hozzá tartozó menü callback-et meghívná. Fontos, hogy ezek a függvények a .module file-ban legyenek.
  20. Saját modulunkban bármikor használhatjuk a 

     $GLOBALS['conf']['cache'] = FALSE;
    utasítást, hogy kikapcsoljuk a Drupal gyorsítótárát az adott oldalon. (Megjegyzés: ha az oldal már a gyorstárban van, akkor ürítsük azt, különben nem fogjuk látni az utasítás hatását.)
  21. Könnyen kideríthetjük egy webhelyről, hogy Drupal alapú-e. Nézzük meg a page expire date-et a szerver által visszaküldött HTTP header-ben. Ha ez 1978. november 19. (a Drupal készítőjének, Dries Buytaert-nak a születésnapja), akkor nagy valószínűséggel az.
  22. Route-olás Drupalban: custom_url_rewrite_inbound() és custom_url_rewrite_outbound().
  23. A megfelelő dátum mezőtípus kiválasztása CCK-ban: 

    - Date (iso date): történelmi és nem teljes dátumok tárolására alkalmas (pl. csak év és hónap).

    - Datestamp (Unix epoch): a Drupal alaprendszer által használt formátummal megegyező dátum formátum.

    - Datetime (ajánlott): natív formában tárolja a dátumot az adatabázisban, tehát így lehetőség van az adatbázis szintjén dolgozni a dátumokkal, ami gyors.
  24. Osszuk fel a sites/all/modules könvytárunkat contrib és custom alkönyvtárakra. A contrib-ba tegyük a drupal.org-ról letöltött kiegészítő modulokat, míg a custom-ba a sajátjainkat.
  25. Ha egy kiegészítő modul kódján változtatnunk kell, akkor ezt mentsük ki egy patch file-ba, így lehetőség lesz a kódváltozások verziókövetésére, valamint a javítás beküldésére. Ezeket a patch-eket egy külön könyvtárba gyűjtsük. Később, ha frissíteni kell a módosított modulokat, ellenőrizzük, hogy továbbra is szükség van-e a módosításainkra. Amennyiben egy patch bekerült a modul hivatalos kiadásába, törölhetjük. Különben egyszerűen alkalmazzuk a patch-et.
  26. Saját moduljainkban a hook_menu() legyen mindig az első függvény, mert hasznos útmutatóként szolgál arról, hogy az adott modul mit csinál és hol érhető el ez.
  27. A form tömbök kulcsai azért kezdődnek kettőskereszttel (#), hogy lehessen akár újabb formokat is definiálni a tömbben.
  28. A Drupal minden form state-hez hozzáad egy "clicked_button" attribútumot, hogy lehetővé tegye a kép típusú űrlap beküldő gombok használatát Internet Explorer-ben is, ugyanis az nem használja a beküldő gomb name attribútumát a beküldő gombokon, ahogy azt a többi böngésző teszi.
  29. Űrlap beküldésekor beágyazott mezőkön a következő módon lehet hibát jelezni: "parent][child". Példa: "home][street", ahol street a mező, home pedig az őt tartalmazó űrlap.
  30. Az előzővel megegyező feladatot lát el a form_error() függvény, azonban tisztább és érthetőbb módon működik, mint a form_set_error(). 


     form_set_error('home][street','You must enter the street address.');
    form_error($form['home']['city'], 'You must enter the street address.');
  31. Ha a $form_state['storage'] tömbelembe bármit is teszünk, akkor a Drupal figyelmen kívül hagy minden korábban definiált átirányítást (ld. $form_state['redirect']), és újraépíti az űrlapot beküldés után. Hogy ezt elkerüljük, töröljük a $form_state['storage'] elemét (ld. php unset() függvény).
  32. Bármilyen HTML-t használhatunk modulunk smink függvényeiben, mert a Drupal sminkek képesek ezt később felülírni.
  33. Űrlap építéskor a Drupal automatikusan lerendereli a $form tömb maradék elemeit, amiket mi nem rendereltünk le korábban. Tehát elegendő csak azokkal foglalkozni, amiket mi magunk szeretnék lekezelni (ld. drupal_render() eljárás).
  34. Ha menet közben külső adatbázisra szeretnénk váltani, használjuk a db_set_active() függvényt a váltáshoz. Ekkor a settings.php-ban megadott kapcsolatok közül választhatunk.
  35. A Table Wizard modul segítségével bármilyen adatbázis táblából készíthetünk saját nézetet, sőt több táblánál megadhatjuk a kulcsokat is, amik alapján össze is kapcsolhatjuk őket (ld. még Migrate modul).
  36. Ha egy űrlapelemnek beállítunk egy értéket (a "#value" kulccsal), akkor a beküldés után mindig az lesz az értéke, független attól amit a felhasználó esetleg beállított az űrlapon.
  37. A "value" típusú űrlap elemek ("#type => "value") nem jutnak el a felhasználóig. Ezek értékét az űrlap adatok között tárolja a Drupal, ahol modulunk függvényéiből érhetjük el őket.
  38. Az inline módon beszúrt JavaScript betöltéséig a böngészők nem töltenek semmi mást, tehát blokkoljuk az oldalletöltést arra az időre. Vigyázzunk ezzel.
  39. A VisualjQuery.com oldalon egy hasznos, vizuális jQuery API-t találunk.
  40. Firebug tipp: A konzol alján található ">>>" szimbólum után saját JavaScript kódot futtathatunk bármely betöltött weboldalon. (Megjegyzés: Drupal oldalakon akár jQuery kódot is, hiszen a jQuery a Drupal része.)
  41. HTML tipp: néhány böngésző kidobja a href attribútum nélküli A tag-eket.
  42. jQuery tipp: class keresése sokkal gyorsabb, ha megadjuk a HTML elem típusát is, mert ekkor a jQuery a böngészők beépített getElementysByTagName() eljárását használja. Pl. 
 

    - Gyors: $('.content');
    - Gyorsabb: $('div.content');
    (Megjegyzés: hasonlóan, ID alapú kiválasztás esetén ne használjunk tag-et, mert akkor nem használja a böngészők beépített getElementNameById() eljárását, helyette végig járja az összes lehetséges tag-et, a megadott ID után kutatva.) 

  43. jQuery tipp: A szelektor függvényekben használt $(this) gyorsabb, mintha újra meghívnánk az aktuális szelektort.
  44. Nézeteink kezelésére egy hasznos módszer, ha kiexportáljuk őket egy saját modulba. Az így kapott php kód verziókezelhető, és védett az oldal felhasználóitól, ugyanis így akár ki is kapcsolhatjuk a Views UI modult. További előnye, hogy bármikor visszatérhetünk a nézetünk egy korábbi verziójára, ha szükséges.
  45. Nagy patch-ek kezelése: készítsünk egy modult, amiben a hook_update() hurkot használva végezzük el a szükséges beállításokat. Ezután frissítsuk a patch-elni kívánt modul kódját, futtassuk az update.php-t, végül alkalmazzuk a patch-et.
  46. Mikor figyeljünk a felhasználótól származó inputtal: általánosságban kijelenthető, hogy a sablon rétegben levő adatok biztonságosak, minden e fölötti szinten már nem. Használjuk a check_plain() eljárást, ha nincs HTML az adatban, és check_markup()-ot, ha pedig van (utóbbi a webhely alapértelmezett beviteli szűrőjét használja).
  47. A Drush make modullal lehetőségünk van modulok, sminkek, telepítési profilok és külső függvény könyvtárak egy listáját előre rögzíteni egy ún. drush make file-ba, majd ezt a file-t egy drush parancsként lefuttatva egy üres Drupal odalt kapunk pillanatok alatt, a megadott kiegészítőkkel. Előnye, hogy nem kell újra és újra ismételgetni az unalmas feladatokat minden egyes új projekt kezdetén.
  48. Használjuk a cache_get() és cache_set() eljárásokat ahol csak lehet, hiszen a gyorstárból jövő adatok jóval kevesebb adatbázis terhelést okoznak.


Jelen írás egy fordított kivonat, melyet zserno készített. Az eredeti itt található:
http://www.missingfeatures.com/2010/02/16/48-essential-drupal-developmen...

Köszönet drifternek, hogy megmutatta az eredeti cikket twitteren: http://twitter.com/drifter/status/13919347596!

Ha bármi hasznos tipped van amit nem találsz a fenti listában, kérlek írd meg hozzászólásban.

A Drupal menürendszere

A hook_menu() kampó megvalósításai elérési címeket jegyeznek be, megadva azok kezelőfüggvényeit, jogosultság értékeit, és megjelenítési adataikat.

Paraméterek

$may_cache Logikai érték, mely azt jelzi, hogy a visszaadott elemeink a gyorsítótárba kerülnek-e. A menü gyorsítótára felhasználófüggő, tehát az elemeket szinte mindig gyorsítótárazhatjuk, kivéve ha a felhasználó aktuális helyzetétől függnek. A node_menu() megvalósításában láthatunk példát olyan elemre, amit nem szabad gyorsítótárba tenni.

Visszatérési érték

Elérési címek tömbje. Minden elem egy asszociatív tömb, ami a következő kulcs-érték párokat tartalmazhatja:

  • "path": kötelező. Az az elérési cím, melyhez a további jellemzők tartoznak.
  • "title": kötelező. Az elérési címhez tartozó felirat. Ez jelenik meg a menüben illetve a fülön, valamint az ezen a címen előállított oldalnak is ez lesz az alapértelmezett címfelirata.
  • "callback": A címhez rendelt kezelőfüggvény. Ha nem addjuk meg, a szülő menüelem kezelőfüggvénye kerül majd meghívásra.
  • "callback arguments": Tömb, melyben a kezelőfüggvénynek átadandó paraméterek vannak.
  • "access": Logikai érték. A felhasználó erre a címre vonatkozó elérési jogosultságát adja meg. Általában a user_access() meghívásának segítségével dől el. Ha ez és a "callback" is hiányzik, akkor a szülő által meghatározott jogosultság érvényes.
  • "weight": Egész szám. A megjelenő menüelemek helyzetét dönti el, a nehezebbek lesüllyednek. Az alapértelmezés 0. Ha nem vagyunk biztosak magunkban, akkor ezt ne addjuk meg. Az alapértelmezett alfabetikus sorrend sokszor megfelelő.
  • "type": Jelzőkből álló bitmaszk. A menüelem tulajdonságait írja le. Sokféle bitmaszkhoz van rövidítésnek konstans definiálva a menu.inc fájlban:
    • MENU_NORMAL_ITEM: Közönséges menüelem, ami megjelenik a navigációs menüben. Az adminisztrátor mozgathatja vagy elrejtheti a menü modullal.
    • MENU_ITEM_GROUPING: Az elemcsoportokat olyan oldalak használják, mint a tartalom beküldése, amelyek egyszerűen aloldalakat sorolnak fel.
    • MENU_CALLBACK: Olyan elérési címet hoz létre, mely nem jelenik meg a menüben, de meghívásakor teljes értékű elérési címként viselkedik.
    • MENU_DYNAMIC_ITEM: A dinamikus menüelemek gyakorta változnak, és nem szabad eltárolni őket a testreszabást lehetővé tevő menü modul számára.
    • MENU_SUGGESTED_ITEM: Az ilyen menüpontok alapértelmezésben nem jelennek meg, de a menü modullal láthatóvá tehetőek.
    • MENU_LOCAL_TASK: A helyi feladatok fülként jelennek meg alapértelmezésben.
    • MENU_DEFAULT_LOCAL_TASK: Minden helyi feladatcsoporthoz kötelezően tartozik egy alapértelmezett feladat. Különlegessége, hogy nem az aktuális, hanem a szülő útvonalára vezet.

    Ha nem adjuk meg a típust, akkor a rendszer a MENU_NORMAL_ITEM érteket feltételezi.

A hook_form_alter() a gyakorlatban

A Drupal.hu frissítésével merült fel az igény arra, hogy bizonyos a fejlesztői csapat által kevésbé lényegesnek ítélt node szerkesztő mezőknek mégis nagyobb jelentőséget tulajdonsítsunk a felület megjelenítésekor, így a Drupal 4.7.0-val érkezett új tömbökre épülő űrlap építő rendszer egyik előnyös tulajdonságát, a hook_form_alter() űrlap módosító hurok képességeit kellett igénybe vennem. Ráadásul ezt Őry Máté fordítói levlistára beküldött magyar dátumokat támogató megoldásával fűszereztem, így egy kis ismertető alapja éppen összeállt.

A Drupal 4.7.0-ás kiadásának legnagyobb fejlesztői változása a teljesen átalakított űrlap összeállításért és ellenőrzésért felelős rendszer, mely a korábbi Drupal verziókkal összehasonlítva valamelyest bonyolutabb, de ugyanakkor sokkal rugalmasabb programozást tesz lehetővé. Megtehetjük például, hogy meglévő űrlapokba új elemeket veszünk fel, beviteli mezők csoportjait sorrendezünk át, és hasonlók.

Lássuk, hogy ezen írás születésekor mit teszünk a drupalhu.module kódjában:

// A drupalhu modul weight értéke nagyra
// van állítva az adatbázsiban, ezért ez
// hívódik meg legutoljára a form_alter()-ek közül
function drupalhu_form_alter($form_id, &$form) {
// Node form átírása
if (preg_match("!_node_form$!", $form_id)) {
// Node típusok a path és options megnyitásához
$openpath = in_array($form['type']['#value'], array('story', 'book', 'page'));
$openoptions = $openpath || $form['type']['#value'] == 'weblink';

// Opciók megnyitása
if (isset($form['options']) && $openoptions) {
$form['options']['#collapsed'] = FALSE;
}
// Álnév kötelező vagy nem látszik
if (isset($form['path'])) {
if ($openpath) {
$form['path']['#collapsed'] = FALSE;
$form['path']['#weight'] = -2;
$form['path']['path']['#title'] = 'Az útvonal álneve';
$form['path']['path']['#required'] = TRUE;
}
else {
unset($form['path']);
}
}
}
// Magyar dátum megjelenítéshez a beállítás
// űrlap átírása Őry Máté kódja alapján
if ($form_id == 'system_settings_form') {
$hu_formats = array(
'short' => 'Y. m. d. H.i',
'medium' => 'Y. F j. H.i',
'long' => 'Y. F j., l H.i'
);
foreach ($hu_formats as $n => $f) {
$form['dates']['date_format_' . $n]['#options'][$f] = format_date(time(), 'custom', $f) . ' (magyar)';
$form['dates']['date_format_' . $n]['#default_value'] = variable_get('date_format_' . $n, $f);
}
}
}
?>

Mikor fut le a drupalhu_form_alter()?

Mindig lefut egyszer, amikor egy űrlap összeállítása során módosíthatjuk annak tartalmát, szerkezetét. Számunkra azonban nem mindegy, hogy más módosító függvényekhez képest mikor hívódik meg saját függvényünk. Mint a függvény előtti megjegyzés is mutatja, fontos, hogy meggyőződjünk róla, hogy a drupalhu_form_alter() függvényünk eléggé későn hívódik meg. A node szerkesztő űrlap 'path' eleme ugyanis szintén egy form_alter megvalósításon keresztül kerül az űrlapra, és ha alfabetikus sorrendben futnának le a modulok, akkor itt még nem látnánk azt az elemet, így nem is tudnánk befolyásolni. A meghívási sorrendet az adatbázis system táblájának weight értékével szabályozhatjuk. A nagyobb súlyú modulok hurkai később hívódnak meg, mint a kisebb súlyúaké.

A módosítandó űrlap kiválasztása

Két űrlapot fogunk módosítani a rendszerben, ezeket a $form_id alapjánt tudjuk azonosítani. A második eset egyszerűbb, hiszen a system_settings_form egyértelműen azonosítja a rendszer beállítások űrlapját. Az első esetben azonban egy story_node_form típusú űrlap azonosítót fogunk kapni, tehát a node típus is szerepel az azonosítóban. Ezért én egy mintaillesztő kifejezést választottam a megfelelő végződés megállapítására. A path_form_alter() egy másik megoldást is mutat ugyanerre a kérdésre.

Node szerkesztő felület testreszabása

Tekintsük az első űrlap módosítást, ahol tehát a node szerkesztő űrlap tulajdonságait változtatjuk meg. Az, hogy adott esetben milyen tömb struktúrán tudunk módosításokat végezni részben a node_form_array(), részben pedig a path_form_alter() függvényből tudjuk kideríteni.

Először is ha a szerkesztőség által jóváhagyandó tartalommal van dolgunk, és már jelen van az adminisztrátorok számára űrlapba tett 'options' mezőcsoport, amelyben a közzétételre, kiemelésre és más általános lehetőségekre vonatkozó beállítások találhatóak, akkor ezt kinyitjuk. Másodszor pedig a webcím álnév beállító csoportját emeljük ki az űrlap elejére, a taxonómia beállítás utánra azoknál a tartalmaknál, melyekhez álnév szokott tartozni. Ráadásul az elemcsoportban kötelezővé tesszük az álnév megadására szolgáló elem kitöltését. Ezzel nem kerülhet álnév nélküli szerkesztett tartalom a rendszerbe. Azért kell címet adnunk az elemnek, mert ezt használja fel a Drupal a hibaüzenet kiírásakor, és emellett jelenhet meg a piros csillag, ami a kötelező kitöltést jelzi. Végül amely típusoknál nem használunk álneveket (weblink és forum a drupal.hu esetében), ott ennek a beállítási lehetőségét el is tüntetjük.

Magyar weblapra magyar dátumokat

Őry Máté küldte be nemrég a fordítói levelezőlistára megoldását, mely szintén a form_alter hurkot használja arra, hogy lehetővé tegye helyes magyar dátum formátumok kiválasztását a rendszer beállításainál. Ennek egy jelentősen rövidített, ám funkcionalitásában többet adó megoldását adtam hozzá a drupalhu.module kódjához.

Tehát ha a system_settings_form űrlappal van dolgunk, akkor ennek dátumokra vonatkozó lehetőségeit szeretnénk kibővíteni a magyar formátum választhatóságával. A módosítható elemek a system_view_general() függvényben azonosíthatóak.

A megoldás elérése érdekében felvesszük a három lehetséges dátum részletezettségnek (short, medium, long) megfelelő magyar formátumokat egy tömbbe. A system_view_general kódjából tudjuk, hogy $form['dates']['date_format_short'] és hasonló nevű űrlap mezők teszik lehetővé a dátum kiválasztását. Ezek választási lehetőségeihez felvesszük a magyar formátumokat, sőt oda is írjuk a minták végére, hogy ezek a helyes magyar dátum formátumok, ugyanis sokan nem tudják például, hogy a magyar idő kijelzésben az óra és perc számait pont választja el egymástól. Végül pedig beállítjuk ezeket a formátumokat alapértelmezésnek, tehát ha még nem állítottak be semmit, akkor a rendszer ezt ajánlja fel.

Fontos megjegyezni, hogy ettől a változtatástól függetlenül a dátumok alapértelmezése nem a magyar formátum lesz a rendszerben, hiszen a máshol használt variable_get() hívásoknak más alapértelmezése van. Ezért vagy az adminisztrációs felületen kell beállítanunk a most már rendelkezésre álló magyar dátum formátumot, vagy az adatbázisban kell átszerkesztenünk. A fenti kódnak köszönhetően a felületen egyszerűbb dolgunk lesz.

Zárszó

A fenti két példa remélhetőleg megmutatta, hogy a Drupal 4.7-es sorozat új űrlap összeállítási rendszere megkönnyíti az életünket, hiszen saját fejlesztéseinket az alap modulok módosítása helyett elkülönített függvényekbe helyezhetjük, megkönnyítve a későbbi hibakeresést és frissítést is.

Amikor változtatni kell a Drupal kódján (1)

Májusban egy Drupal without modifications című szál kapott erőre a Drupal fejlesztői levelezőlistán, mely arról is szólt, hogy szükséges-e módosításokat végrehajtanunk a Drupal alap kódján ahhoz, hogy a kívánt webhelyet megkapjuk. Ez természetesen azzal a komoly igazsággal zárult, hogy "attól függ". A Drupal.hu kialakításakor például szigorú vezérelv volt, hogy alap telepítést használjunk, csak modulokkal kiegészítve a rendszert. Így ahelyett, hogy varázslatokat művelnénk, a maga valóságában tudjuk bemutatni a Drupal rendszert. A módosítások elkerülése sok esetben működik, de nem minden esetben tartható.

A jelenleg Drupal 4.6 alapú Weblabor.hu esetében például nem tudtunk megoldani néhány dolgot anélkül, hogy a Drupal alapmotor forráskódon változtattunk volna. Minden esetben igyekeztünk átgondolni, hogy szükségünk van-e egy-egy változtatásra, és időről-időre visszatérünk ezekre a kérdésekre. Éppen egy ilyen nemrég megvalósult "visszatérés" ihlette ezt a kis tippet.

Dokumentáljuk a változásokat

Ha mégis módosítanunk kell valamit, minden esetben érdemes a változtatásokat a kódban dokumentálni. Mi erre a célra a +WL: megjegyzés előtagot választottuk, mely jól kereshető a kódban. A több soros változtatásoknál hosszabban írjuk le, hogy miért nyúltunk a kódba, de mindig egy sorba tesszük a megjegyzést, mert így később programozottan is egyszerűen kigyűjthető. Egy valós példa a Weblabor kódból:

// +WL: máshol jelenítjük meg az XML ikont
// $output .= theme('xml_icon', url('taxonomy/term/$tid/0/feed'));
?>

A kód közvetlen módosítása

A kód módosításának egyik útja, hogy közvetlenül a meglévő forrást szerkesztjük. Ezt főleg kisebb változtatások esetén alkalmazzuk, amikor csak 1-5 sort kell módosítani, vagy hozzáadni. Néhány érdekes változtatás, amit meg kellett tennünk:

  • A contact.module kódjában a szerkesztőség CC-zésének lehetősége. Pár sor változtatást igényelt.
  • Változtatható karakter kódolás az RSS csatornához. A node modul módosítását igényelte.
  • Hozzászólás űrlap megjelenítése csak azokon az oldalakon, ahol még nincs hozzászólás (különben a szálazás érdekében a válasz linkekre irányítjuk a figyelmet). Néhány karakteres módosítást kellett tennünk.

Érdekes, hogy ezek és még számos változtatásunk sokszor az űrlapokat érintik, amik a Drupal 4.7-estől kezdve bevezetett sokkal rugalmasabb tömb alapú megoldással már sokkal kevésbé igénylik majd a kód módosítását.

Számos kifejezetten Weblabor specifikus függvényt kiszerveztünk egy wllib.inc nevű fájlba, melyet a sites/default/settings.php fájlból töltünk be, így elkerülve, hogy emiatt módosítani kelljen a Drupal kódját. Így a módosításainkat kis méretűen tudjuk tartani, legalábbis, amikor további kódot kell hívni.

Meglévő viselkedés helyettesítése

A fenti rövid kódpélda esetében is érdemes elgondolkodni, hogy egyáltalán bárhol is szükségünk van-e az xml_icon megjelenítésére. Ha nincs, akkor természetesen a theme_xml_icon()-nak megfelelő saját smink függvényünket kell elkészíteni, üres visszatérési értékkel. Ez a Weblabor környezetében az azigazi_xml_icon() nevű függvény lenne. Esetünkben viszont úgy gondoltuk, hogy nem akarjuk teljesen eltávolítani ezt a rendszerből (bár valószínű ez a döntés is megérett a felülvizsgálatra).

Vannak azonban olyan helyzetek, amiket nem lehet egyszerű kód módosítással kezelni, és a smink függvényekhez hasonló felülíró mechanizmust sem kapunk, hanem jobban járunk, ha egy függvényt máshol definiálunk. Így alakult, hogy a drupal_get_path_map(), drupal_get_path_alias(), drupal_get_normal_path(), és a drupal_rebuild_path_map() függvényeket a kódban egyszerűen megjegyzésbe tettük, a +WL: magyarázattal ellátva, és a wllib.inc-ben valósítjuk meg a működésüket. Sajnos eléggé összetett álnév számítási rendszert fejlesztettünk ki, ragaszkodva a lehetőleg magyar webcímek használatához. Ez nagyon komoly teljesítmény problémákat okozott a 4.6-os álnév rendszerbe épüléssel, ezért a tesztjeink arra mutattak, hogy jobban tesszük, ha saját céljainkra optimalizált megvalósítást teszünk a kódba. Ráadásul emiatt több kisebb változtatást is el kellett végeznünk más modulokban is.

A változtatásokat kezelni kell

Az embernek könnyen "eljár a keze", akár meggondolatlanul is képes változtatásokat vinni a rendszerbe, ha szükségét látja. Sajnos ez gondot okozhat a későbbi frissítéskor, amin a speciális megjegyzések sem segítenek, ha egyszerűen csak felülmásoljuk a kódot. A következő részben azt szeretném leírni, hogy a fenti változtatások megtartása mellett hogyan tudjuk mégis folyamatosan frissíteni a Weblabor mögött működő Drupal rendszert.

Amikor változtatni kell a Drupal kódján (2)

Érdekes módon éppen a változtatásokról szóló tipp-kettős előző részének megjelenése napján vált elérhetővé a Lullabot Podcast tizenhatodik része, melyben Jeff Robins készít interjút Dries Buytaert-tel, a Drupal alapítójával. Ebben hangzik el a következő párbeszéd:

Jeff Robins: Don't hack Drupal, if you are hacking the code, you are doing something wrong.
Dries Buytaert: Either you are doing something wrong, or core needs to be extended.
Jeff Robins: [...] but then some security update comes out, or a new version of Drupal comes out, you can't upgrade, because your hacks will break everything.

Most arra a kérdésre próbálom megadni a választ, hogy mit tehetünk, ha mindenképpen saját módosításokat kell alkalmaznunk, de ezeket a frissítésekkel is meg szeretnénk tartani. A megoldás természetesen nem olyan egyszerű, mint egy klasszikus Drupal frissítés, de aki módosít a kódon, annak ezt vállalnia kell.

Melyik Drupal verziót használjuk?

Ha a legújabb hibajavításokkal szeretnénk élni, akkor könnyen lehet, hogy elvesztjük a biztos tudatunkat arról, hogy mégis melyik Drupal verziót használjuk. A Weblabor esetében például jelenleg úgy foghatjuk meg a használt verziót, hogy a Drupal 4.6-os ágon valamilyen dátummal bezárólag aktuálisak a fájljaink. A szükség esetén megjelenő 4.6.x-es kiadások követése mellett a folyamatos hibajavításokat is érdemesnek tartjuk alkalmazni, így valószínű, hogy amit használunk, annak nincs is hivatalos verziószáma. Még ha lenne is, akkor sem lehet megállapítani a fájlok alapján, hogy pontosan melyik verzióval állunk szemben (hacsak nem vezetjük következetesen egy jegyzetben, hogy melyik verzióra frissítettünk legutóbb).

Így kell egy eszköz, ami megmondja, hogy milyen dátumból kell kiindulni. Végig kell nézni minden fájlt, amiben a fejlesztői (CVS) verziónak megfelelő információ található, beleértve a Drupal rendszerbeli legutóbbi módosítás dátumát is. Ezt magunk is megtehetjük, de az alábbi saját fejlesztésű szkript automatizálja a feladatot:

// Grab all files with possible CVS Id data
$files = find_drupal_files();

$cvsids = array();
$latest = '';

// Go over the files and collect CVS Id data
foreach ($files as $filename) {
$content = join('', file($filename));
if (preg_match('!\\$Id: ([^\\$]+) Exp \\$!', $content, $found)) {
$cvsids[$filename] = $found[1];
if (preg_match('!\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}!', $found[1], $date)) {
if (strtotime($date[0]) > strtotime($latest)) {
$latest = $date[0];
}
}
}
else {
$cvsids[$filename] = 'n/a';
}
}
print "LATEST;$latest\n";
foreach($cvsids as $name => $id) {
print substr($name, 2) . ";$id\n";
}

// Recursively go over possible Drupal files
function find_drupal_files($root = '.') {
$folders = glob($root . '/*', GLOB_ONLYDIR);
$files = glob($root . '/*.{php,inc,txt,module,theme,engine,mysql,pgsql,install}', GLOB_BRACE);
if (file_exists($root . '/.htaccess')) {
$files[] = $root . '/.htaccess';
}
foreach($folders as $folder) {
if (!in_array($folder, array('.', '..'))) {
$files = array_merge($files, find_drupal_files($folder));
}
}
return $files;
}
?>

Ezt a Drupal gyökerébe téve, és onnan lefuttatva megkapjuk CSV formában a fájlok verzió azonosítóit, és legutóbbi fejlesztői módosítási dátumait. A szkript megkísérli a legutóbbi dátumot kiválasztani. Ebben azonban nem szabad vakon bízni. Ha kiegészítő modulokat is telepítettünk, érdemes azoktól eltekinteni a legutóbbi dátum megállapításakor. Hiszen valószínű az alaprendszert és a kiegészítőket külön frissítjük, illetve lehet, hogy egy kiegészítő jóval későbbi dátummal rendelkezik, mint amikor az alaprendszert legutóbb frissítettük. Ez indokolja a CSV formátumot, amit így tudunk importálni kedvenc táblázatkezelőnkbe, és szűrhetjük az elemeket, sorrendezhetünk, és könnyedén kiválaszthatjuk a legutóbbi frissítés dátumát.

Így kiderítettük, hogy mikori a Drupal rendszerünk.

Változáskövetés

Követnünk kell a változtatásainkat. Erre a legkézenfekvőbb eszköz egy saját célra telepített Subversion, CVS vagy más változáskezelő rendszer. Ha eléggé szerencsések vagyunk, hogy egy ilyet az eszköztárunkban tudhatunk, akkor már félig megoldottuk a problémát. Ezzel persze csak a saját módosításainkat követhetjük, illetve a frissítések fájlokra tett hatását ellenőrizhetjük.

Feltételezem viszont, hogy olvasóimnak nincs verziókezelő rendszer a tarsolyában, és szerencsére a változáskövetés esetünkben enélkül is megvalósítható. A Drupal.org CVS szervere ugyanis segítségünkre van mindenben. A megállapított dátumnak megfelelő kódot kell először is letöltenünk a szerverről. Attól függően, hogy milyen CVS klienst használunk, ez más-más módon történik. Én parancssori klienst alkalmaztam. Tegyük fel, hogy a megállapított dátumunk 2005. december 13. és a Drupal 4.6-os kódra van szükségünk. Az alábbi parancs segítségével kaphatjuk meg.


$ cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal co -r DRUPAL-4-6 -D "2005-12-13" drupal

Ezzel kaptunk egy tiszta módosítatlan Drupal forráskódot. Most nincs más dolgunk, mint megállapítani a saját Drupal kódunk, és a tiszta Drupal kód eltéréseit, hogy a változtatásainkat egyértelműen azonosíthassuk. Erre a célra én a Meld nevű programot használtam, de nagyon hasonlóan használható a WinMerge is Windows rendszert alkalmazóknak.

Most hogy tételesen látjuk a változtatásainkat, érdemes egy pillanatra végiggondolni mindegyiknek a létjogosultságát, és amennyiben megtartásuk mellett döntünk, dokumentáljuk mindegyiket az előző részben bemutatott konvenciók szerint. Ez még nagyon meghálálja magát a későbbi kódfrissítéseknél.

Frissítsünk

A változtatásaink azonosítása számos előnnyel jár, de leginkább azért van rá szükség, hogy megkezdhessük a kódfrissítést. Készítsünk egy másolatot az előbbi tiszta Drupal verzióról az összes extra CVS könyvtárral együtt. Ennek a másolatnak a fájljait írjuk felül a saját módosított fájljainkkal. Ezek után futtassuk le a CVS frissítő parancsát továbbra is a 4.6-os ágon maradva, de kérve a legaktuálisabb fájlokat:


$ cvs update -r DRUPAL-4-6

A megjelenő üzenetek között a C jelű fájlokra kell különösen figyelmet fordítanunk, mert ezekben a saját módosításainkat és a Drupal forráskód módosításait a CVS nem tudta összefésülni, és mindkét verziót berakta a fájlba speciális jelzésekkel. Nyissuk meg ezeket a fájlokat bármilyen szerkesztő programban és eseti jelleggel magunk döntsük el, hogy mi lesz a helyes új kód. Ha minden ilyen konfliktust megoldottuk, futtassunk le még egy ilyen update parancsot, és figyeljük meg, hogy csak M jelű fájlunk maradt-e. Ezek az általunk módosított fájlok, amik a frissítéssel összefésülve megtartották a változtatásainkat.

Még nem vagyunk készen

Mivel a kódfrissítés és összefésülés elkészült, egyszerűen felülírhatnánk a webhelyünk forráskódját az új kóddal. Én azonban azt javaslom, hogy ne siessük el, hanem vegyük elő a fent használt különbség vizsgáló programunkat, és ezúttal a frissült kód és a webhelyünk forráskódja között tekintsük át a változtatásokat. Így látni fogjuk, hogy milyen módosítások kerültek a Drupal rendszerbe. Ezek egyrészt adott esetben sminkünk vagy kiegészítő modulunk működésének javítását is szükségessé tehetik. Másrészt pedig ha valami elromlik a friss kód gyakorlatba ültetése után, akkor több tippünk lesz, hogy mégis mi mehetett tönkre, mi változott egyáltalán.

Biztonsági mentés

Végül pedig, mielőtt az új kódot hadrendbe állítanánk, ne felejtsünk el biztonsági mentést készíteni a webhelyünk forráskódjáról és az adatbázisról. Ha valamit elszúrtunk volna, akkor arra még mindig visszatérhetünk.

Calendar modul, listázás az esemény időpontja alapján

Sokan használják a Date és a Calendar modulokat, hogy Views segítségével listázhassák a beküldött naptárbejegyzéseket.

A Calendar modul bekapcsolása után a Views listában megjelenik a „calendar” nézet. Engedélyezés után alapértelmezetten a „weboldalneve.hu/calendar” útvonalon érhető el. Meglepő módon mintegy archívum működik, és a tartalmak létrehozásának/utolsó módosításának dátuma szerint rendezi, jeleníti meg azokat. Ha jobban belegondolunk ez jogos is, hiszen a modul nem tudhatja, hogy mi milyen nevet fogunk adni a Date típusú mezőnknek.

Az eseménynaptár kialakításának a helyes módja

Akkor nézzük az elejétől.

  1. Hozzunk létre egy új mezőt a kívánt tartalom típusunkban, mondjuk „Date” névvel.
  2. Vigyünk fel pár új tartalmat (node-ot), kitöltött dátummal mezővel.
  3. Kapcsoljuk be a Calendar modult.
  4. Engedélyezzük a Views-ban a „calendar” nézetet, készítsünk egy másolatot róla és azt nyissuk meg. Mindenhol a „Default” nézetet kell szerkeszteni! Az eredeti „calendar” nézetet kapcsoljuk ki/tiltsuk le, mert így könnyen összetéveszthető lesz majd az új blokk a régivel (ami ugye nem azt fogja mutatni, amit mi szeretnénk).
  5. A „Default” nézet „Mezők” részében adjuk hozzá (+) a „Tartalom: Dátum - (field_date)” mezőt.
    Megjegyzés: amennyiben a tartalom típus date mezőjénél engedélyeztük a „To Date” funkciót (befejezés dátumát), akkor a Views listájában ezek a mezők külön-külön jelennek meg:
    „Dátum (field_date) - From date” és „Tartalom: Dátum (field_date) - To date”. Normál esetben az elsőt kell választani.
  6. Az „Arguments” részben kattintsunk a „Dátum: Date (node) Tartalom: Updated date” linkre, és szerkesztés módban válasszuk a „Törlés” gombot.
  7. A „Default” nézet „Arguments” részében adjuk hozzá újként (+) a „Dátum: Date (node)” mezőt.
    Beállítások:
    • „Action to take if argument is not present: Provide default argument”
    • „Default argument type: Current date”
    • A „Date field(s):” részben válasszuk ki a „Tartalom: Dátum - (field_date)” sort, és kattintsunk a „Frissítés” gombra.
  8. A „Default” nézet „Mentése”.

Most már (elvileg) ha a „weboldalneve.hu/calendar” nézetben nem a node utolsó módosításának ideje alapján, hanem a „Date” mezőben megadott dátum alapján rendezi sorba a tartalmakat.

Amúgy kicsit bugos pont ez a rész, fontos a sorrend betartása. Erről a hibáról bővebben az alábbi linken olvashatunk:
http://drupal.org/node/350279#comment-1223870

Ha „The calendar_nav style requires a Date argument.” hibaüzenetet kapunk, akkor rossz sorrendben csináltuk... ;) Kezdjük újból!

Én szépen bele is futottam, amikor próbálgattam a nézetet átalakítani, hogy elkészülhessen ez a leírás.

A Tippek és trükkök számára a cikk alapjául a Calendar modul fórum téma szolgált.

Palócz „Paal” Pál

Views export

Segítségként kiexportáltam a nézetet. Csak abban az esetben működik, ha a CCK/Date mezőt - a fenti leírásnak megfelelően - Date-nek nevezzük el.

$view = new view;
$view->name = 'calendar';
$view->description = 'A multi-dimensional calendar view with back/next navigation.';
$view->tag = 'Calendar';
$view->view_php = '';
$view->base_table = 'node';
$view->is_cacheable = FALSE;
$view->api_version = 2;
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
$handler = $view->new_display('default', 'Defaults', 'default');
$handler->override_option('fields', array(
  'title' => array(
    'label' => '',
    'link_to_node' => 1,
    'exclude' => 0,
    'id' => 'title',
    'field' => 'title',
    'table' => 'node',
    'relationship' => 'none',
  ),
  'field_date_value' => array(
    'label' => 'Date',
    'alter' => array(
      'alter_text' => 0,
      'text' => '',
      'make_link' => 0,
      'path' => '',
      'link_class' => '',
      'alt' => '',
      'prefix' => '',
      'suffix' => '',
      'help' => '',
      'trim' => 0,
      'max_length' => '',
      'word_boundary' => 1,
      'ellipsis' => 1,
      'strip_tags' => 0,
      'html' => 0,
    ),
    'link_to_node' => 0,
    'label_type' => 'widget',
    'format' => 'default',
    'multiple' => array(
      'multiple_number' => '',
      'multiple_from' => '',
      'multiple_to' => '',
      'group' => TRUE,
    ),
    'repeat' => array(
      'show_repeat_rule' => '',
    ),
    'fromto' => array(
      'fromto' => 'both',
    ),
    'exclude' => 0,
    'id' => 'field_date_value',
    'table' => 'node_data_field_date',
    'field' => 'field_date_value',
    'relationship' => 'none',
  ),
));
$handler->override_option('arguments', array(
  'date_argument' => array(
    'default_action' => 'default',
    'style_plugin' => 'default_summary',
    'style_options' => array(),
    'wildcard' => 'all',
    'wildcard_substitution' => 'Minden',
    'title' => '',
    'breadcrumb' => '',
    'default_argument_type' => 'date',
    'default_argument' => '',
    'validate_type' => 'none',
    'validate_fail' => 'not found',
    'date_fields' => array(
      'node_data_field_date.field_date_value' => 'node_data_field_date.field_date_value',
    ),
    'year_range' => '-3:+3',
    'date_method' => 'OR',
    'granularity' => 'month',
    'id' => 'date_argument',
    'table' => 'node',
    'field' => 'date_argument',
    'validate_user_argument_type' => 'uid',
    'validate_user_roles' => array(
      '2' => 0,
    ),
    'relationship' => 'none',
    'default_options_div_prefix' => '',
    'default_argument_user' => 0,
    'default_argument_fixed' => '',
    'default_argument_php' => '',
    'validate_argument_node_type' => array(
      'merci_reservation' => 0,
      'merci_reservation_template' => 0,
      'book' => 0,
      'page' => 0,
      'picture' => 0,
      'story' => 0,
    ),
    'validate_argument_node_access' => 0,
    'validate_argument_nid_type' => 'nid',
    'validate_argument_vocabulary' => array(),
    'validate_argument_type' => 'tid',
    'validate_argument_transform' => 0,
    'validate_user_restrict_roles' => 0,
    'validate_argument_php' => '',
  ),
));
$handler->override_option('filters', array(
  'status' => array(
    'operator' => '=',
    'value' => 1,
    'group' => '0',
    'exposed' => FALSE,
    'expose' => array(
      'operator' => FALSE,
      'label' => '',
    ),
    'id' => 'status',
    'table' => 'node',
    'field' => 'status',
    'relationship' => 'none',
  ),
));
$handler->override_option('access', array(
  'type' => 'none',
  'role' => array(),
  'perm' => '',
));
$handler->override_option('cache', array(
  'type' => 'none',
));
$handler->override_option('title', 'Calendar');
$handler->override_option('header_empty', 1);
$handler->override_option('items_per_page', 0);
$handler->override_option('use_more', 0);
$handler->override_option('style_plugin', 'calendar_nav');
$handler = $view->new_display('calendar', 'Calendar page', 'calendar_1');
$handler->override_option('path', 'calendar');
$handler->override_option('menu', array(
  'type' => 'none',
  'title' => '',
  'description' => '',
  'weight' => 0,
  'name' => 'navigation',
));
$handler->override_option('tab_options', array(
  'type' => 'none',
  'title' => '',
  'description' => '',
  'weight' => 0,
));
$handler->override_option('calendar_colors', array(
  '0' => array(),
));
$handler->override_option('calendar_colors_vocabulary', array());
$handler->override_option('calendar_colors_taxonomy', array());
$handler->override_option('calendar_popup', 0);
$handler->override_option('calendar_date_link', '');
$handler = $view->new_display('calendar_block', 'Calendar block', 'calendar_block_1');
$handler->override_option('block_description', 'Calendar');
$handler->override_option('block_caching', -1);
$handler = $view->new_display('calendar_period', 'Year view', 'calendar_period_1');
$handler->override_option('style_plugin', 'calendar_style');
$handler->override_option('style_options', array(
  'display_type' => 'year',
  'name_size' => 1,
  'max_items' => 0,
));
$handler->override_option('attachment_position', 'after');
$handler->override_option('inherit_arguments', TRUE);
$handler->override_option('inherit_exposed_filters', TRUE);
$handler->override_option('displays', array(
  'calendar_1' => 'calendar_1',
  'default' => 0,
  'calendar_block_1' => 0,
));
$handler->override_option('calendar_type', 'year');
$handler = $view->new_display('calendar_period', 'Month view', 'calendar_period_2');
$handler->override_option('style_plugin', 'calendar_style');
$handler->override_option('style_options', array(
  'display_type' => 'month',
  'name_size' => '99',
  'with_weekno' => '1',
  'date_fields' => NULL,
  'max_items' => 0,
));
$handler->override_option('attachment_position', 'after');
$handler->override_option('inherit_arguments', TRUE);
$handler->override_option('inherit_exposed_filters', TRUE);
$handler->override_option('displays', array(
  'calendar_1' => 'calendar_1',
  'default' => 0,
  'calendar_block_1' => 0,
));
$handler->override_option('calendar_type', 'month');
$handler = $view->new_display('calendar_period', 'Day view', 'calendar_period_3');
$handler->override_option('style_plugin', 'calendar_style');
$handler->override_option('style_options', array(
  'name_size' => '99',
  'with_weekno' => 0,
  'max_items' => 0,
  'max_items_behavior' => 'more',
  'groupby_times' => 'hour',
  'groupby_times_custom' => '',
  'groupby_field' => '',
));
$handler->override_option('attachment_position', 'after');
$handler->override_option('inherit_arguments', TRUE);
$handler->override_option('inherit_exposed_filters', TRUE);
$handler->override_option('displays', array(
  'calendar_1' => 'calendar_1',
  'default' => 0,
  'calendar_block_1' => 0,
));
$handler->override_option('calendar_type', 'day');
$handler = $view->new_display('calendar_period', 'Week view', 'calendar_period_4');
$handler->override_option('style_plugin', 'calendar_style');
$handler->override_option('style_options', array(
  'name_size' => '99',
  'with_weekno' => 0,
  'max_items' => 0,
  'max_items_behavior' => 'more',
  'groupby_times' => 'hour',
  'groupby_times_custom' => '',
  'groupby_field' => '',
));
$handler->override_option('attachment_position', 'after');
$handler->override_option('inherit_arguments', TRUE);
$handler->override_option('inherit_exposed_filters', TRUE);
$handler->override_option('displays', array(
  'calendar_1' => 'calendar_1',
  'default' => 0,
  'calendar_block_1' => 0,
));
$handler->override_option('calendar_type', 'week');
$handler = $view->new_display('calendar_period', 'Block view', 'calendar_period_5');
$handler->override_option('style_plugin', 'calendar_style');
$handler->override_option('style_options', array(
  'display_type' => 'month',
  'name_size' => '1',
));
$handler->override_option('attachment_position', 'after');
$handler->override_option('inherit_arguments', TRUE);
$handler->override_option('inherit_exposed_filters', TRUE);
$handler->override_option('displays', array(
  'calendar_1' => 0,
  'default' => 0,
  'calendar_block_1' => 'calendar_block_1',
));
$handler->override_option('calendar_type', 'month');
$handler = $view->new_display('block', 'Upcoming', 'block_1');
$handler->override_option('fields', array(
  'title' => array(
    'label' => '',
    'link_to_node' => 1,
    'exclude' => 0,
    'id' => 'title',
    'field' => 'title',
    'table' => 'node',
    'relationship' => 'none',
    'format' => 'default',
  ),
  'changed' => array(
    'label' => '',
    'link_to_node' => 0,
    'exclude' => 0,
    'id' => 'changed',
    'field' => 'changed',
    'table' => 'node',
    'relationship' => 'none',
    'date_format' => 'small',
    'format' => 'default',
  ),
));
$handler->override_option('arguments', array());
$handler->override_option('filters', array(
  'status' => array(
    'operator' => '=',
    'value' => 1,
    'group' => '0',
    'exposed' => FALSE,
    'expose' => array(
      'operator' => FALSE,
      'label' => '',
    ),
    'id' => 'status',
    'table' => 'node',
    'field' => 'status',
    'relationship' => 'none',
  ),
  'date_filter' => array(
    'operator' => '>=',
    'value' => array(
      'value' => NULL,
      'min' => NULL,
      'max' => NULL,
      'default_date' => 'now',
      'default_to_date' => '',
    ),
    'group' => '0',
    'exposed' => FALSE,
    'expose' => array(
      'operator' => FALSE,
      'label' => '',
    ),
    'date_fields' => array(
      'node.changed' => 'node.changed',
    ),
    'granularity' => 'day',
    'form_type' => 'date_select',
    'default_date' => 'now',
    'default_to_date' => '',
    'id' => 'date_filter',
    'table' => 'node',
    'field' => 'date_filter',
    'override' => array(
      'button' => 'Use default',
    ),
    'relationship' => 'none',
  ),
));
$handler->override_option('title', 'Upcoming');
$handler->override_option('items_per_page', 5);
$handler->override_option('use_more', 1);
$handler->override_option('style_plugin', 'list');
$handler->override_option('style_options', array(
  'grouping' => '',
  'type' => 'ul',
));
$handler->override_option('block_description', 'Upcoming');
$handler->override_option('block_caching', -1);

UPDATE: Lezártam a hozzászólások lehetőségét. Akinek kérdése van a témával kapcsolatban, az új fórum téma nyitásával teheti meg (hivatkozza be a leírás címét).

Köszönöm!

Egyedi "Karbantartás alatt" oldal készítése

Ha Drupal honlapunkon karbantartási munkákat végzünk, az admin/settings/site-maintenance oldalon célszerű a webhelyet offline üzemmódba kapcsolni. Ekkor csak a webhely adminisztrátora fér hozzá a honlap tartalmához, a többi látogató az alábbi feliratot látja:

Karbantartás miatt zárva

Az oldal szövegét szintén az admin/settings/site-maintenance oldalon tudjuk módosítani – például ha szeretnénk jelezni, hogy mikor indul újra a honlap, itt megtehetjük. Lehetőség van HTML kód bevitelére is:

A nyitás tervezett időpontja:

2007. június 24. hétfő, reggel 9 óra

Nyitás: 2007. június 24. hétfő, reggel 9 óra

Ha szeretnénk teljesen egyénivé tenni az oldalt, könnyedén lecserélhetjük a Drupal logót és a „Karbantartás miatt zárva” főcímet is a theme_maintenance_page() függvény felülírásával.

Az egyik lehetőség, hogy egyszerűen bemásoljuk a függvényt a template.php fájlba, átnevezzük phptemplate_maintenance_page()-re, és elvégezzük a kívánt módosításokat.

Kicsit bonyolultabb, de végső soron kényelmesebb megoldás, ha külön sablont készítünk a karbantartási oldal számára. Ehhez hozzunk létre egy page-maintenance.tpl.php nevű fájlt a sminkmappában, és hívjuk meg a template.php segítségével:


function phptemplate_maintenance_page($content, $messages = TRUE, $partial = FALSE) {
return _phptemplate_callback('page-maintenance', array('content' => $content, 'messages' => $messages, 'partial' => $partial));
}
?>

Ezek után készítsük el a page-maintenance.tpl.php sablont. Belinkelhetjük a webhely favikonját és a karbantartási oldalhoz készített külön CSS fájlt is. Az admin/settings/site-maintenance oldalon megadott üzenetünket a $content változó segítségével tudjuk kiíratni.




Webhely karbantartás | Honlap.hu


Sajnáljuk, honlapunk pillanatnyilag nem elérhető




A végeredmény:

Egyedi karbantartásjelző oldal

Feladat alapú jogosultság kezelés a jogosultság hurokkal

A Drupal 4.5.0-ás kiadásával kezdődően többféle jogosultság séma jelenléte lehetséges a rendszerben, melyek együttes hatásán múlik az, hogy egyes oldalakat illetve tartalmakat ki érhet el, ki szerkeszthet és ki törölhet. Ebben a leírásban a feladat alapú jogosultságkezeléshez történő fejlesztéssel foglalkozunk.

Bevezető

A Drupalban minden felhasználó valamilyen csoportba tartozik. Látogatóknak nevezzük azokat, akik nem regisztráltak a rendszeren, vagy nem léptek be. Ezek az érdeklődők az alapértelmezésben rendelkezésre álló anonymous user csoportba tartoznak automatikusan. Azon felhasználók, akik beléptek a rendszerbe, regisztrációjukkor az authenticated user csoportba kerültek besorolásra. Ezen a két alapcsoporton kívül természetesen lehetőség van bármennyi csoport létrehozására, az alaprendszerben pedig a felhasználók egyenkénti szerkesztésével tudjuk csoportba tartozásukat megadni.

A Drupal moduljai különböző jogosultságokat definiálnak, melyeket megadhatunk az egyes csoportoknak. Az adminisztráció » felhasználók » beállítás » jogosultságok oldalon találjuk azt a mátrixot, melyben a különböző csoportoknak megadhatjuk az egyes jogokat. Egy felhasználó több csoportba is tartozhat, és ilyenkor a jogosultságai összeadódnak, tehát ha egy csoportban megadtunk neki valamilyen jogot, akkor azt megkapja akkor is, ha más csoportnak – melyeknek tagja – nincs ilyen joga. Az elsőként regisztrált felhasználónak mindenhez joga van, csoportba tartozásától függetlenül.

A feladat alapú jogosultság rendszer azt teszi lehetővé, hogy egyes feladatokhoz (például írások létrehozása) jogot adjunk egy csoportnak. Ezzel a csoport tagjai a webhelyen létrehozhatnak írás típusú tartalmakat. A legfontosabb alapjogokat a node modul definiálja: tartalmak elérése és tartalmak adminisztrációja. Az előbbi lehetővé teszi, hogy a webhely tartalmait az adott csoportba tartozó felhasználók olvassák (ha más, például tartalom-szintű jogosultság ezt felül nem bírálja), az utóbbi pedig a tartalmak metaadatainak szerkesztését is lehetővé teszi, azaz tartalmak közzétételére, eltüntetésére is jogot ad.

Támogatott jogosultságok ellenőrzése

Saját modul fejlesztésekor könnyen előfordulhat, hogy egy meglévő jogosultság ellenőrzése tökéletesen megfelel számunkra. Ilyenkor a user_access() függvény és a kívánt jogosultság eredeti angol nevének ismerete elegendő. A user_access() az aktuális felhasználó (vagy a második paraméterében megadott felhasználó) jogosultságait ellenőrzi. Ezt a függvényt nagyon sok helyen megtaláljuk a Drupalban, többek között a menük előállításakor.

// A 'tartalom elérése' jogosultság ellenőrzése
if (user_access('access content')) {
// ...
}
?>

A jogosultság hurok

Mégis honnan tudhatjuk meg, hogy mi a számunkra érdekes jogosultság neve, illetve, a Drupal miként tudja listázni a beállítható jogosultságokat az adminisztrációs felületen? A válasz igen egyszerű: a jogosultság hurokban megadhatjuk a modulunk által definiált jogosultságokat. Ez a hurok egyszerűen egy jogosultság név listával tér vissza. Ezeket ajánlja majd fel a Drupal az adminisztrációs felületen, és ezeket használhatjuk nyugodtan user_access() hívásokban is. Nem kell definiálnunk a jogosultság hurkot, ha megelégszünk a rendelkezésre álló jogosultság készlettel, és nincs szükségünk újabbra. Ilyenkor a meglévő modulok jogosultság hurkaiból tudjuk kideríteni a definiált jogosultságok eredeti neveit – melyeket az adminisztrációs felület lefordítva jelenít meg a karbantartók életének megkönnyítése érdekében.

function meggymag_perm() {
return array("administer cherry seed", "collect cherry seeds");
}
?>

A jogosultságok elnevezésére bevett szokás az administer és access előtagok használata az adminisztrációs illetve elérési jogok megadására, ám ezek nem korlátozó szabályok, csak az áttekinthetőség érdekében bevezetett irányelvek.

Ha a fenti jogosultság hurkot definiáltuk, modulunkban nyugodtan használhatjuk a user_access("collect cherry seeds") jogosultság ellenőrzést, ha arra vagyunk kíváncsiak, hogy az adminisztrátor jogot adott-e az aktuális felhasználónak a meggymagok gyűjtésére.

Fordítások készítése egyszerűen

A cikk elavult kérlek olvasd inkább a Magyar felület fordításáról szóló kézikönyv lapot vagy nézd meg a tanarurkerem.hu oldalán található videót.

Előfordulhat, hogy letöltünk egy modult, amihez sehol nem találunk a számunkra szükséges nyelven fordítást. Ilyenkor tanácstalanok lehetünk, fel is adhatjuk a modul használatát, vagy az adott nyelv fordítói csapatánál verhetjük az asztalt, hogy márpedig le kellene azt a modult fordítani. Valószínűleg egyik út sem a kívánt eredményre fog vezetni, ezért jó tudni, hogy a magunk kezébe is vehetjük az ügyet.

Nagyon ritka, hogy olyan modullal találkozunk, amit nem készítettek fel a fordíthatóságra. Az azonban már sokkal gyakoribb, hogy könnyen szerkeszthető .pot (Gettext Portable Object Template) sablon fájlt nem kapunk a modulhoz, vagy amit a csomagban letöltöttük, már nem aktuális. Ilyenkor lesz segítségünkre az extractor.php nevű szkript, . Ezt használják a Drupal fordítók is arra, hogy az alaprendszer és a kiegészítő modulok, sminkek fordítási sablonjait előállítsák. Mivel ez a szkript webbarát, nincs más dolgunk, mint a kívánt kiterjesztés vagy modul mappájába tenni, és a böngészőnkből a webszerveren keresztül lefuttatni. Ezzel az adott mappában keletkezik legalább egy .pot fájl, vagy ha nincs joga a szkriptnek új fájlokat létrehozni, akkor hibaüzenetet ad.

A .pot fájlok már egyszerű UTF-8 kódolású szöveges fájlok, amik csak a fordítandó karaktersorozatokat tartalmazzák. Ezeket akár egy UTF-8-képes szöveges fájl szerkesztővel is lefordíthatjuk, vagy bevethetünk egy a poEdit képességeivel rendelkező speciális programot.

A kész fordítást az administer » locales » import (adminisztráció » nyelvek » import) fülön tölthetjük fel, pont úgy, mint ahogy az alaprendszer fordítását is importáltuk.

Ne felejtsük el a kész fordítást az adott nyelv fordítói csapatával is megosztani! Érdemes a közösség által végzett munka kiaknázása után hozzájárulni a közösség gyarapodásához, ha már magunk számára úgyis elkészítettük a fordítást. A magyar fordítói csapat a levelezőlistáján keresztül érhető el.

Kapcsolat űrlap és levél módosítása a Drupalban

Korábban már írtam a hook_form_alter() előnyeiről és működéséről. Ennek segítségével – némi programozással – elérhetjük, hogy a Drupal módosítása nélkül a rendszer űrlapjai kedvünkre változzanak meg. De mi van akkor, ha az űrlapok változtatása nem elegendő? Az utóbbi hetekben a Weblabor.hu 4.6.x-es rendszerről 5.x-es Drupal rendszerre frissítésén is munkálkodom szabadidőmben, és éppen ma értem el a Weblabor szerkesztőit is megcímző kapcsolati űrlap funkcióhoz. Lássuk mi az igényelt funkció, és milyen kényelmes megtenni a módosításainkat Drupal 5.1-gyel!

A kérdéses szolgáltatás csak az adminisztrátorok számára érhető el a Weblaboron, és azt teszi lehetővé, hogy amikor egy levelet küldünk (jellemzően valamilyen moderációs lépésről) egy felhasználó kapcsolat űrlapjáról az adott tagnak, akkor azt egyben cc-zzük is a Weblabor szerkesztőinek. Fontos, hogy egy cc fejléc elemet veszünk fel a levélbe, és a Drupal beépített 'másolatot kérek' funkciójával ellentétben nem új levelet küldünk. A cc fejléc használata ugyanis lehetővé teszi, hogy a szerkesztők is azonnal reagáljanak, hozzátegyék saját véleményünket a levélhez úgy, hogy azt a szerkesztők és a felhasználónk is megkapja. Ráadásul szálkövető marad a levelezés.

A fejlesztői alapproblémám egy a korábbi Drupal 4.6-os rendszerkód módosításával megvalósított szolgáltatás átültetése ezúttal már oly módon, hogy ne kelljen Drupal kódot módosítanom. Szerencsére a két ehhez szükséges eszköz a Drupal 5.0-val megérkezett, tudok megjelenített űrlapot és elküldött levelet is módosítani. A demonstráció kedvéért nevezzük a modulunkat wlcontact-nak, a domain pedig legyen example.com (így aki innen másolja a kódot, az nem rögtön nekünk címzi a leveleit).

Először is az űrlapot kell megváltoztatnom. Itt azonosítani kell, hogy a felhasználói kapcsolat űrlapról van szó, és adminisztrátorral van dolgunk. Ha ez teljesül, akkor felveszünk egy új 'toeditors' elemet az űrlapba, ami jelölőnégyzet típusú. Magyarázatot is adunk, hiszen a működés eltérő a beépített másolat funkciótól (és az összekeverés elkerülése érdekében arra a beépített elemre is adunk magyarázatot). Meg kell még oldanunk, hogy az új elemünk a submit gomb fölött jelenjen meg. Mivel ebben az űrlapban nincsenek megadott súlyozások, a submit gombnak nagyobb súlyt adunk meg, mint a saját jelölőnégyzetünknek. Ezzel kész is az űrlap külalakja. Még egy dologra kell figyelnünk, mégpedig, hogy értesüljünk az űrlap elküldéséről és feldolgozzuk a másolat kérést. Ennek érdekében az űrlap elküldése után lefutó függvények elejére regisztrálunk egy saját eseménykezelőt, ami így befolyásolni tudja majd a további működést. Fontos, hogy az elejére regisztráljunk, hiszen az alapértelmezett eseménykezelő küldi a levelet, és nekünk azelőtt kell közbelépnünk a folyamatba.

function wlcontact_form_alter($form_id, &$form) {
  if($form_id == 'contact_mail_user' && user_access('access administration pages')) {
    $form['toeditors'] = array(
      '#title' => 'Másolat az Example.com szerkesztőknek',
      '#type' => 'checkbox',
      '#description' => 'Másolat küldése a szerkesztőségi email címre is.',
      '#weight' => 9,
    );
    $form['copy']['#description'] = 'Másolat küldése a személyes email címre.';
    $form['submit']['#weight'] = 10;
    $form['#submit'] = array('wlcontact_mail_user_submit' => array()) + $form['#submit'];
  }
}

Felmerülhet a kérdés, hogy miként ismertem fel az űrlap azonosítóját és elemeit. Ilyen részletek érdekében nem szeretem a forráskódot böngészni, ezért egyszerűen a var_dump($form_id) és var_dump($form) használatával derítettem fel a struktúrát, illetve, hogy mit hol kell módosítanom, milyen nevű kulcsok vannak a tömbben. Ez éppen elegendő volt a céljaimra.

Nos, lássuk mit tudunk tenni, ha az űrlapot elküldik. Mivel a követelményünk az volt, hogy nem második levelet küldünk, hanem a contact modul által egyébként is elküldött levelet módosítjuk, nem tudunk azonnal levelet küldeni. Meg kell viszont jegyeznünk, hogy a szerkesztőségnek is el kell küldeni a levelet, mert erre még később szükségünk lesz. A submit függvények két paramétert kapnak, az űrlap azonosítóját és a beküldött értékeket. Most biztosak vagyunk az űrlap azonosítóját illetően, hiszen csak egy űrlaphoz kötöttük ezt az eseménykezelőt, ezért annak értékével nem foglalkozunk, csak azzal, hogy be legyen állítva. Amennyiben pedig az űrlap értékek között a szerkesztőknek való elküldést kérték, ezt egy statikus értékben megjegyezzük. Ha úgy hívjuk meg ezt a függvényt, hogy nem adunk meg semmilyen űrlap azonosítót, akkor fogjuk visszakapni a megjegyzett értéket.

function wlcontact_mail_user_submit($form_id = NULL, $form_values = array()) {
  static $toeditors = FALSE;
  if (isset($form_id)) {
    $toeditors = (bool) $form_values['toeditors'];
  }
  else {
    return $toeditors;
  }
}

Végül szükségünk van arra, hogy a megjegyzett értéknek megfelelően elhelyezzünk egy cc fejlécet a kiküldött levelekben. Erre a célra tavaly nyár óta a hook_mail_alter() használható. Ezt kell tehát már csak megvalósítanunk. Itt a levél részletes adatait kapjuk meg külön-külön paraméterekben. Most számunkra az az érdekes, hogy melyik levéllel van dolgunk. Szerencsére a contact modul fejlesztői okosan megkülönböztették a normál kapcsolat levelet a (beépített képességgel előállított) másolat levéltől, így csak a normál kapcsolat levélre tudunk koncentrálni. Itt ellenőrizni kell, hogy a korábban megjegyzett űrlap mező érték mi volt. Ha kérték a cc fejléc beállítását, akkor a fejlécek tömbjéhez adjuk ezt hozzá.

function wlcontact_mail_alter(&$mailkey, &$to, &$subject, &$body, &$from, &$headers) {
  if ($mailkey == 'contact-user-mail' && wlcontact_mail_user_submit()) {
    $headers['Cc'] = 'info@example.com';
  }
}

Voilá! Készen is vagyunk a kapcsolat űrlap funkcionalitásának kiterjesztésével, anélkül, hogy bármit módosítani kellett volna az alaprendszeren.

Media modullal feltöltött kép beszúrása a CKEditor szerkesztőmezőjébe

Feladat:
Media modullal feltöltött képeket szeretnénk beszúrni CKEditorral szerkesztett tartalomba, egyéni képstílus használatával.

Felhasznált modulok:

Megjegyzés:
A Wysiwyg modul és a CKEditor feltelepítését nem tartalmazza a leírás. A Media modul fejlesztői változata több olyan módosítást is tartalmaz, amelyik visszafelé nem kompatibilis, többek közt a Media entitás helyett a File entitással kell elvégezni az itt vázolt műveleteket.

Lehetséges megoldás:

  1. Az admin/config/media/image-styles oldalra ellátogatva adjunk hozzá egy új képstílust, majd állítsuk be hozzá a kívánt effektusokat (pl. elforgatás, átméretezés).
  2. Ezután az admin/config/media/file-styles oldalon adjunk hozzá egy új fájlstílust, a képeknél beállítva neki az előzőleg létrehozott képstílust:
  3. Az admin/structure/ds/view_modes/manage címen készítsünk egy új nézetmódot, melyet csatoljunk a Media entitáshoz:
  4. Kapcsoljuk be az admin/config/media/types/manage/image/display oldalon, hogy a képeknél az újonnan készített nézetmódhoz egyéni megjelenítési beállításokat szeretnénk használni:
  5. Mentés után válasszuk ki az új nézetmódhoz csatolandó fájlstílust:
  6. Az admin/config/content/wysiwyg oldalon szerkesszük a használt profilt, a „Nyomógombok és bővítmények” szekcióban kapcsoljuk be a „Media browser”-t.
  7. Az admin/config/content/formats oldalon a Wysiwyg editorhoz (CKEditor) használt szövegformátum beállításainál engedélyezzük a „Converts Media tags to Markup” szűrőt.
  8. Tartalom beküldésekor a CKEditorban kaptunk egy új nyomógombot:
  9. A gombra kattintva tölthetünk fel új képet vagy választhatunk a már feltöltöttek közül, majd kiválaszthatjuk a kívánt nézetmódot:
  10. Ezután a képpel már a hagyományos módon dolgozhatunk, jobb gombbal kattintva rajta a „Kép tulajdonságai” menüpontban beállíthatjuk az igazítását és egyéb tulajdonságokat.

Természetesen, ha megelégszünk az automatikusan létrejövő képstílusok számával és elnevezésével, akkor a fenti műveletek helyett elég a már meglévő képstílusokat módosítani.

Melyik nevedet mutassam?

A készülő Drupal alapú magyar Ubuntu közösségi webhely készítése során valósítottam meg azt, ami már a Weblabor kapcsán is többször felmerült bennem. Mostanában ?divattá? vált az interneten a teljes név használata semmitmondó nicknevek mögé zárkózás helyett. Ez a jelenség a Weblabornál, mint szakmai médiumnál megfigyelhető, a warezoldalakon ? érthető okokból ? kevésbé. Ez viszont felvet egy technikai problémát: magyarok vagyunk, és ?gonosz? módon nem csak ASCII neveink vannak. Erre a problémára adhat megoldást a felhasználónév, a nick és a teljes név különválasztása.

A konkrét megoldásom az volt, hogy a felhasználó személyes beállításainál megadhatja teljes- és becenevét illetve azt, hogy melyik jelenjen meg beküldött tartalmai, hozzászólásai beküldőjeként. A megvalósításhoz három változtatást kell eszközölni.

Az első a ?profile? modul engedélyezése.

A második a megfelelő profiladatok felvétele, amit az adminisztrációs rendszerből (admin/settings/profile) a legegyszerűbb elvégezni:

Típus Mezőnév Láthatóság Megjegyzés
egysoros szövegmező profile_nickname publikus Becenév; opcionális
egysoros szövegmező profile_fullname publikus Teljes név; opcionális
választólista profile_whichname személyes Alapértelmezett név; Lehetőségek: Felhasználói név, Teljes név, Becenév

Végül a harmadik lépés PHPTemplate sablonrendszer használata esetén a smink gyökerében egy template.php fájlt létrehozása (ha nem létezik még). Ebbe a következőt írjuk:

/**
* Format a username.
*
* @param $object
* The user object to format, usually returned from user_load().
* @return
* A string containing an HTML link to the user's page if the passed object
* suggests that this is a site user. Otherwise, only the username is returned.
*/
function phptemplate_username($object) {

if ($object->uid && $object->name) {

// Shorten the name when it is too long or it will break many tables.
if (drupal_strlen($object->name) > 20) {
$name = drupal_substr($object->name, 0, 18) .'?';
}
else {
$name = $object->name;
}

if (user_access('access user profiles')) {
//sajnos be kell töltenünk a felhasználói profilt
profile_load_profile($object);
$output = "";
//a lényeg
if ($object->profile_whichname=="Teljes név") {
$_title = t('View user profile.') . ' Felhasználói név: ' . $name;
$output .= l($object->profile_fullname, 'user/'. $object->uid, array('title' => $_title));
}
elseif ($object->profile_whichname=="Becenév") {
$_title = t('View user profile.') . ' Felhasználói név: ' . $name;
$output .= l($object->profile_nickname, 'user/'. $object->uid, array('title' => $_title));
}
else {
//az eredeti
$output .= l($name, 'user/'. $object->uid, array('title' => t("View user profile.")));
}
}
else {
$output = check_plain($name);
}
}
else if ($object->name) {
// Sometimes modules display content composed by people who are
// not registered members of the site (e.g. mailing list or news
// aggregator modules). This clause enables modules to display
// the true author of the content.
if ($object->homepage) {
$output = l($object->name, $object->homepage); $output .= " homepage}\" rel=\"nofollow\" title=\"Személyes weblapja\">" .
theme_image('themes/ubuntuhu/gfx/url.png', 'Honlapja') . '
';
}
else {
$output = check_plain($object->name);
}

$output .= ' ('. t('not verified') .')';
}
else {
$output = variable_get('anonymous', 'Anonymous');
}
return $output;
}
?>

Ez a Drupal 4.7.2 theme_username függvényének megfelelő kiegészítése. A megjegyzésekben látszik, hogy mi nem az eredeti függvény része.

Mire jó a ctools + panels + views trió

A 2012 Drupalaton és egy fórum kérdés ösztönözte ezt az írást.

A konkrét probléma: hogyan listázzunk ki felhasználókat bizonyos taxonómia kategóriába tartozás szerint ctools, panels és views segítségével Drupal 7 alatt.

Értelemszerűen telepítsük ezeket a modulokat a szokásos módon:
Views
Ctools
Panels

Kapcsoljuk be a Chaos tools, Page manager, Views content panes, Panels, Views, Views UI modulokat.

Ha ezzel megvagyunk akkor kezdjük a munkát. Hozzunk létre egy szótárt (admin/structure/taxonomy/add) "Felhasználó kategória" névvel és legyen a programok által használt neve "user_cat", ez lesz majd a felhasználói profilban felvéve mezőként. Készítsünk néhány kategóriát (admin/structure/taxonomy/user_cat/add) tetszés szerint.

Ezután a felhasználói beállításoknál (admin/config/people/accounts/fields) vegyük fel a mezőt a felhasználóhoz:
Mezőtípus: kifejezés hivatkozás
Név: Kategória
Programok által használt név: field_category
Szótár: Felhasználó kategória

Miután ez is elkészült hozzunk létre néhány teszt felhasználót különböző kategóriákkal, vagy módosítsuk a meglévőket. Így az előkészületekkel meg is volnánk.

Hozzuk létre az oldalunkat ami listázni fogja a megadott kategóriába tartozó felhasználókat. Ehhez a Ctools Page managerét fogjuk használni: admin/structure/pages

Page manager
Adjunk hozzá egy új oldalt ahol a megadott kategóriába tartozó felhasználókat ki fogjuk listázni.
Adjunk nevet az oldalnak és legyen az elérési út: user-list/%taxonomy_term
Így listázható lesz például a teszt1 kategória: user-list/teszt1

Új oldal
Mást itt nem kell beállítanunk, kattintsunk a Folytatás gombra.

Argumentum beállítás
Az előkészített argumentumunkat kell most beállítani, kattintsunk a Változtat gombra.

Argumentum típus kiválasztása
Válasszuk a "Taxonómia kifejezés: ID"-t és kattintsunk a Folytatás gombra.

Az argumentum beállítása
Ha term id-t szeretnénk a linkben használni akkor a Kifejezésazonosítót válasszuk, ha a kifejezés nevét szeretnénk használni akkor értelemszerűen a Kifejezés nevét válasszuk.
Ha csak a létező kifejezésekre akarjuk, hogy megjelenjen az oldal akkor a Limit to these vocabularies kiválasztóknál válasszuk a Felhasználó kategóriát. Ha mindent beállítottunk akkor kattintsunk a Befejezés gombra.

Az argumentum beállítás kész
Beállítottuk az argumentumot mehetünk tovább a Folytatás gombra kattintva.

Panel elrendezés
Ezen az oldalon állíthatjuk be a panel elrendezést. Jelenleg csak listázni akarunk, ezért az egy oszlop is megteszi, de ha komplexebb megjelenést szeretnénk akkor itt lubickolhatunk a lehetőségek között. Ha megvan a megfelelő akkor Folytatás.
A következő oldalak az induláshoz már nem feltétlen szükségesek ezért változtatás nélkül kattinthatunk a Folytatás, majd Befejezés gombokra.
Ha mindent jól csináltunk akkor most az összegző beállító oldalra kerültünk, kattintsunk a Mentés gombra.

Az oldalunk ezzel majdnem kész is van, már csak egy olyan lekérdezést kell gyártanunk ami előállítja a felhasználó listánkat, hogy beilleszthessük az oldalba.

Ehhez készítsünk egy nézetet: admin/structure/views/add

Nézet hozzáadása
Adjunk nevet a nézetnek és válasszuk a felhasználókat, oldalt és blokkot nem készítünk. Ha megvagyunk a beállításokkal akkor tovább a Folytatás és szerkesztés gombra kattintva.

Két dolgot feltétlenül meg kell tennünk:
1. Argumentumot kell létrehozni a Felhasználó kategóriának
2. Környezetet kell kreálni ami átadja az argumentumot a nézetnek

Szövegkörnyezeti szűrő hozzáadás
Kattintsunk a Haladó beállításoknál a SZÖVEGKÖRNYEZETI SZŰRŐK Hozzáadás linkre.

Mező kiválasztás
Válasszuk ki a Kategória mezőt, amit a felhasználóhoz hozzáadtunk.

Nézet elrejtése, ha nincs eredmény
Válasszuk a Nézet elrejtését, ha nincs argumentum, majd kattintsunk az Alkalmazás gombra.

Adjuk hozzá a környezetet.

Környezet hozzáadása
Ezután szerkesszük az argumentum bemenetet.

Argumentum bemenet szerkesztése
Nekünk a panel által biztosított környezetből fog kelleni a kifejezés azonosító, mert a kifejezés nevét átkonvertálta már azonosítóra.

Argumentum bemenet beállítása
Ha megvagyunk, akkor kattintsunk az Alkalmazás gombra.
A megjelenési nevet állítsuk beazonosíthatóra, például "Felhasználó listázása kategória szerint", és a nézetet mentsük el.

Ha mindent jól csináltunk akkor már csak egy dolgunk maradt, a listánkat az oldalhoz adni. Menjünk az oldal szerkesztéséhez (admin/structure/pages megfelelő oldal szerkesztés link) és válasszuk a tartalom fület.

Tartalom hozzáadása
Kattintsunk a fogaskerékre és válasszuk a Tartalom hozzáadáadása linket.

Nézet kiválasztása
Itt válasszuk a Nézetek fület, azon belül az imént létrehozott nézetünket.

Képernyő kiválasztása
Ezen belül pedig a jól beazonosítható képernyőt, majd Folytatás.

Környezet átadás beállítása
Már csak a mezőnket kell beállítani, hogy a megfelelő környezetet adja át a nézetnek, válasszuk a Kifejezésazonosító-t, és a Befejezés gombot.
Mentsük az oldalt a Frissítés és mentés gombra kattintva.

Írjuk be a címsorba domain.név/user-list/kifejezésnév és ha mindent jól csináltunk akkor megkapjuk az óhajtott felhasználó listát.

Az eredeti taxonómia oldal felüldefiniálása

Amennyiben az eredeti taxonomy/term/%taxonomy_term útvonalat akarjuk használni akkor engedélyezzük a rendszer term_view oldalt és csináljuk végig az előző részben leírtakból mindent csak nem új oldalt hozunk létre, hanem szerkesztjük a rendszer term_view oldalt. Ilyenkor útvonalat, argumentumot már nem kell beállítani!

Rendszer term_view szerkesztése
Kattintsunk a Szerkesztés linkre.

Változat létrehozása
Kattintsunk az Új változat hozzáadása linkre.

Változat beállítása
Adjunk nevet a változatnak és pipáljuk be a Választási szabályok checkbox-ot, majd a Változat létrehozása linkre kattintsunk.

Választási szabályok beállítása
Itt a legördülőből a Taxonómia szótár-t válasszuk és a Hozzáadás linkre kattintunk.

Választási szabályok beállítása2
Kiválasztjuk a Felhasználó kategória szótárt és Mentés.

Ha most a Folytatás gombra kattintunk, akkor elérkezünk a panel elrendezése oldalhoz ahonnan folytathatjuk a beállításokat.

Publishboard: a drupal.hu szerkesztőségi segítője

A Drupal.hu indulásakor a webhely egyik funkciója az új tartalmak beérkezése esetén az adminisztrátorok értesítése (notify) volt. Ezt egyrészt a megnövekedett link beküldés szám, másrészt pedig az akkor használt modul hátrahagyottsága (a frissítés elmaradása miatt) nem használtuk tovább. Így viszont, és a kis szerkesztőség kevés idejének is betudhatóan nem követtük a beérkező tartalmakat, sokszor külön emailben értesültünk, hogy valamilyen cikket vagy hírt most már át kellene nézni, és meg kellene jelentetni.

Amikor a drupal.hu szerkesztőségét bővítettük (mely ma már a legtöbb kiemelten aktív drupal.hu tagot magában foglalva hét tagot számlál), ez a gond még erősebben jelentkezett. Úgy döntöttünk, hogy email értesítés helyett folyamatosan szem előtt lévő összefoglalót alakítunk ki. Ennek a fejlesztését én vállaltam, s a néhány hónapja használt Publishboard modulunk meglátásom szerint beváltotta a hozzá fűzött reményeket.


A publishboard blokk megjelenése

A feladat tehát a beküldött tartalmak összefoglalása volt, hogy értesülhessünk a beküldött új tartalmakról, és minél hamarabb cselekedhessünk ezeket illetően. Természetesen belefoghattunk volna valamilyen általános modullal összevarázsolni a saját eszközünket, de az eredményként született jól kommentezve is csupán 115 soros modul karcsúbb és célratörőbb lett.

Először is természetesen szükségünk volt egy .info fájlra a modulhoz:


name = Publishboard
description = Beküldött tartalmak kezelését segíti
package = "Drupal.hu"
core = 6.x

Itt látszik, hogy a drupal.hu számára kifejlesztett moduljainkat egy csomagban fogjuk össze.

A számunkra lényeges információ az, hogy mennyi még megjelenésre váró tartalom van, illetve ezek közül mennyi új és régi. Drupal terminológiával most újnak tekintünk minden tartalmat, amit az aktuálisan belépett felhasználó még nem látott vagy amióta látta, azóta változtattak rajta. Ennek érdekében a következő összesítő kódot alakítottuk ki:

/**
* A nem olvasott tartalmak számának összesítése.
*/
function publishboard_unread_counters() {
global $user;
// Üres tömbökkel indulunk.
$unchanged = $changed = $list = $types = array();
// Az összes most nem közzétett node néhány adatára van szükség és a típusaik listájára.
$result = db_query('SELECT nid, type, changed FROM {node} WHERE status = 0');
while ($node = db_fetch_object($result)) {
$list[$node->nid] = $node;
if (!in_array($node->type, $types)) {
$types[] = $node->type;
}
}
// Ha van bármilyen nem közzétett tartalom.
if (count($types)) {
// Nézzük, hogy az éppen látogató felhasználó melyeket látta már ezekből legutóbbi
// módosításuk óta (melyek így tényleg újak a számára). Az IN() tartalma biztos számokból áll
// a fenti kód alapján, ezért azt biztonságos az SQL-ben közvetlenül használni.
$part = join(", ", array_keys($list));
$result = db_query("SELECT nid, timestamp FROM {history} WHERE uid = %d AND nid IN ($part)", $user->uid);
while($history = db_fetch_object($result)) {
if ($list[$history->nid]->changed > $history->timestamp) {
// Van róla adatunk, és régebben látta, mint amikor legutóbb változott.
$changed[$list[$history->nid]->type]++;
}
else {
// Volt róla adat, de a változás régebbi, mint a látogatás.
$unchanged[$list[$history->nid]->type]++;
}
// Kezeltük a history tábla alapján.
unset($list[$history->nid]);
}
}
// Ezeket nem találtuk meg a history táblában, azaz soha nem látta a user,
// vagy nagyon régen látta, és már a history táblában nincs benne a bejegyzés.
// Csak a limitet tudjuk alapul venni az ellenőrzéshez.
foreach ($list as $node) {
if ($list[$node->nid]->changed > NODE_NEW_LIMIT) {
$changed[$node->type]++;
}
else {
$unchanged[$node->type]++;
}
}
// A típusok listájával és a számolások eredményével térünk vissza.
return array($types, $changed, $unchanged);
}
?>

Ezekről az adatokról egy emberileg olvasható blokkban szeretnénk információkat megjeleníteni:

/**
* hook_block() megvalósítás.
*/
function publishboard_block($op = 'list', $delta = 0, $edit = array()) {
if ($op == 'list') {
// Egy blokkot biztosítunk a rendszer számára.
return array(0 => array('info' => 'Beküldött tartalmak',
'weight' => -100, 'enabled' => 1, 'region' => 'right'));
}
elseif ($op == 'view' && user_access("access administration pages")) {
// Csak adminisztrátoroknak mutatjuk meg ezt a blokkot.
$items = array();
// Emberek számára olvasható nevek praktikusabbak, mint a gépi nevek.
$names = node_get_types('names');
// Alapadatok lekérdezése.
list($types, $changed, $unchanged) = publishboard_unread_counters();
foreach ($types as $type) {
$title = '';
// Elvileg valamelyik be van állítva, de azért menjünk biztosra.
if (isset($unchanged[$type]) || isset($changed[$type])) {
if (isset($unchanged[$type])) {
$title .= $unchanged[$type] .' régi'. (isset($changed[$type]) ? ', ' : ' ');
}
if (isset($changed[$type])) {
$title .= $changed[$type] .' új ';
}
$items[] = l($title . $names[$type], 'publishboard/'. $type);
}
}
if (count($items)) {
// Vannak megjelenítendő linkek.
return array(
'subject' => 'Beküldött tartalmak',
'content' => theme('item_list', $items),
);
}
}
}
?>

A blokk funkciója természetesen nem csak az információ átadása, hanem a szerkesztőség segítése is, tehát olyan linkeket kellett kialakítani, amik az adminisztrációs felület megfelelő oldalára vezetnek. A tartalmak kezelőfelülete azonban nem linkelhető a webcímbe helyezett olyan kritériumokkal, mint, hogy adott típusú, nem publikált tartalmakra van szükségünk. Ezért kellett bevezetni az előző kódban látható publishboard címen a köztes oldalunkat, ami a megfelelő beállításokkal átirányít a tartalmak adminisztrációs oldalára.

/**
* hook_menu() megvalósítás.
*/
function publishboard_menu($may_cache) {
$items = array();
// Cached menu items
if (!$may_cache) {
$items[] = array(
'path' => 'publishboard',
'title' => 'Beküldött tartalmak',
'callback' => 'publishboard_page',
'access' => user_access("access administration pages"),
'type' => MENU_CALLBACK,
);
}
return $items;
}
?>

Itt egy menüben meg nem jelenő, a publishboard_page()-et hívó webcím kezelőt állítottunk be. Az pedig egyszerűen:

function publishboard_page($type) {
// A tartalom lista szűrése meg nem jelent $type típusú elemekre, a node.module kódja alapján.
$_SESSION['node_overview_filter'] = array(
array('status', 'status-0'),
array('type', $type),
);
// Átirányítás a céloldalra.
drupal_goto('admin/content/node');
}
?>

Ennyivel meg is oldottuk a funkcionalitást. Rendelkezésünkre áll egy blokk, ami kijelzi a meg nem jelent tartalmakat, és a linkekre kattintva az adminisztrációs felületre vezet. Bekapcsolhatjuk a blokkot akár minden felhasználó számára, hiszen maga a blokk akadályozza meg, hogy nem adminisztrátorok részére is láthatóvá válljon. Számunkra még egy fontos elem volt: emeljük ki a blokkot a többi közül, megkönnyítve a szemünk számára a blokk azonosítását. Ezt a következő egyszerű CSS szabályok sminkünkbe helyezésével értük el:


/* Publishboard */
#sidebar #block-publishboard-0 {
background-color: #ffc;
padding: 0.8em;
}
#sidebar #block-publishboard-0 .item-list ul {
margin: 0;
}
#sidebar #block-publishboard-0 a {
color: #036;
font-weight: bold;
}

Ezzel egy figyelemfelkeltő sárga háttérrel megjelenő blokkot kaptunk, mely a fent látott módon fest.

Szálakba rendezett új hozzászólások követhetővé tétele

Alapvető és állandó probléma nagyobb forgalmú oldalakon, hogy a sok hozzászólás között igen nehéz a különböző szálakba érkezett új hozzászólások követése. A Drupal alapértelmezés szerint az általunk még nem olvasott hozzászólásokat (és tartalmakat) ?új? jelzéssel látja el. Ezt javítottam föl azzal, hogy arra kattintva a következő új hozzászólásra ugorjon.

A megoldás két egyszerű lépésből áll PHPTemplate sminkmotor használata esetén. Az első a template.php kiegészítése. Ez a fájl az adott smink gyökerében helyezhető el, például /sites/all/themes/azensminkem/template.php. Feladata a gyárilag használható .tpl.php fájloknál kevésbé általános finomhangolások végrehajtása, esetünkben a comment.tpl.php-nak küldött változók felülírása. Ez a _phptemplate_variables($hook, $vars) függvénnyel történhet a következő módon:

function _phptemplate_variables($hook, $vars) {
  switch ($hook) {
    case 'comment':
        static $numnew;
        $numnew=$numnew?$numnew:0;
        if ($vars['new']) {
          $vars['numnew'] = ++$numnew;
        }
 
    break;
  }
  return $vars;
}

Mivel ez a függvény meghívódik minden sablon kiértékelésekor, a hozzászólások megjelenítése előtt meg tudjuk vizsgálni, hogy éppen egy új hozzászólást dolgozunk-e fel. Ha új hozzászólásról van szó, akkor a függvényhívások között megjegyzett értékű (static) $numnew változóban számoljuk, hogy hányadik új hozzászólást találtuk. Az aktuális hozzászólásra vonatkozó $numnew változót megkapja a sablonunk.

A második lépés a hozzászólásoknál a megjelenés testreszabása, a link elhelyezése. Ez a comment.tpl.php, hozzászólások megjelenését szabályozó fájllal oldható meg. Ha nem létezik, létre kell hozni. Drupal 5 használata esetén az alapértelmezése a következő.

<div class="comment<?php print ($comment->new) ? ' comment-new' : ''; print ($comment->status == COMMENT_NOT_PUBLISHED) ? ' comment-unpublished' : ''; ?> clear-block">
  <?php print $picture ?>
 
<!-- módosítandó rész eleje -->
<?php if ($comment->new) : ?>
  <a id="new"></a>
  <span class="new"><?php print $new ?></span>
<?php endif; ?>
<!-- vége -->
 
  <h3><?php print $title ?></h3>
 
  <div class="submitted">
    <?php print $submitted ?>
  </div>
 
  <div class="content">
    <?php print $content ?>
  </div>
 
  <?php print $links ?>
</div>

A megjegyzésekkel jelölt részt kell módosítani a következőre.

if ($comment->new) {
  if ($numnew == 1) {
    print '<a id="new"></a>';
  }
  print '<span class="new"><a name="new' . $numnew . '" '.
   'title="következő olvasatlan hozzászólás" href="#new' . ($numnew+1) . '">' . $new . '</a></span>';
}

Ezzel ha új hozzászólást látunk, és az első új hozzászólásról van szó, akkor a new horgornyt rakjuk le. Különben egy new5 típusú horgonyt, ahol 5 az új hozzászólások közötti sorszám. A következő új hozzászólásra linkeljük a feliratot.

Tartalomszervezési megoldások I. - Taxonomy és Book modul

Az egyik leggyakoribb feladat honlapkészítés során, hogy a webhelyre feltöltött nagy mennyiségű tartalmat (írásokat, oldalakat, képeket – Drupal szakzsargonban: a node-okat) valahogyan rendszerezzük. Erre a Drupal alapcsomag két modult is kínál: a Taxonomy (kategorizáló) modullal a tartalmakat kategóriákba sorolhatjuk, a Book (könyv) modullal pedig "szülő-gyermek" kapcsolatot, azaz hierarchikus viszonyt alakíthatunk ki közöttük. Egyszerűbb webhelyeken ez általában elegendő a tartalmak rendszerezéséhez; azonban ahogy honlapunk egyre összetettebbé válik, előfordulhat, hogy beleütközünk az alapcsomag korlátaiba. Ilyenkor kiegészítő modulok telepítésével növelhetjük a Drupal képességeit. Az alábbi kétrészes cikkben először a tartalomszervezés problémáját ismertetjük, majd többféle, egyre növekvő komplexitású megoldást mutatunk be kezdő és haladó Drupal webmesterek számára.

A feladat

Vegyünk egy egyszerű példát: szeretnénk egy sportegyesületnek webhelyet készíteni. Az egyesület keretein belül 2 csapat is működik, egyenként 15-20 taggal. Honlapunkon az egyesület adatai mellett szeretnénk önálló oldalt nyitni mindkét csapatnak, valamint egyenként az összes játékosnak. Természetesen a játékosok oldalain fel kell tüntetnünk, hogy melyik csapatban játszanak.

Első megoldás: Taxonomy modul

Ha Taxonomy modullal oldjuk meg a feladatot, akkor hozzunk létre egy "Csapatok" nevű szótárat, és ehhez adjunk 2 kategóriát: Piros csapat, Kék csapat. Ezek után hozzuk létre a játékosok oldalait, és a tartalombeküldő oldalon a Kategóriák rovatban jelöljük be, hogy az adott játékos melyik csapathoz tartozik. A végeredmény valahogy így fog kinézni:

  • Piros csapat (taxonomy/term/1)
    • Játékos-1 (node/1)
    • Játékos-2 (node/2)
    • Játékos-3 (node/3)
    • stb.
  • Kék csapat (taxonomy/term/2)
    • Játékos-4 (node/4)
    • Játékos-5 (node/5)
    • stb.

Ha most felkeressük webhelyünkön a www.honlapneve.hu/taxonomy/term/1 oldalt, akkor ott egy listát találunk, amelyben a linkek a Piros csapat tagjainak egyéni oldalaira mutatnak (www.honlapneve.hu/node/1, stb.); az egyéni oldalakon pedig ott látjuk a cím alatt a kategória linket, amely megmutatja, hogy az adott játékos melyik csapatnak a tagja, és amelyen keresztül visszatérhetünk a csapat oldalára. A menübe felvehetjük a Piros csapat és Kék csapat oldalaira mutató linkeket – ezzel el is készítettünk egy egyszerű webhelyet.

Ezután azonban szeretnénk továbblépni: logikusnak tűnik, hogy a csapatok oldalának tetején, a játékosok listája felett ott legyen a csapatvezető neve és elérhetősége, az edző neve, a csapat története, közös fotója, stb. A Taxonomy modul erre nem ad lehetőséget. A modul által létrehozott kategóriák csak egyszerű dobozok, amelyekbe betehetjük az egyes tartalmakat (a játékosok egyéni oldalait) – de a dobozon csak a kategórianév (Piros csapat, Kék csapat) szerepel, és nem írhatunk rá további információkat.

Második megoldás: Book modul

Próbálkozzunk most a Book modullal. Ennek bekapcsolása után a node oldalakon a Megtekintés és a Szerkesztés mellett megjelenik egy újabb fül: a Vázlat. Hozzunk létre egy "Csapatok" nevű oldalt, a Vázlat fülön nyilvánítsuk könyv címlapnak (azaz olyan oldalnak, amely legfelső szintű); majd hozzuk létre a csapatok oldalait, és ezeket rendeljük a könyv címlap mint "szülő" oldal alá. Mivel most a csapatok oldalai egyszerű Drupal könyvlapok, bármilyen információt rájuk írhatunk, így végre helyet kaphat a csapat elérhetősége, története, stb. Következő lépésben hozzuk létre a játékosok egyéni lapjait, és soroljuk be őket a csapat-oldalak alá. Struktúránk most így néz ki:

  • Csapatok
    • Piros csapat (node/1)
      • Játékos-1 (node/2)
      • Játékos-2 (node/3)
      • Játékos-3 (node/4)
      • stb.
    • Kék csapat (node/5)
      • Játékos-4 (node/6)
      • Játékos-5 (node/7)
      • stb.

Ez a megoldás már megfelel az elképzeléseinknek – és egyszerűbb, pár oldalas webhelyeken ennél többre nincs is szükségünk. Mi azonban szeretnénk további listázó oldalakat készíteni, például a játékosok neme (fiú-lány) szerint.

  • Piros csapat
    • Piros lányok
    • Piros fiúk
  • Kék csapat
    • Kék lányok
    • Kék fiúk

Ha Taxonomy modullal dolgozunk, a feladat egyszerű. Létrehozunk egy Nemek szótárat, abban két kifejezést (Fiúk, Lányok), majd a játékosokat besoroljuk valamelyik kategóriába. Ha szeretnénk a Piros lányokat listázni, csak kombinálnunk kell a kategóriák azonosítóit az oldalra mutató link végén, és a Drupal magától tudja, hogy mely játékos-oldalakat kell felvennie a listára.

  • Csapatok szótár
    • Piros csapat kategória (taxonomy/term/1)
    • Kék csapat kategória (taxonomy/term/2)
  • Nemek szótár
    • Fiúk kategória (taxonomy/term/3)
    • Lányok kategória (taxonomy/term/4)

A Piros lányok listáját a következő címen találjuk: www.honlapneve.hu/taxonomy/term/1,4.

A Kék fiúkat pedig itt: www.honlapneve.hu/taxonomy/term/2,3.

A megoldás kifogástalanul működne – ha nem vetettük volna el a kategóriák használatát akkor, amikor kiderült, hogy nem tudjuk a csapatok elérhetőségét a csapat-oldal tetejére kiírni. A Book modullal készített Piros csapat és Kék csapat viszont nem kategória, hanem közönséges oldal, ezért ezeket nem tudjuk a Fiúk-Lányok kategóriákkal kombinálni.

Harmadik megoldás: mindent bele!

Kézenfekvőnek tűnik, hogy a két rendezési módszert kombináljuk, azaz a játékosok oldalait tegyük be kategóriákba (Piros csapat, Kék csapat, Fiúk, Lányok), és egyúttal soroljuk be őket a Piros csapat és Kék csapat c. könyvlapok alá. Ezzel megoldódott a kombinált listázás problémája. Ugyanakkor most egy zavaró jelenséget tapasztalhatunk: ha látogatónk felkeresi a Piros csapat könyvlapját (elérhetőségekkel, csapattörténettel, stb.), majd onnan elnavigál Játékos-1 személyes oldalára, ott találja Játékos-1 neve mellett a Piros csapat kategóriára mutató linket. Ha tehát az oldal elolvasása után a látogató szeretne a Piros csapat többi tagjával is megismerkedni, akkor rá fog kattintani erre a linkre, ez viszont nem a Piros csapat könyvlapra mutat (ahol az előbb járt, és ahová szeretne visszalépni), hanem a Piros csapat nevű kategória listázó oldalára (amely a látogató számára is nyilvánvaló módon nem azonos a korábban megtekintett csapat-oldallal).

A problémát rövid úton megoldhatjuk, ha eltüntetjük a könyvlapokról a kategória-linkeket. Keressük meg a sminkünk könyvtárában a node.tpl.php nevű fájlt, és készítsünk róla ugyanebbe a könyvtárba egy másolatot node-book.tpl.php néven. Ezzel lényegében arra utasítottuk a Drupalt, hogy könyvlapok esetén a megjelenítéshez ne a node.tpl.php fájlt használja, hanem a most létrehozott node-book.tpl.php-t. Ezután nyissuk meg a fájlt egy kódszerkesztő programmal (Windows alatt pl. Notepad++), és töröljük a taxonómiára vonatkozó részt. Garland smink esetén a törlendő kódrészlet a következő:

Ha most felkeressük bármelyik játékos oldalát, nem látjuk a zavaró kategória-linkeket. Ezzel azonban annak lehetősége is megszűnt, hogy látogatóink maguktól felfedezzék a kombinált kategóriák oldalait (Piros lányok, Kék fiúk) – így ezekre külön fel kell hívnunk a figyelmet. Ehhez kapcsoljuk be az alapcsomagban kapott Path modult, amelynek segítségével egyszerűen megjegyezhető útvonal álneveket rendelhetünk oldalainkhoz. A Piros csapat oldalához a szerkesztő űrlapon adjuk meg a piros útvonal álnevet (az oldal ezután a www.honlapneve.hu/piros címen is elérhető lesz), a Kék csapat oldalához pedig a kek-et; majd vegyük fel a két oldalra mutató linket az elsődleges menübe. A játékosok oldalainak szintén adjunk egyedi útvonalneveket, ahol az útvonal első tagja mindig a csapat neve legyen: piros/kisspal, kek/nagypeter.

Ezután készítsünk egy menüt, amelynek elemei a Piros lányok, ill. Piros fiúk oldalára mutatnak; majd a blokk beállítások között határozzuk meg, hogy ez a menü csak a piros és a piros/* útvonalon található oldalakon jelenjen meg. Ugyanezt ismételjük meg a Kék lányok, Kék fiúk esetén. Most tehát ha látogatónk elnavigál bármelyik csapat, vagy játékos oldalára, akkor feltűnik a lapon egy újabb menü, amelyen keresztül eljuthat a Piros lányok, Piros fiúk, ill. Kék lányok, Kék fiúk oldalakra.

Tartalomszervezési megoldások II. - Views és CCK modul

Negyedik megoldás: Views

Tételezzük fel, hogy egyesületünk honlapján a csapatnevek mellet nem 2 hanem 4 további kategóriát szeretnénk bevezetni: Fiúk, Lányok, Hazai játékosok, Vendégjátékosok. Ez a – rendkívülinek nem nevezhető – helyzet azt eredményezi, hogy webhelyünk szerkezete, és ezzel párhuzamosan a menürendszer kényelmetlenné és a kategóriák közötti átfedésektől függően nehezen áttekinthetővé vált:

  • Piros csapat
  • Piros fiúk
  • Piros vendégjátékosok
  • Piros vendégjátékos fiúk
  • Fiúk
  • Hazai játékos fiúk
  • ...stb.

Ha például azt szeretnénk, hogy a hazai játékosoknak és a vendégjátékosoknak a csapatok mintájára külön oldaluk legyen (ne csak egy-egy lista), akkor ezeknek külön könyvet kell nyitnunk; és mivel egy oldalt (könyvlapot) csak egy könyvbe tudunk beilleszteni, a játékosok oldalait kétszer kell felvinnünk a honlapra. Hasonló problémát okoz az a korlátozás, hogy egy node-hoz csak egy útvonal álnevet rendelhetünk, ezért pl. egy Piros csapatban játszó vendégjátékos fiú oldalán vagy a Piros fiúk, vagy a Vendégjátékosok segédmenüpontot fogjuk tudni megjeleníteni (hacsak nem használunk többszörösen összetett útvonal álneveket, amivel éppen az álnév lényegét – egyszerűen megjegyezhető internetcím – veszítjük el). Ezen a ponton már a Drupal alapcsomag korlátait feszegetjük, és éppen ideje kiegészítők után néznünk: ismerkedjünk meg a Views modullal.

A Views a Drupal rendszer által készített listák felülírására, valamint új listák létrehozására használható. A felülírás azt jelenti, hogy egy adott webcímen, pl. a www.valami.hu/taxonomy/term/1 alatt nem a szokásos kategória-listát látjuk, hanem egy általunk megadott szempontok szerint módosított felsorolást. Lássuk, milyen módosításokat tesz lehetővé a Views:

  • Lista megjelenítése nem csak önálló oldalon, hanem blokkban is
  • Hozzáférés korlátozása felhasználói csoportok szerint
  • Oldalaknak, blokkoknak egyéni cím
  • Oldalaknak, blokkoknak egyéni fejléc és lábléc
  • Listázott elemek megjelenítési módjának meghatározása (teljes node, bevezető, táblázat, felsorolás)
  • Listában szereplő node-ok számának meghatározása
  • Listában szereplő mezők meghatározása (pl. cím, beküldési idő, szerző neve, stb.)
  • Lista szűkítése kategóriák, tartalomtípusok, közzétételi beállítások szerint
  • Lista rendezése növekvő vagy csökkenő sorrendbe
  • ... stb.

A fenti felsorolás nem teljes, de talán érzékelteti a modul sokoldalúságát. Számunkra most a legérdekesebb az a lehetőség, hogy a listázó oldalakhoz egyéni fejlécet készíthetünk. Eredeti problémánk az volt, hogy nem tudtuk a kategória listázó oldalak tetejére beszúrni a csapatok elérhetőségét és egyéb információit – ezt a feladatot szépen megoldhatjuk a listázó oldal felülírásával. Készítsük el tehát a kategóriákat és vigyük fel a játékosok oldalait:

  • Piros csapat (taxonomy/term/1)
    • Játékos-1 (node/1)
    • Játékos-2 (node/2)
    • Játékos-3 (node/3)
    • stb.
  • Kék csapat (taxonomy/term/2)
    • Játékos-4 (node/4)
    • Játékos-5 (node/5)
    • stb.

Ezután az admin/build/views/add oldalon készítsük el a nézeteket, amelyekkel felülírjuk a taxonomy/term/x oldalakat:

  • Name (név): piroscsapat
  • Access (hozzáférés): mivel mindenki számára elérhetővé kívánjuk tenni az oldalt, ne jelöljük be egyik csoportot sem
  • Description (leírás): Piros csapat oldala (ezt a szöveget csak az adminisztrátor látja)
  • Page (oldal)
    • Provide page view (oldal készítése): kipipálva
    • URL (webcím): taxonomy/term/1
    • View type (nézet típusa): Teaser List (bevezetők)
    • Title (az oldal látható címe): Piros csapat
    • Use Pager (lapozó használata): kipipálva
    • Breadcrumb trail should not include "Home" (A morzsa ne tartalmazza a 'Nyitólap' linket): hagyjuk üresen
    • Nodes per Page (node-ok száma): 10
    • Header (fejléc): ide illeszthetjük be a Piros csapatra vonatkozó információkat; ha HTML vagy PHP kódot használunk, ne felejtsük el átállítani a beviteli módot
  • Filters (szűrők):
    • Node: Published -> Equals: Yes (csak a közzétett oldalak szerepeljenek a listán)
    • Taxonomy: Terms for Csapatok -> Is One Of: Piros csapat (csak a 'Piros csapat' kategóriába sorolt cikkek szerepeljenek a listán); Option: 10 (ha szeretnénk, hogy a 'Piros csapat' kategória alá besorolt esetleges alkategóriák tartalmát is listázza)
  • Sort Criteria (sorrendezési szabályok):
    • Node: Sticky -> Order: Descending ('kiemelt' cikkek az oldal tetejére, csökkenő időrendi sorrendben)
    • Node: Created Time -> Order: Descending (többi cikk csökkenő időrendi sorrendben)
  • Látogassunk el a www.valami.hu/taxonomy/term/1 oldalra: a korábbi egyszerű listázó oldal helyett most a fejléccel kiegészített, teljes értékű Piros csapat oldalt látjuk. Természetesen használhatunk egyszerűen megjegyezhető webcímeket is; ehhez navigáljunk az 'Útvonal álnevek' menüpont alá, és adjuk meg, hogy a taxonomy/term/1 mellett legyen oldalunk a piros címen is elérhető. A 'Menük' oldalon állítsunk be egy piros címre mutató menüpontot is – ezzel el is készültünk a Piros csapat oldalával. Ismételjük meg a nézet készítés, útvonal álnév megadás, menüpont készítés lépéseket valamennyi kategóriánkra. Ha ezek után felkeressük bármelyik játékos oldalát, ott találjuk a kategória linkeket (Piros csapat, Fiúk, Vendégjátékosok); és ha bármelyik linkre rákattintunk, a Views által készített, fejlécekkel/láblécekkel feljavított nézet-oldalakra lépünk. Ezeket az oldalakat igen jól lehet sminkelni, tehát egyéni megjelenést is tudunk nekik adni; emellett a fejléc/lábléc szövegbe egyszerűen beilleszthetünk kombinált taxonómia-linkeket (pl. a Fiúk oldalon a hazai és vendégjátékos fiúk listájára mutató linkek), ill. Insert View modullal bármilyen más nézetet – így kordában tudjuk tartani a csak bizonyos oldalakon megjelenítendő másodlagos menük burjánzását. Webhelyünk ezzel áttekinthető szerkezetet és következetes navigációs struktúrát kapott.

    Megjegyzés: A Views nézetkészítő űrlapján lehetőség van URL-ként útvonal álnevek megadására, valamint menüpont készítésre is. Ezeket a szolgáltatásokat akkor célszerű használni, ha teljesen új, a rendszerben nem szereplő listát készítünk (pl. ha a "Piros csapat" kategóriában szereplő "kép" típusú tartalmakat szeretnénk kigyűjteni egy külön képgaléria számára). Ha egy meglévő listát szeretnénk felülírni, akkor először készítsük el a nézetet a rendszer által meghatározott URL (pl. taxonomy/term/x) megadásával, majd külön lépésben definiáljuk az útvonal álnevet és a menüpontot. Ezzel elkerülhető, hogy webhelyünkön egymás mellett éljen a hagyományos oldal és annak felülírt verziója, ami esetleg megzavarhatja a barangoló látogatókat.

    Ötödik megoldás: CCK + Views

    A Views modullal már egészen összetett webhelyeket is építhetünk anélkül, hogy egyetlen sor PHP-kódot kellene írnunk; mégis elképzelhető néhány olyan eset, amikor még ez a megoldás sem elégíti ki az igényeinket. A Views kezelőfelülete meglehetősen bonyolult, márpedig minden egyes alkalommal, amikor új nézetet készítünk (pl. új kategóriával bővül a webhely), vagy meglévőt módosítunk (megváltozott a csapat telefonszáma), akkor kénytelenek vagyunk ezen a barátságtalan űrlapon dolgozni. Húsz-harminc nézetnél többet ilyen módon kezelni még akkor is kényelmetlen, ha a webhely karbantartását hozzáértő webmester végzi – laikusoktól pedig egyáltalán nem várható el, hogy kiigazodjanak a Views bokros opciói között.

    Szintén tovább kell lépnünk akkor, ha szeretnénk kétszintes honlapunkat (játékosok, csapatok) három- vagy többszintessé bővíteni. Képzeljük el azt az esetet, amikor több tucat csapatunk van, és szeretnénk a csapatok részvételével versenyeket szervezni. Létrehozhatunk egy Versenyek kategóriát, de ehhez nem tudjuk hozzárendelni a csapatokat, mert azok nem node-ok, hanem kategóriák, ill. a kategóriák módosításával létrehozott nézetek. Ennek a problémának a megoldására született a Category kiegészítő modul – az ezzel a modullal létrehozott kategóriák tehát egyúttal node-ok is, amelyek tetszőleges információkat (elérhetőségek, verseny helyszíne, időpontja, stb.) tartalmazhatnak, és amelyeket a szokásos módon kategorizálhatunk és listázhatunk. A Category felülete azonban legalább olyan bonyolult, mint a Views modulé, és csupán egyetlen szolgáltatást kínál. Cikkünk megírásának időpontjában általában jobb megoldás a Content Construction Kit (rövidítve CCK) modul használata.

    A Drupal alapcsomaggal létrehozott tartalomtípusok csak két mezőt tartalmaznak: a címet és a törzset. A CCK modul legfontosabb szolgáltatása, hogy lehetővé teszi a tartalomtípusok bővítését további mezőkkel. Ha jól strukturálható adatokkal dolgozunk, akkor a CCK segítségével a tartalmak beküldését és megjelenítését nagymértékben szabályozni tudjuk. Példánknál maradva hozzunk létre 3 tartalomtípust a szükséges mezőkkel:

    • Játékos
      • Name: Játékos
      • Type: jatekos
      • Title field label: A játékos neve
      • Body field label: A játékos életrajza
      • Text field: A játékos telefonszáma
      • Node reference: Csapat
    • Csapat
      • Name: Csapat
      • Type: csapat
      • Title field label: A csapat neve
      • Body field label: A csapat története
      • Text field: Az edző neve
      • Text field: Az edző telefonszáma
      • Node reference: Játékos
    • Verseny
      • Name: Verseny
      • Type: verseny
      • Title field label: A verseny neve
      • Body field label: A verseny leírása
      • Text field: Helyszín
      • Date field: Időpont
      • Node reference: Csapat

    A tartalomtípusok létrehozása után a "Tartalom beküldése" menüpont alatt megjelenik a 3 típus – az ezekhez tartozó beküldő űrlap kitöltése pedig a laikus felhasználó számára sem okoz gondot. Tartalomszervezés szempontjából figyelemre méltó a "Node reference" mező, amely megoldást jelent kategorizálási problémáinkra: ennek segítségével a versenyzőket a csapatokhoz, a csapatokat pedig a versenyekhez tudjuk rendelni. A gyakorlatban ez azt jelenti, hogy például egy új játékos létrehozása két lépésből áll: először is létre kell hoznunk a "játékos" típusú tartalmat, amelynek során kiválasztjuk a node reference listából, hogy az új ember melyik csapatban játszik; majd az adott csapat node reference listájára fel kell vennünk az új játékost. A munkafolyamatot tovább egyszerűsíthetjük, ha a csapatok tagjait nem node reference útján határozzuk meg, hanem Views modullal készítünk egy csapattagokat listázó nézetet, majd ezt beágyazzuk a Csapat tartalomtípusba.

    • URL: csapat
    • Filters:
      • Node Published -> Equals: Yes
      • Node: Type -> Is One Of: Játékos
    • Arguments:
      • Argument type -> Node reference: Játékos (field_csapat)
      • Default -> Summary, unsorted

    Ezzel lényegében megmondtuk a Views-nak, hogy készítsen listát azokból a játékos node-okból, amelyek rendelkeznek field_csapat nevű CCK-s mezővel. Ha a nézet nem kap argumentumot az URL végén (pl. a www.honlapneve.hu/csapat címen), akkor készít egy összesített listát, zárójelben a csapat játékosainak számával:

    • Piros csapat (20)
    • Kék csapat (18)

    Ha a nézet az URL végén argumentumot kap (pl. www.honlapneve.hu/csapat/12), akkor kilistázza az adott csapatra – esetünkben a node/12 azonosítójú, csapat típusú tartalomra – node reference útján hivatkozó node-okat, azaz a csapat játékosait. Ezt a nézetet Viewfield modullal tudjuk beágyazni a csapat node-okba:

    1. Egészítsük ki a Csapat CCK-s tartalomtípusunkat egy Viewfield mezővel.
    2. Argumentumként adjuk át az aktuális node azonosítóját: %nid.
    3. Ha ezek után felkeressük bármelyik Csapat típusú node-ot, ott látjuk az adott node-ra node reference útján hivatkozó játékosok listáját.

    A CCK és a Views a legdinamikusabban fejlesztett kiegészítő modulok közé tartoznak, egyes funkcióik hamarosan az alapcsomagban is megjelennek. Számíthatunk rá, hogy a jövőben a hasonló rendszerező oldalak készítése tovább fog egyszerűsödni. Elég jó angol nyelvű dokumentáció található róluk a Drupal.org kézikönyvében:

    További újdonságot jelenthet a Book modul tervezett átalakítása, amely kimondottan a hierarchikus honlapok készítését fogja megkönnyíteni.

    Remélhetőleg a fenti példákból is nyilvánvalóvá vált, hogy szinte bármilyen tartalomszervezési problémával kerülünk szembe, találni fogunk a Drupal kínálatában olyan megoldást, amellyel PHP kód írása nélkül, esetleg pár soros kódrészlet beillesztésével a feladat megoldható. Azonban a Drupal csak a kódolás terhét tudja levenni a vállunkról – gondolkodni nem tud helyettünk. A számunkra optimális megoldást csak akkor tudjuk kiválasztani, ha a webhely építését megelőzően elemezzük, modellezzük a feladatot, és még a tartalmak tömeges felvitele előtt alaposan tesztelünk.

    CsatolmányMéret
    cck_viewfield.png13.35 KB

    Többnyelvű oldal fejlesztése Drupal 6.x alatt

    A több helyről érkező pozitív információ nyomán elhatároztam, hogy kipróbálom a Drupal alapú tartalomkezelés gyakorlati megvalósítását. Teljes mértékben kezdőként érkeztem a feladathoz, ezért rengeteg olvasással és videó nézéssel töltöttem a kezdeti időszakot, köszönet érte a szerzőknek. Ennek eredményeként úgy éreztem, hogy az elméleti alapokat megszereztem (természetesen ez a gyakorlat során kiderült, hogy nem igaz), nekiállhatok a tényleges fejlesztésnek.

    Ha az ember többnyelvű fejlesztésbe kezd akkor a Drupal több lehetőséget is felkínál erre. Lehetőségünk van a keretrendszer és tartalom vagy csak a tartalom többnyelvű megjelenítésére. Ez utóbbi az egyszerűbb megoldás:

    1. A 6-os verzióban található locale modult (Adminisztráció -> Webhely építés -> Modulok) be kell kapcsolni.
    2. Ha a drupal alaprendszert is szeretnénk több nyelven, akkor a kívánt nyelve(ke)t letölteni a Drupal webhelyéről, és kicsomagolás után bemásolni a Drupal gyökérkönyvtárba a könyvtárakat és fájlokat.
    3. Az Adminisztráció -> Webhely beállítása -> Nyelvek oldalon a nyelv hozzáadása linkre kattintva a kívánt nyelv kiválasztása és mentés.
    4. A tartalom típusoknál a megfelelő tartalomra ( Adminisztráció -> Tartalom kezelés -> Tartalom típusok ) engedélyezni kell az "Általános beállítások" alatt a "Több nyelv támogatása:" választógombok közül a "Engedélyezett, fordítás támogatással" opciót.

    Ezután a megadott tartalom típusoknál tartalom beküldésekor kiegészül a beviteli forma egy "Nyelv" választólistával, ahol megadhatjuk a tartalomhoz választott nyelvet. A tartalom mentése után feltűnik egy újabb opció a tartalomnál "Fordítás".

    Erre kattintva egy listát kapunk, ahol a tartalom fordításai vannak felsorolva. A különböző nyelvekre kattintva tudjuk a fordítást elvégezni. Amennyiben a fordítást megcsináltuk, akkor a tartalom alatt megjelennek a nyelvek, amin a fordítás elérhető, ezekre kattintva az adott nyelvre fordított szöveg jelenik meg. Ezzel a tartalmak többnyelvű megjelenítését el is végeztük.

    Hátradőlhetünk, illetve hátradőlhetnénk, ha egy többnyelvű felülettől csak ennyit várnánk el. Egy igazi többnyelvű oldal azonban több kihívást rejt magában, amikor a címeket, menüket és tartalmakat is úgy akarjuk elkészíteni, hogy az adott nyelvet beszélő felhasználók otthon érezzék magukat az oldalunkon. Ez már keményebb dió, de a Drupalos fejlesztők erre is kidolgozták a megoldásokat. Ez az írás azért született, hogy könnyebben tudjanak eligazodni a hozzám hasonló kezdők ennek a megvalósításában.

    Mire lesz szükségünk?

    A fentebb írt 1-3 pontokban vázoltak végrehajtására.

    Az alaprendszeren túl szükségünk lesz az Internationalization modulra, jó ha megjegyezzük, hogy ez az i18n modul, mindenhol így hivatkoznak rá.

    Miután telepítettük a webszerverre, be kell kapcsolnunk a modulok között is: Adminisztráció -> Webhely építés -> Modulok a Multilanguage szekcióban kapcsoljunk be mindent amit csak lehetséges, majd mentsük a beállításokat.

    A nyelvválasztó menüt meg kell jeleníteni, ez fontos szerepet tölt be már a fejlesztés során is. Az Adminisztráció -> Webhely építés -> Blokkok oldalon a Nyelv választó blokkot pozicionáljuk az általunk preferált helyre. Mentés.

    Ezután menjünk a Adminisztráció -> Webhely beállítása -> Multilingual system oldalra és állítsuk a "Content selection mode: " választómezőt a Mixed current language (if available) or default language (if not) and language neutral opcióra.

    Ezzel nagy lépést tettünk előre, de korántsem eleget. Ha eddig nem szerettünk belepiszkálni az alapbeállításokba, most erőt kell venni magunkon és határozott mozdulatokkal be kell másolnunk a következő blokkot
    /**
    * Multilingual settings
    *
    * This is a collection of variables that can be set up for each language when i18n is enabled.
    * These are the basic ones for Drupal core, but you can add your own here.
    */
    $conf['i18n_variables'] = array(
    // Site name, slogan, mission, etc..
    'site_name',
    'site_slogan',
    'site_mission',
    'site_footer',
    'anonymous',
    // Different front page for each language
    'site_frontpage',
    // Primary and secondary links
    'menu_primary_links_source',
    'menu_secondary_links_source',
    // Contact form information
    'contact_form_information',
    );
    a telepített drupal rendszerünk sites/default/settings.php fájl végére.

    Ezzel elérjük, hogy a telepített nyelveken is meg tudjuk adni a webhely adatait. Ehhez menjünk az Adminisztráció -> Webhely beállítása -> Webhely információk oldalra és figyeljük meg a beviteli mezők alatti feliratot, miszerint a "Név:" mezőnél például a következő szöveg látszik: "A webhely neve. This is a multilingual variable". Furfangos ennek a kezelése, mert mindig a kiválasztott nyelven kell megadni a szövegeket az ilyen információval bíró mezőknél, majd mentés. Így létrejönnek jelen esetben a webhely információi az összes nyelven, amit kiválasztunk és megadjuk az adatokat, majd elmentjük. Ha ezek után nyelvet váltunk, már ezek az adatok az adott nyelven jelennek meg az oldalunkon. Hurrá! Nem is volt olyan nehéz. Akkor most hozzunk létre menüpontokat.

    Adminisztráció -> Webhely építés -> Menük és válasszuk az Elsődleges linkek menüt. Adjunk hozzá egy menüpontot. Keressünk olyan mezőt ahol látjuk a "This is a multilingual variable" feliratot. Igen kedves olvasó, nem találunk ilyen mezőt, az elsődleges és másodlagos linkek kezelése egy kicsit másként működik. Ha létrehoztuk ezt a menüpontot akkor töröljük, így semmit nem tudunk kezdeni vele. De akkor hogy lesz nekünk többnyelvű oldalunk? Semmi izgalom, a megoldás a következő:

    Adminisztráció -> Webhely építés -> Menük a Menü hozzáadása oldalon adjunk hozzá annyi menüpontot ahány nyelvet felvettünk a Nyelv hozzáadása oldalon. Például egy Magyar, Angol oldalnál vegyünk fel egy menüt "Magyar" és egy menüt "Angol" néven (bármilyen szöveg megadható ). Ha ezek megvannak, akkor az Adminisztráció -> Webhely építés -> Menük oldalon válasszuk a Beállítások pontot. És lássunk csodát! Az "Elsődleges hivatkozások forrása: " választómezőnél láthatjuk a "This is a multilingual variable." feliratot. Innentől könnyű dolgunk van: az éppen kiválasztott nyelv megfelelő menüpontját válasszuk ki (pl. Magyar) és mentsük a beállítást, majd ismét a Beállítások menü, váltsunk másik nyelvre és válasszuk forrásnak a másik hozzáadott menüt (Angol), mentés. Innentől nincs más dolgunk, mint létrehozni a Magyar, Angol, stb. menük alatt a menüpontokat. Ezek fognak megjelenni a kiválasztott nyelvnél.

    Most elértük, hogy az oldal több nyelven is tud kommunikálni velünk, már csak a tartalmaknál kell ugyanezt elérni. A fent vázolt megoldás helyett, már sokkal elegánsabb megoldást is választhatunk. Nem kell minden tartalom alatt megjelenniük a nyelveknek, már a nyelv választó menüben választott nyelven "automatikusan" is megjelenhetnek.

    Én a taxonómiát választottam a megoldás kidolgozására. Ezért erről tudok most írni, bizonyára van több megoldás is.

    Menjünk az Adminisztráció -> Tartalom kezelés -> Taxonómia oldalra és adjunk egy szótárt hozzá a Szótár hozzáadása menüvel.

    A "Translation mode" választómezőben válasszuk a "Localize terms. Terms are common for all languages, but their name and description may be localized." opciót. Nyelvnek azt a nyelvet amilyen nyelven a "Szótár nevét" megadjuk. Innentől a szokásos eljárás, tartalom típus ahol használni szeretnénk valamint a választás módja a tartalomnál majd. Mentés.

    A szótárhoz most adjunk kifejezéseket a "kifejezések listája" linkre kattintva. Lehetőleg a szótárral azonos nyelven hozzuk létre a kifejezések nevét. Ezt majd később fogjuk fordítani, és bár nem próbáltam másként, logikailag az a gyanúm, hogy a szótárnál megadott nyelv lesz a fordítás alapja.

    Ha az összes kifejezést megadtuk akkor kezdhetjük lefordítani az Adminisztráció -> Webhely építés -> Felület fordítása oldalon. A "Keresés" linkre kattintva egy elég komplex oldalra jutunk, itt jelen esetben érdemes a "Keresés csak a következőkben: " választólistából a "Taxonómia" pontot választani, majd a "Keresés"-re kattintani. Ha mindent jól csináltunk akkor itt megjelennek a taxonómiáknál megadott szövegeink amiket elkezdhetünk lefordítgatni a szerkesztés linkre kattintva.

    Én az angolt választottam default nyelvnek, nem tudom, hogy mi történik, ha más nyelven hozzuk létre a szótárakat a kifejezésekkel.

    Remélem, hogy minden fontos lépést leírtam, ezáltal használható lesz olyan embereknek akik hozzám hasonlóan most ismerkednek a Drupal-lal, illetve a többnyelvű felületekkel Drupal alatt.

    Dudás József

    egyedi regisztrációs űrlap létrehozása

    Feladat! Olyan regisztrációs felület létrehozása, mely túlmutat a Profil űrlapi mezők nyújtotta lehetőségeken. (pl. CCK mezők használata)

    Megoldás!

    Telepítendő modulok:
    - Content Profile
    - CCK
    - Conditional Fields

    Beállítás:
    CCK:
    A modul telepítésénél engedélyezzük a használni kívánt felületi elemeket. (Fieldgroup, Option Widgets)
    Content profile: A modult betöltve létrejön egy Profil tartalom típus. Vele egy tartalmat van lehetőségünk létrehozni. (ezt fogjuk majd felhasználni a regisztrációkor)
    Az Adminisztráció/Tartalomkezelés/Tartalomtípusok oldalon beállíthatjuk az újonnan létrejött típusunkat.
    A Szerkesztés alatt az általános tartalomtípus beállításokat találhatjuk. Új csoportként megjelenik a Tartalomprofil, ahol az "Ennek a tartalomtípusnak a használata, mint felhasználói tartalomprofil" opciót találjuk. Ezt engedélyezzük!
    A tartalom típus szerkesztő ablakának felső területen megjelennek extra hivatkozások, ezek sorban:
    Tartalomprofil: Itt lehet beállítani a profilinformációra vonatkozó opciókat. Fontos megemlíteni a Felhasználó regisztráció csoportban található Használat regisztráció során opciót. Ezt bejelölve engedélyezhetjük a tartalom típus, mint regisztrációs űrlapként történő használatát.
    Ha felhasználói döntéstől függő beviteli mezőket szeretnénk, akkor a mező hozzáadásakor (amennyiben select típusú mezőt hozunk létre) megjelenő beállító oldalon a Conditional fields settings csoportban tudjuk beállítani, hogy mitől függjön az adott elem megjelenése.
    Fontos, hogy csak azonos csoportba rendezett választó mezőktől lehet függővé tenni az adott beviteli mezőt.
    Létrehozzuk a kívánt regisztrációs tartalomtípusunkat és elérhető, mint egyedi regisztrációs
    űrlap.

    Remélem hasznos volt, hogy összefoglaltam a teendőket.

    Melyik modulhoz, modulokhoz kapcsolódik a téma?: 
    Drupal verzió: 

    Útvonal kiegészítése az aktuális oldal címével

    Feladatul kaptam, hogy az útvonalban (breadcrumb) jelenjen meg az aktuálisan megjelenített tartalom címe is. Úgy gondoltam, hogy a smink módosításával érdemes megoldani a problémát, hiszen az útvonalak összeállítása különböző helyeken történik a Drupalban.

    Megkerestem a következőt a page.tpl.php-ben (phptemplate esetén)

     print $breadcrumb;

    Majd lecseréltem a következőre:

     
            mb_regex_encoding('utf-8');        
            print mb_ereg_replace('</div>',' ? '.$title.'</div>',$breadcrumb); 

    Persze lehetett volna "egyszerűbben" is:

     print $breadcrumb.' ? '.$title;

    Ekkor viszont a $title nem kerül bele a megfelelő <div> elembe így nem lesz hasonlóan formázva mint az útvonal többi eleme.

    Fontos felhívni a figyelmet, hogy mindig a multibyte string függvényeket használjuk, hisz a Drupal UTF-8 kódolással tárolja a szövegeket, és magyarok lévén nagy valószínűséggel találkozunk olyan esettel, amikor a hagyományos szövegkezelő függvények nem működnek megfelelően.

    Üdvözlőszöveg

    Andrássy Tamás kérdezte, hogyan lehetne hasonló képernyőt előállítani, mint ami a Drupal telepítésekor fogad minket. Mivel Tamásnak nagy köszönettel tartozunk, hiszen az ő lelkesedése hívta életre a Drupal.hu -t, ezért elkészítettem neki az alábbi modult, amit közre is adok, hátha más is szeretne hasonló nyitóoldalt:

    function cimoldal_menu($may_cache) {
    $items = array();
    if ($may_cache) {
    $items[] = array('title' => '', 'path' => 'cimoldal', 'callback' => 'cimoldal_page', 'type' => MENU_CALLBACK, 'access' => TRUE);
    }
    return $items;
    }
    function cimoldal_page($nid) {
    $result = db_query('SELECT body, format FROM {node} WHERE nid = %d', $nid);
    if ($node = db_fetch_object($result)) {
    print theme('page', theme('node', check_output($node->body, $node->format), FALSE, TRUE));
    }
    else {
    drupal_not_found();
    }
    }
    ?>

    Ez definiál egy cimoldal nevű Drupal útvonalat, ami után egy tartalom azonosító kell. Tehát, ha a node/1234 -ban van a tartalom, akkor cimoldal/1234 -en fogjuk találni a tartalom szövegét és a blokkokat (ne feledjük, hogy megadható, hogy melyik blokk hol jelenjen meg és hol nem).

    Grafikus felső menü

    A Drupal egyik apróbb problémája, hogy a felület nem nyújt lehetőséget szép, grafikus felső menüt összeállítani. Ezen könnyen segíthetünk egy megfelelő smink használatával. Vegyük a Marvin 2K sminket alapul, én annak a PHPtemplate-es változatából szoktam kiindulni.

    Az ötlet a List Apartról származik. A lényege az, hogy CSS-el készítünk kliens oldali térképet, mégpedig úgy, hogy linkeket megfelelő méretre "felfújunk" és a helyükre tolunk. Ilyenkor a linkek szövege csak zavaró, ezért azokat eltüntetjük. Háttérnek pedig betesszünk egy képet, ami a menüt alkotó ikonok egymás mellé rakásából keletkezett.

    A layout.css-ben a következő maradt meg a #main-nav -ra vonatkozó szabályokból:


    #main-nav {
    position: absolute;
    left: 180px;
    top: 53px;
    margin: 0;
    padding: 0;
    height: 69px;
    width: 435px;
    }

    A style.css-ben pedig ez:


    #main-nav {
    background: url(menu.jpg) no-repeat top left;
    }
    #main-nav li {
    list-style-type: none;
    float: left;
    display: inline;
    position: relative;
    width: 86px;
    }
    #main-nav li a {
    width: 86px;
    height: 69px;
    display: block;
    text-decoration: none;
    }
    #main-nav a span {
    visibility: hidden;
    }

    A phptemplate.engine-ben keressünk $links[$type][]-re, és azt a sort cseréljük a következőre:

    $links[$type][] = l(''. $value['text'][$i] .'', $value['link'][$i], $attributes);
    ?>

    Ezzel készen is vagyunk. Természetesen nem biztos, hogy pont 5*86 széles lesz a menüképünk, változassunk a fenti CSS-en értelemszerűen. De mi van akkor ha netán nem egyforma szélesek a menüikonok? Vagy szeretnénk a hivatkozott List Apart cikkhez hasonló módon rollovereket is? Ekkor a fenti sor helyett a következő kettőt írjuk a phptemplate.engine-be:

    $attributes['id'] = 'main-nav-link-'. $i;
    $links[$type]['main-nav-list-'. $i] = l(''. $value['text'][$i] .'', $value['link'][$i], $attributes);
    ?>

    És még a page.tpl.php-ben kell egy apró módosítás, keressünk ul id="main-nav"-re, és cseréljük a következőre:

    Így elértük, hogy minden egyes listaelemre és linkre egyedi azonosítóval hivatkozhatunk a CSS-ben: main-nav-list-0, main-nav-list-1 stb. illetve main-nav-link-0, main-nav-link-1 stb.

    Banner modul telepítése

    Az egyik barátom éppen most élesztette fel a banner modult Drupal 4.5.x alatt. Segítséget kért, mert hiába kapcsolta be a modult és állitott abban be bármit, a reklámcsíkok sehogy se akartak megjelenni. A hiba ott volt, hogy a xtemplate.patch fájlban leírtak szerint kellett volna módosítania három fájlt. Mivel a módosítások leírása nem volt igazán felhasználóbarát, úgy gondoltam, megpróbálom emberi nyelven leírni a lépéseket. Ezek a változtatások csak azoknak működnek, aki xtemplate alapú sminket használnak. Az alap rendszerben ilyen a bluemarine és a pushbutton.

    A themes/engines/xtemplate/xtemplate.engine fájlba a 139. sor környékén kell beszúrni a +-al megjelölt sorokat:


    $xtemplate->template->parse('header.site_name');
    }
    + if (function_exists('banner_display')) {
    + $xtemplate->template->assign('banner', banner_display());
    + $xtemplate->template->parse('header.banner');
    + }
    +
    if (theme_get_setting('toggle_slogan')) {
    $xtemplate->template->assign('site_slogan', variable_get('site_slogan', ''));
    $xtemplate->template->parse('header.site_slogan');

    A themes/bluemarine/xtemplate.xtmpl fájlba a 29. sor környékén kell beszúrni a +-al megjelölt sorokat.

    +

    +
    +

    +

    +

    +
    +

    {secondary_links}
    {primary_links}

    A themes/pushbutton/xtemplate.xtmpl fájlba a 33. sor környékén kell beszúrni a +-al megjelölt sorokat:

    +

    +
    +

    +

    +

    +
    +

    +

    {primary_links}

    Amint megvannak a változtatások, egyből működnie kell mindennek. Ám ismerősömnek nem felelt meg a reklámcsík helye. Ahelyett, hogy (telefonon keresztül) nekiálltunk volna a smink közös szerkesztésének, egy új blokkot készitettünk, a következő tartalommal:



    Ezzel elértük azt, hogy a reklámcsíkot bármelyik blokk pozícióra kirakhatjuk az oldalra. Természetesen ahhoz, hogy az eredeti helyéről eltűnjön a banner, vissza kellett állítani a három fájlt az eredeti állapotára.

    Egy kódbázisra több Drupal - nagy változások

    Eddig is volt erre lehetőség de most már a fejlesztői verzióban sokkal széles körűbbek a lehetőségek: nemcsak saját adattáblákat és/vagy adatbázist használhatunk, hanem saját modulokat és sminkeket is. Rögvest le is fordítottuk az INSTALL.txt vonatkozó részét.

    Az alapértelmezett beállításokat a feltelepített Drupal rendszer sites/default/settings.php fájlja tartalmazza. A további webhelyek beállításait alkönyvtárakba kell elhelyezzük. Minden webhely alkönyvtárának tartalmaznia kell egy settings.php fájlt, ezt legegyszerűbben az alapértelmezett settings.php lemásolásával és értelemszerű módosításával állíthatjuk elő. Az alkönyvtár nevét a webhely URL-jéből állítja elő a rendszer.

    A valahol.hu, a valami.valami.hu és a valami.valahol.hu/bolt3 külön-külön webhelyek lehetnek. Ehhez a következő alkönyvtárakra és fájlokra van szükség:

    • sites/valahol.hu/settings.php
    • sites/valami.valahol.hu/settings.php
    • sites/valami.valahol.hu.bolt3/settings.php

    A Drupal a http://valami.valahol.hu/bolt3 beállításait a következő helyeken keresi a megadott sorrendben, és az első találatot fogja használni:

    • sites/www.valami.valahol.hu.bolt3/settings.php
    • sites/valami.valahol.hu.bolt3/settings.php
    • sites/valahol.hu.bolt3/settings.php
    • sites/www.valami.valahol.hu/settings.php
    • sites/valami.valahol.hu/settings.php
    • sites/valahol.hu/settings.php
    • sites/default/settings.php

    Minden webhelynek lehetnek saját moduljai és sminkjei azon felül, amelyeket a normál modules és themes könyvtárakban találhatunk. Ehhez egyszerűen az adott webhelyhez tartozó könyvtárban kell modules és themes alkönyvtárakat létrehoznunk. Például ha a valami.valahol.hu használ egy saját sminket és egy saját modult, akkor a következő alkönyvtárakra és fájlokra lehet szükségünk:

    • sites/valami.valahol.hu/
    • sites/valami.valahol.hu/settings.php
    • sites/valami.valahol.hu/themes/sajat_theme/
    • sites/valami.valahol.hu/modules/sajat_module/

    További információkat a kézikönyvben találhatunk (majd).

    Alaprendszer frissítése drush segítségével

    Köszönet Den-nek az alábbi leírásért:

    Ezen frissítési leírás biztosan működik frissítési kiadások között, pl: 6.13 -> 6.14.

    Mindent ugyanúgy kell csinálni, ahogy a drupal telepítő készlet upgrade.txt-jében meg van határozva.

    Néhány telepítési pontban nagy segítséget nyűjt a drush:

    5.  Disable all custom and contributed modules.

    Ki kellene kapcsolni minden modult, amely nem az alap rendszer része. Erre tökéletesen alkalmas a drush. Először is kilistázzuk a bekapcsolt modulokat:

    $ drush statusmodules

    Minden parancsot a drupal telepítés főkönyvtárában kell kiadni.

    A statusmodules --pipe kapcsolóját használva egy olyan modul név listához jutunk, amely felhasználható a modulok be és kikapcsolásához is. Mindkét parancs üres karakterrel tagolt modul név listát vár. A --pipe pont ilyen listát ad:

    $ drush statusmodules --pipe
     
    admin_menu admin_menu_toolbar automenu block comment content content_copy custom_breadcrumbs date date_api date_popup date_timezone dblog excerpt extlink fieldgroup filefield filter globalredirect googleanalytics help image_fupload image_fupload_imagefield imageapi imageapi_gd imagecache imagecache_ui imagefield imce imce_wysiwyg jquery_ui locale menu menu_breadcrumb node nodereference nodewords noindex_external_links number optionwidgets page_title path path_redirect pathauto robotstxt seochecklist site_verify spamspan system taxonomy taxonomy_breadcrumb text token translation update upload user userreference wysiwyg

    Egy a baj ezzel a listával: olyan modulok is benne vannak, amelyek az alap rendszer részei (pl. node, system). Ezeket egy mozdulattal ki lehet szedni a listából. Ha nem így teszünk, akkor annak csúnya vége lehet. Próbáltam...

    $ drush statusmodules --pipe | sed 's! block ! !; s! filter ! !; s! node ! !; s! system ! !; s! user ! !; s! update ! !; s! menu ! !; s! path ! !; s! locale ! !;'

    A sed parancs egy unixos stream szerkesztő szövegek szűréséhez és átformálásához. Az s parancs a szöveg csere parancs. A határoló jelek között lévő szövegeket cseréli ki. Az s! block ! ! azt jelenti, hogy ahol a block szöveg szerepel üres karakterekkel határolva, azt cserélje le egy üres karakterre. Ha az üres karakterek nélkül adnánk ki a parancsot, akkor más block nevet tartalmazó modul nevét elrontaná a módszer.

    A fenti verzióban benne maradt még néhány, nem rendszer modult: menu, path, locale, update. Ha nincs update modul, akkor nem fog lefutni az adatbázis update.

    A fenti parancssor futtatása után már csak azokat a modulokat kapjuk a listában, amelyeket ténylegesen ki kell kapcsolni:

    admin_menu admin_menu_toolbar automenu comment content content_copy custom_breadcrumbs date date_api date_popup date_timezone dblog excerpt extlink fieldgroup filefield globalredirect googleanalytics help image_fupload image_fupload_imagefield imageapi imageapi_gd imagecache imagecache_ui imagefield imce imce_wysiwyg jquery_ui menu_breadcrumb nodereference nodewords noindex_external_links number optionwidgets page_title path_redirect pathauto robotstxt seochecklist site_verify spamspan taxonomy taxonomy_breadcrumb text token translation upload userreference wysiwyg

    Erre szükség lesz később is. Ha nem tudjuk elmenteni, akkor irányítsuk át egy állományba:

    $ drush statusmodules --pipe | sed 's! block ! !; s! filter ! !; s! node ! !; s! system ! !; s! user ! !; s! update ! !; s! menu ! !; s! path ! !; s! locale ! !;' > active_modules.lst

    Ezután az active_modules.lst állományt listázva megkaphatjuk azon modulok listáját, amit az alap rendszer frissítése előtt ki kell kapcsolni, majd a frissítés után meg be.

    A modulok kikapcsolása:

    $ drush disable admin_menu admin_menu_toolbar automenu comment content content_copy custom_breadcrumbs date date_api date_popup date_timezone dblog excerpt extlink fieldgroup filefield globalredirect googleanalytics help image_fupload image_fupload_imagefield imageapi imageapi_gd imagecache imagecache_ui imagefield imce imce_wysiwyg jquery_ui menu_breadcrumb nodereference nodewords noindex_external_links number optionwidgets page_title path_redirect pathauto robotstxt seochecklist site_verify spamspan taxonomy taxonomy_breadcrumb text token translation upload userreference wysiwyg

    Ezután végezzük el a rendszer frissítés többi pontját, egészen a 12.-es pontig:

    12. Re-enable custom and contributed modules and re-run update.php to update custom and contributed database tables.

    Itt megint hívjuk segítségül a drush-t és az elmentett modul listát:

    $ drush enable admin_menu admin_menu_toolbar automenu comment content content_copy custom_breadcrumbs date date_api date_popup date_timezone dblog excerpt extlink fieldgroup filefield globalredirect googleanalytics help image_fupload image_fupload_imagefield imageapi imageapi_gd imagecache imagecache_ui imagefield imce imce_wysiwyg jquery_ui menu_breadcrumb nodereference nodewords noindex_external_links number optionwidgets page_title path_redirect pathauto robotstxt seochecklist site_verify spamspan taxonomy taxonomy_breadcrumb text token translation upload userreference wysiwyg

    Ha minden jól megy, akkor azon modulok lesznek engedélyezve, amelyek frissítés előtt is voltak. Nem kell megjegyezni, nem kell felírni papírra semmit, a munka fárasztó részét a drush végezte.

    Folytassuk tovább a rendszer frissítést a 13. pontnál.

    Menü csak belépett felhasználóknak

    A Drupal.org-on már nem egyszer felütötte a fejét ez a kérdés, és most a magyar support listán is. Ebből az oldalból kihüvelyezhetjük, hogy a megoldás egy saját blokk létrehozása, aminek a tartalma:

    global $user;
    if ($user->uid) {
    if ($menu = theme_menu_tree()) {
    $menu = '

    ';
    return $menu;
    }
    }
    else {
    return;
    }
    ?>

    Ennél általánosabb megoldáshoz már saját modult kell írnunk. Ez elég, ha csak a hook_menu kampót valósítja meg, ennek segítségével az egyes menüpontokhoz megadhatunk tetszőleges jogosultságokat is.

    Egy kódbázisra több Drupal

    Gondoltam megosztom minden érdeklődővel tapasztalataimat a témában!

    Eleve egy olyan CMS rendszert kerestem ami képes arra, hogy egy telepítéssel és több adatbázissal futtatható legyen több portál egy tárterületen, úgy, hogy ha az egyik oldalon regisztrálta magát valaki akkor ez a regisztráció érvényes legyen minden eddigi és jövőbeli részlegre (nevezzük így egy drupal egy konfigurációját saját tartalmával).

    Szóval miután sikerült telepítenem a drupal-t a gyökérkönyvtárba, valamint az adatbázisba feltöltenem a database.mysql file-t, elérkezett a többi oldalnak a felvitele.

    Ehez az alábbi lépéseket kellet elvégezni:
    (végigmegyünk egy új oldal telepítésén, hogy egyszerű legyen a dolog)

    Szeretnék egy www.domain.hu/alfa oldalt feltelepíteni aminek legyen:

    • saját sminkje
    • saját tartalma (adatbázis táblák)
    • saját beálításai

    Hogy ezt elérjed, kell egy új cím ugyebár. Ezt egy szimbólikus link segítségével kaphatod meg.

    Gyökér URL: www.domain.hu
    Új URL: www.domain.hu/alfa

    Arról, hogy hogyan csinálhattsz ilyet UNIX alatt itt olvasshatsz bővebben.

    Ha nincs UNIX shell hozzáférésed a szolgáltatódnál olvassd el ezt!

    Ezek után szükséged lesz egy új configurációs állományra, ami leírja az új oldalat által használt:

    • adatbázist
    • adatbázis prefixumot
    • bázis URL-t
    • valamint a megosztott adatbázistáblák nevét

    Másold le és nevezzd át az /includes/conf.php -t. Vigyázat! Az új név legyen: www.domain.hu.alfa.php.

    Az elnevezés miértjéről itt olvasshatsz bővebben.

    Ebbe az új config fileba írd át/add hozzá az alábbi sorokat

    $db_prefix = array(
           "default" => "alfa",
           "users" => "",
           "sessions" => "",
           "role" => "",
           "authmap" => ""
           "sequences" => ""
           );

    default = a nem megosztani kívánt táblák prefixuma
    a többi a megosztani kívánt táblák prefixuma. Azaz ha az első durpal-t prefixum nélkül telepítettuk fel a mysql szerverre, akkor itt is NULL-t kell megadni a közös táblák prefixumára.
    $base_url = "http://www.endomainem.hu/alfa";

    Ezek után ha a www.domain.hu/alfa oldalra ellátogatsz akkor már működnie kell az új oldladnak. Egyből be kell tudond lépni az oldalra az első telepítésnél használt admin felhasználónévvel/jelszóval, mivel a users táblát megosztva használod.

    Ezek után mehet a testreszabás, smik kiválasztás stb.

    Mindenkinek sok szerencsét.

    Drupal fejlesztői bookmarklet

    A Drupal fejlesztői dokumetációja most már az api.module segítségével készül és a http://drupaldocs.org/ címen érhető el. Találunk itt egy pompás bookmarkletet is, melyet Walkah készített. A bookmarklet a felugró dialógus dobozba beírt Drupal függvény leírását keresi elő.

    Első lépések

    Ebben a rövid cikkben a Drupal telepítése utáni első néhány lépésben próbálok segíteni, mind a felhasználóknak, mind a programozóknak. Feltételezem, hogy sikerült a Drupal telepítése, s a kérdés már csak az: szép ez a valami, de hogy lesz ebből nekem végleges oldalam? Sikeres telepítés után a következő képernyő fogad minket:

    A bal felső sarokban a Druplicont láthatjuk. Az eredete a holland "druppel" (csepp). Ennek angol formája lett végül a "Drupal", annak kapcsán, hogy így megegyezik a kiejtése a holland eredetivel. A "Druplicon", azaz a Drupal logója egy cseppre épül. A szemei ravaszul egy végtelen jelet formálnak, ezzel finoman a Drupal képességeinek határaira akartak utalni állítólag a készítők...

    A Druplicon mellett most még nem sok minden van a fejlécben és az egyszerűség kedvéért ezeket most nem is tárgyaljuk.

    Alatta láthatjuk az első blokkunkat, a bejelentkezés (angolul login) blokkot. A blokkok egyszerű szövegdobozok, amik a bal vagy a jobb oldalon jelenhetnek meg. A blokkokat ki- és bekapcsolhatjuk, de lehetőséget adhatunk a felhasználóknak is erre. Egy blokkot beállíthatunk úgy is, hogy csak bizonyos oldalon jelenjen meg -- ez már a haladóbb fejezetbe tartozik azonban, reguláris kifejezések szükségesek hozzá. Blokkot kétféleképpen adhatunk meg: vagy programot írunk, vagy pedig egyszerűen begépelhetjük a tartalmát, mint ahogy az Ajánlott oldalak nevű blokkunkat mi is megvalósítottuk.

    Az adminisztráció (settings) oldalon egy nagyon érdekes bejegyzésre hívnám a figyelmet, ennek kapcsán jutunk el végre valahova. Talán már hallottuk, hogy a Drupalban minden, amit felviszünk egy node, melyet tartalomnak fordítottunk. Minden tartalomnak van egy összefoglalója, ez alapértelmezésben az első 600 karaktere. Ha behívjuk például a http://weblabor.hu/node címet, akkor láthatjuk a legutolsó tíz node összefoglalóját. Mivel a Drupal alapértelmezés szerint ezt az oldalt hívja be, ezért alapértelmezés szerint ezt a tízes összefoglalót fogjuk látni a legtöbb helyen.

    Természetesen egy -- nagyon rövid -- idő után több mint tíz node lesz. Valószínűleg szeretnénk valami szervezettséget is, s nem csak egymásra hányt oldalakat. Erre van a taxonómia (angolul taxonomy) modul, mely telepítés után be van kapcsolva. A feladata egy kategóriarendszer kialakítása. Ez a Drupal egyik legnagyobb erőssége: szótárakat alakíthatunk ki, minden szótárban kategóriákat, s a kategóriákhoz kapcsolódhatnak a tartalmak. Egy szótárban a kategóriákat háromféleképpen rendezhetjük el: egyrészt lehetséges, hogy csak egyszerűen fel vannak sorolva, anélkül, hogy hierarchiát alkotnának. Lehetséges egy egyszerű fa, olyan, mint a merevlemezünkön a mappák -- ilyenkor egy kategóriának egy szülőkategóriája van, de természetesen egy szülőnek több gyermeke is lehet. S végül a harmadik lehetőség, a gráfszerű kialakítás, ahol egy kategóriának számos szülőkategóriája is lehet, ezáltal egy bonyolult, de a tartalmaink szerepét az oldalon pontosan lefedő rendszert alkotva.

    A kategóriák megjelenhetnek egy blokkban, ahogy ezen az oldalon is történik.

    A modulok-blokkok sorát most még az archívum (archive) modult említem. Ennek a megjelenése egy naptár blokk, aminek segítségével kiválaszthatunk egy adott napot, és megjelennek az akkor készült tartalmak. Ha szeretnénk elérhetővé tenni a régebbi tartalmainkat (amelyek már nem férnek bele az első tízbe), akkor ez egy jó választás lehet ennek megoldására.

    Ilyen egyszerűen születik tehát egy Drupal site:

    • elkészítjük a szótárakat és a kategóriákat
    • kiválasztjuk a szükséges blokkokat, modulokat.
    • már lehet is feltölteni az oldalakat!

    A programozók saját igényeik szerint egészíthetik ki a rendszert, nekik ajánlom, hogy első ismerkedésként nézzék meg, hogy kezeli le a "node.module" a főoldalt, vagyis a "node path"-ot. Elsőként a "node_menu" függvényt érdemes megnézni, ott egy 'path' => 'node' bejegyzést lehet látni, ezen keresztül a "node_page" eljárás hívódik meg, onnan pedig a "node_page_default"-ra kerülünk. Érdemes megnézni még a "node_link" függvényt is. További példák vannak elvileg a http://drupal.org/doxygen/drupal/ címen, bár nekem mostanában nem töltődnek be a példaoldalak. Addig is megtalálhatjuk őket a http://drupal.kollm.org/tmp/drupal-phpdoc/ oldalon.

    Nos, ennyit bevezetésül, mindenkinek javaslom, hogy nézzen körül a beállítási lehetőségek között, és próbálja ki, mi mire való!

    A lusták modulja: taxonomy_html

    Egy nagyon gyorsan változó portálod van? Nincs kedved/időd állandóan a saját gyártmányú blokkjaidat szerkesztgetni és azt szeretnéd, hogy a taxonómiában beállított kategóriák egyből megjelenjenek a honlapodon? Ha igen, akkor a taxonomy_html.module -t neked találták ki..

    Vágjunk a közepébe: mit is csinál pontosan ez a modul? Blokkokat gyárt a taxonomiában szereplő szótárakból. Lépésről lépésre szeretném megmutatni, hogy mennyire egyszerűen.
    Elsőként is, töltsük le taxonomy_html a modult és másoljuk be a portálunk modules könyvtárába. Ezután engedélyezzük a modulok alatt az újdonsült szerzeményünket. Most pedig egy konkrét példán keresztül mutatom be, hogy működik.


    A taxonomy menüpontban létrehozok egy "Receptek" szótárt, azon belül pedig egy "süti" kategóriát, mivel szeretném a Bakláva receptemet feltenni. A "Receptek" -et a recipe kategóriába tettem. (A recipe is egy modul, amit külön le kel tölteni és installálni.)




    Nyomás a beállítások/blokkok menüpontba és láss csudát, ott csücsül a "receptek" blokk, már csak engedélyeznünk kell a kis négyzet kipipálásval.


    Ezek után hiába lessük a főoldalt, nem jelenik meg a "Receptek" blokk. Mégpedig azért, mert még nincs benne tartalom, az automatizálás csak létező tartalommal működik. Tehát tartalom beküldése menü -> recept (recipe) -> a recept begépelése. Megadom azt is, hogy a recepteken belül a süti kategóriában jelenjen meg:



    Beküldés és a főoldalon megjelenik a munkánk gyümölcse:


    Egy kis finomhangolás: a beállítások/modulok alatt található a taxonomy_html modul link, ha erre kattintunk, beállíthatjuk, hogy kihagyjon a blokkgyártásból bizonyos szótárakat. Válasszuk ki a listából (Omitted Vocabularies) ami nem kell.


    Csonti

    Ultimate Gallery

    Köszönet e cikkért mib kollégának.

    Elöljáróban annyit szeretnék megjegyezni, hogy nagyon sok galéria leírás van, viszont egyik sem elégítette ki azt a tudást amit elvárnék, így nekiálltam megcsinálni a sajátomat, amit ugyancsak lehetne még tökéletesíteni (és hogy mit azt majd a végén részletezem), de a célnak megfelel.

    Milyen modulokra lesz szükségünk?

    • cck
    • imagefield
    • filefield_paths
    • views
    • views attach module (node content nézett miatt)
    • imagecache + imagecache action (opcionális)
    • image_fupload
    • lightbox2

    A megvalósítás lépései

    Node

    Először létrehozunk 2 imagecache kép mintát, amit használni akarunk majd a galéria kiskép és nagykép megjelenítéseknél (referenciak_kiskep, referenciak_nagykep).

    Ez után 1 új tartalom típust (galéria) csinálunk, amiben kikapcsoljuk az alapértelmezett beállításokban hogy címlapra kerüljön, és ha van a hozzászólásokat is tiltjuk,

    galeria_node.png

    galeria_node2.png

    hozzáadunk egy fupload mezőt, és a következő beállításokat eszközöljük rajta: a multiple images per node-t választjuk, filepaths beállításba beállítjuk hogy az url-t és a file nevét tisztítsa meg, nagy betűket kicsinyítse le (az útvonal beállítás opcionális, én szeretem ha külön menti el a többi file-tól), ezen kívül hogy szükséges és az értékek száma korlátlan.

    galeria_node3.png

    galeria_node4.png

    galeria_node5.png

    galeria_node6.png

    A mező megjelenítésben label-t kikapcsoljuk, bevezetőre beállítunk egy kép megjelenítést (mindegy hogy mit, mivel nem ezt használjuk), a teljes nézetet meg elrejtjük.

    galeria_node7.png

    Views

    A viewsban 2 nézet fogunk létrehozni. Az egyik a galériákat gyűjti össze, a másik a node típust (galeria) formázza meg.

    Hozzunk létre egy új nézetet, névnek adjuk page_galeriak (ahol a page utal arra az oldalra ami a galériákat összegyűjti), nálam ez a referenciak oldal, így én a referencia_galeriak nevet adtam neki (továbbiakban page helyett a referenciát használom). A view type tartalom. Adjunk hozza egy page nézet típust. Sok beállítási lehetőség van de ami nekünk fontos az a következők:

    A szűrőknél 2 dolgot állítunk be, tartalom: közzétett és tartalom típus: Galéria

    A mezőknél ami fontos a tartalom: fupload mező amit a galeria típusnál beállítottunk. A többszörös értéket pipájuk be és értéknek adjuk az 1-et. A formátum résznél meg válasszuk az a imagecache mintát amit a kis képekhez készítettünk (nem a lightbox2-féle verziót).

    A page settings-nél meg állítsuk be a pathot és a menüt amihez hozzáadjuk.

    galeria_views.png

    galeria_views2.png

    galeria_views7.png

    Hozzuk létre a második nézetet is:

    Név referencia_node_content, view type tartalom. Adjunk hozzá egy új node_content nézet típust. A szűrőknél a beállítások ugyan azok mint az előbb, a mezőknél annyiban módosul hogy nincs többszörös érték csoportosítás és a formátumnál a lightbox2-t állítjuk be a kiskép nagykép váltáshoz. (ligthbox2: kiskep->nagykep). Az argumentumnál a tartalom node id-t állítjuk be és ami fontos az a validator galéria típus. A node content settings-nél a tartalom típus a galéria.

    galeria_views3.png

    galeria_views5.png

    galeria_views6.png

    Ezzel meg is volnánk. Ami még hiányzik az egy vissza link a galériákból, amit könnyen hozzá tudunk adni a node_content view template file-hoz. Mivel én a rács megjelenítést használom a tpl.php-m a views-view-grid--referencia-node-content--node-content-1.tpl.php. A file végére illesszük be ezt:
    ahol a referenciak az a oldal ahol összegyűjtjük a galériákat.

    A kész galéria

    galeria.pnggaleria2.png

    Pro

    • A taxonomyval ellentétben úgy lehet képeket törölni hogy azt látjuk is.
    • Ha akarjuk az ajaxos pager könnyen megvalósítható.
    • A galéria létrehozása és a képek feltöltése egy azon lapon történik a tartalom beküldésben, ellenben image gallery-vel ami taxonomyt használ, és elösször a galériát kell létrehozni (ami nem a tartalom beküldés oldalon van!), utánna meg a képeket beküldeni egyesével (lehet image import is de ahoz elöbb serverre fell kell tölteni képeket, ami megintcsak gáz), így az image gallery nem user frendly.

    Kontra, vagyis mit lehetne még fejleszteni rajta?

    Ami kimaradt az a galériában galéria funkció, ami könnyen megoldhatunk a node reference url widgettel, viszont ez még több megoldandó feladatott eredményezne. pl. hogyha kitörlünk egy szülő galériát akkor az összes gyermek galériát is kitörölje a képekkel együtt. Ezen kívül még kéne egy breadcrumb funkció ahol a galériákat tudnánk nyomon követni. Az én véleményem az hogy ez csak összezavarná usereket, így jobb ha galériának nem lehet gyermek galériája.

    És az elmaradhatatlan hibák

    A filefield path beállításánál nekem nem működött a külön galériák szerinti kép mentés, és az imagecache sem törli ki a létrehozott majd törölt képek file-jait. (és ezzel a modul fejlesztők is tisztában vannak, szal még nem tökéletes). A másik hiba ami még előjöhet hogy fupload modul nem kompatibilis az fck editorral így minden a törsz mezőbe beírt szöveget töröl és <!--break--> jelet írja be helyette így én ideglenesen ki is kapcsoltam fck editort a galéria tartalom típusnál.

    Sok sikert hozzá, MiB