Archive for April, 2009

A WYSIWYG editorok lényege, hogy a felhasználó HTML tudás nélkül is tudjon webes felületen formázni. Ezt úgy érjük el, hogy a html szöveget tartalmazó textarea-ra egy JavaScript szerkesztőfelületet adunk, ami a begépelt és formázott szöveget HTML kódokká alakítja át.

Drupal alá is létezik sok közkedvelt WYSIWYG editor modul, pl: FCK Editor vagy a TinyMCE Editor, melyek mindig előkelő helyen vannak a leggyakrabban letöltött Drupal modulok között.

Nekem a kedvencem mégis a Wysiwyg API modul, mégpedig azért, mert egy alapvető problémát old meg: a beviteli formák összeütközése a WYSIWYG editorral a beviteli forma szűrői miatt. Ez röviden annyit takar, hogy a többi WYSIWYG editor jogosultsághoz van rendelve, tehát ha egy felhasználónak megvan a joga az editorhoz akkor használhatja, de ha kiválasztja Filetred HTML beviteli formát, amelyben a sortörések, url-ek stb. szűrve vannak, akkor az eléggé össze fog akadni a WYSIWYG editorral. Ilyenkor ugyebár a felhasználó boldogan formázza a szöveget, majd elmenti a Drupal a szűrt beviteli formában és egyáltalán nem úgy néz ki a végeredmény mint ahogy azt akarta a felhasználó. Persze Ő soha nem fog rájönni, hogy mimiatt történt, és úgy gondolja valami fejlesztési hiba van a dologban. Természetesen ez nem fejelsztési hiba, de akkor sem várható el egy editortól, hogy rájöjjön mi okozza a problémát.

A Wysiwyg API modul erre talált gyógyírt, mégpedig azzal, hogy az editor használhatóságát a beviteli formához lehet megadni, nem pedig egy user jogosultság. Minden definiált beviteli formához külön-külön más és más editoroka lehet beállítani. Ekkor két kérdés merül fel:

  1. mi az hogy más és más WYSIWYG editorokat lehet rendelni?
  2. bárki használhatja az editort hisz nincs jogosultsághoz kötve?

Az első kérdésre a válasz az, hogy a Wysiwyg API egyszerre több WYSIWYG editort képes kezelni. Ez annyit takar, hogy a beállítási oldalán fel van sorova egy csomó WYSIWYG editor (pl MarkItUp, FCK editor vagy a TinyMCE editor), és amelyik fel van telepítve a megadott könyvtárba azt ki lehet választani egy legördülőből a beviteli formák felsorolása mellett.

A másodikra a válasz: nem. A beviteli formák beállítása oldalán meg lehet adni, hogy egy adott beviteli formát milyen csoport hasznáhat, és tetszőleges számút létrehozhatunk. Tehát pl, ha a Full HTML-t meg akarjuk hagyni a HTML-t értő és WYSIWYG editort nem használó csoport számára, akkor létrehozunk egy új beviteli formát, amelyben szintén nincs semmilyen szürő és hozzárendeljük a kívánt csoporthoz.

Ennyit a Wysiwyg API modul szépségéről, térjünk rá a cikk lényegére, a teaser beállítására.

Miután feltelepítettük a modult észrevehetjük, hogy eltűnt a teaser lehetőségére szolgáló gomb a body rész felett. Ez annyit takar hogy innenstől kezdve magunk nem állíthatjuk be hogy a teaser rész meddig tartson és azt sem, hogy ez akár különbözzön a tartalom többi részétől.

Mivel én hasznosnak találom ezt a funkciót, és többször is szükség volt már rá, ezért elkezdtem kutakodni és utánajárni hogy is lehetne megoldani. A TinyMCE editorban van egy pagebreak plugin, tehát lehetséges hogy megoldódik a probléma, mert a Drupal body részében egy helyre beírjuk a

<!--break-->

kifejezést, akkor ezt a Drupal úgy értelmezi, hogy az előtte lévő rész a teaser rész. Ez eddig szép és jó, csak kéne egy ilyen gomb az editor szerkesztőfelületére.

Ehhez a modul editors/tinymce.inc fájl wysiwyg_tinymce_plugins() fgv-ét kell módosítanunk.
A végén találunk benne egy ilyen részt:

  1.   if ($editor['installed version'] > 3) {
  2.     $plugins['xhtmlxtras'] = array(
  3.       'path' => $editor['library path'] .'/plugins/xhtmlxtras',
  4.       'buttons' => array('cite' => t('Citation'), 'del' => t('Deleted'), 'abbr' => t('Abbreviation'), 'acronym' => t('Acronym'), 'ins' => t('Inserted')),
  5.       'url' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras',
  6.       'internal' => TRUE,
  7.       'load' => TRUE,
  8.     );
  9.     $plugins['safari'] = array(
  10.       'path' => $editor['library path'] .'/plugins/safari',
  11.       'extensions' => array('safari' => t('Safari compatibility')),
  12.       'url' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari',
  13.       'internal' => TRUE,
  14.       'load' => TRUE,
  15.     );
  16.   }

ezt kell kibővítenünk a következő képpen:

  1.   if ($editor['installed version'] > 3) {
  2.     $plugins['xhtmlxtras'] = array(
  3.       'path' => $editor['library path'] .'/plugins/xhtmlxtras',
  4.       'buttons' => array('cite' => t('Citation'), 'del' => t('Deleted'), 'abbr' => t('Abbreviation'), 'acronym' => t('Acronym'), 'ins' => t('Inserted')),
  5.       'url' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras',
  6.       'internal' => TRUE,
  7.       'load' => TRUE,
  8.     );
  9.     $plugins['safari'] = array(
  10.       'path' => $editor['library path'] .'/plugins/safari',
  11.       'extensions' => array('safari' => t('Safari compatibility')),
  12.       'url' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari',
  13.       'internal' => TRUE,
  14.       'load' => TRUE,
  15.     );
  16.     $plugins['pagebreak'] = array(
  17.       'path' => $editor['library path'] .'/plugins/pagebreak',
  18.       'buttons' => array('pagebreak' => t('Drupal Teaser')),
  19.       'url' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/pagebreak',
  20.       'internal' => TRUE,
  21.       'load' => TRUE,
  22.     );
  23.   }

Ekkor a TinyMCE editor beállítási oldalán a gombok között megjelenik a Drupal Teaser gomb. Ha kiválasztjuk, akkor a tartalom szerkesztésénél láthatjuk, hogy megjelenik és ha megnyomjuk megjelenik a

<--! pagebreak -->

felirat a szövegben. Várjuk a csodát, hogy majd biztos jó lesz, de sajnos nem működik. Ennek az az oka, hogy a TinyMCE editor pagebreak plugin-je a következőt helyettesíti be:

<--! pagebreak -->

holott a Drupal ezt tudja értelmezni:

<!--break-->

Ebből látszik, hogy a plugin JavaScript fájljába is bele kell kontárkodnunk egy kicsit, mégpedig a modul könyvtárán belül a tinymce/jscripts/tiny_mce/plugins/pagebreak/editor_plugin.js fájlba. Keressük meg a részt, ami beleteszi a kifejezést:

"pagebreak_separator","<!-- pagebreak -->"

és írjuk át a megfelelőre:

"pagebreak_separator","<!---break-->"

Ekkor ha újra megpróbáljuk beállítani, hogy meddig tartson a teaser rész, tökéletesen működni fog.

Mostmár csak egy dolgot akarunk megoldani, mégpedig hogy a teaser rész különbözhessen a tartalomtól.

Ennek megoldására a wysiwyg.module wysiwyg_form_alter() (hook_form_alter()) fgv-ét kell módosítanunk.
Ha megnézzük az egész fgv. csak ennyitből áll:

  1. function wysiwyg_form_alter(&$form, &$form_state) {
  2.   $form['#after_build'][] = 'wysiwyg_process_form';
  3.   // Disable 'teaser' textarea.
  4.   if (isset($form['body_field'])) {
  5.     unset($form['body_field']['teaser_js']);
  6.     $form['body_field']['teaser_include'] = array();
  7.   }
  8. }

Kommentbe benne is van, hogy a teaser részt kiszedi a modul. A teaser_js részt megszűnteti, ez jó is nekünk így, mert egyrészt összeakad a modullal a js (ez az, ha rányomunk a gombra akkor DHTML-el felkerül egy újabb textarea amelyben megadhatjuk a teaser rész tartalmát), másrészt pedig a WYSIWYG editorral már meg tudjuk oldali a teaser elkülönítését, és így legalább szerkeszthető is (úgy értem a WYSIWYG editorral). Nekünk gyakorlatilag csak az “Összefoglaló megjelenítése a teljes nézetben is” checkbox kellene, hogy meg tudjuk adni a teaser beépüljön a tartalomba, vagy különböző legyen.

Ezt úgy tudjuk elérni kiszedjük (vagy kikommentezzük) a következő részt:

  1. $form['body_field']['teaser_include'] = array();

Így a wysiwyg_form_alter() fgv. a követekző képpen módosul:

  1. function wysiwyg_form_alter(&$form, &$form_state) {
  2.   $form['#after_build'][] = 'wysiwyg_process_form';
  3.   // Disable 'teaser' textarea.
  4.   if (isset($form['body_field'])) {
  5.     unset($form['body_field']['teaser_js']);
  6.     //$form['body_field']['teaser_include'] = array();
  7.   }
  8. }

Ekkor szépen megjelenik a checkbox, és beállíthajuk a teaser-t is.

Nekem már többször előfordult olyan problémám, hogy a Drupal elsődleges menüjének (primary links) egyedi kinézete miatt, kizárólag képekből álló menüt kellett készítenem. Ezalatt azt értem, hogy nincs a háttérkép felett semmilyen szöveg, a menü címe is a képen szerepel.

A háttérképeket egyszerűen megadhatjuk, a menüpontok egyedi azonosítója miatt (pl. menu-130 stb.), viszont a rajta lévő szöveget el kell tüntetni valahogy.

CSS-ben ha levesszük a betűméretet 0px-re sajnos nem a megfelelő megoldás, mert IE-ben, Chrome-ban és Operában kicsiben, de látszik.

Egy megoldás, ha a hyperlinkek szövegét span elemek közé tesszük, és a span elemeket tüntetjük el CSS-ben a display: none; definícióval, tehát a cél az lenne, hogy így nézzen ki egy menüelem:

  1. <li class="menu-129"><a class="menu-129" href="/"><span class="primary-title">Nyitólap</span></a></li>

Ekkor minden további nélkül megadhatjuk CSS-ben a következőt:

  1. ul.primary-links span.primary-title {
  2. display: none;
  3. }

Igenám, de hogy érjük el, hogy a Drupal span elemek közé tegye a linkek címét?

A megoldás az, hogy a sminkünk template.php fájljába kifejtjük a theme_links() fgv-t.

Íme a kód:

  1. /**
  2.  * Csak kepekbol allo menu elkeszitesenek alapjai
  3.  *
  4.  * @param unknown_type $links
  5.  * @param unknown_type $attributes
  6.  * @return HTML
  7.  */
  8. function phptemplate_links($links, $attributes = array('class' => 'links')) {
  9.   global $language;
  10.   $output = '';
  11.  
  12.   if (count($links) > 0) {
  13.     $output = '<ul'. drupal_attributes($attributes) .'>';
  14.  
  15.     $num_links = count($links);
  16.     $i = 1;
  17.  
  18.     foreach ($links as $key => $link) {
  19.       $class = $key;
  20.  
  21.       // Add first, last and active classes to the list of links to help out themers.
  22.       if ($i == 1) {
  23.         $class .= ' first';
  24.       }
  25.       if ($i == $num_links) {
  26.         $class .= ' last';
  27.       }
  28.       if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
  29.           && (empty($link['language']) || $link['language']->language == $language->language)) {
  30.         $class .= ' active';
  31.       }
  32.       $output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
  33.      
  34.       $link['attributes'] = array('class' => $class);
  35.  
  36.       if (isset($link['href'])) {
  37.         // Pass in $link as $options, they share the same keys.
  38.         $link['html'] = true;
  39.         $output .= l('<span class="primary-title">' . $link['title'] . '</span>', $link['href'], $link);
  40.       }
  41.       else if (!empty($link['title'])) {
  42.         // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
  43.         if (empty($link['html'])) {
  44.           $link['title'] = check_plain($link['title']);
  45.         }
  46.         $span_attributes = '';
  47.         if (isset($link['attributes'])) {
  48.           $span_attributes = drupal_attributes($link['attributes']);
  49.         }
  50.         $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
  51.       }
  52.  
  53.       $i++;
  54.       $output .= "</li>\n";
  55.     }
  56.  
  57.     $output .= '</ul>';
  58.   }
  59.  
  60.   return $output;
  61.  
  62. }

Ez a kód, majdnem megegyezik az API oldalon található fgv. kóddal. Nézzük az eltéréseket:

  1. $link['attributes'] = array('class' => $class);

Ezzel annyit érünk el, hogy ne csak a listaelemnek (li) legyen egyedi azonosítója, hanem a hyperlink-nek (a) is. Ez azért fontos, hogy a :hover eseményre is tudjunk képet rakni, ha esetleg különbözik.

  1. $link['html'] = true;
  2. $output .= l('<span class="primary-title">' . $link['title'] . '</span>', $link['href'], $link);

A link tömb lesz az l() fgv. harmadik paramétere, ebbe kell szerepelnie a beállításoknak, és mivel HTML-t adunk meg, ezért a $link tömb html kulcsát igaz értékre kell állítanunk. Az l() fgv. első paraméterének pedig beírjuk a span elemek közé a link címét.

Ha ezt elvégeztük, feltöltjük a template.php-t a smink könyvtárába, majd hogy a rendszer érzékelje az új fgv. létrejöttét, menjünk be a sminkek beállítási oldalába (admin/build/themes) és nyomjunk egy mentést.

Ezután minden menüelem egyedi azonosítoval fog rendelkezni, és a menü címe span elemek között lesz, amit könnyedén eltüntetetünk. Innenstől kezdve csak CSS kérédése az egész.