Joomla@jogger.pl

 

30

października

2010

Dostęp do parametrów szablonu w plikach error.php i offline.php

Strony błędów (error.php) oraz logowania (offline.php) są generowane w trochę inny sposób niż zwykłe strony szablonu. Nie mamy w nich bezpośredniego dostępu do parametrów naszego szablonu. Na szczęście możemy to łatwo naprawić.

Parametry szablonów są przechowywane przez obiekt klasy JParameter. Nadpisywane przez nas pliki stron błędów i logowania znajdują się w głównym katalogu szablonu. W tym samym katalogu znajduje się też plik params.ini, który przechowuje informacje o wartościach parametrów szablonu. Dodatkowo konstruktor klasy JParameter przyjmuje jako pierwszy argument ciąg danych w formacie znanym z plików *.ini.

Zatem wystarczy wczytać plik params.ini i tak odczytane dane podać jako argument konstruktora klasy JParameter aby uzyskać dostęp do potrzebnych nam danych. Całość wymaga dosłownie jednej linijki kodu PHP:

$tpl_params = new JParameter(JFile::read(dirname(__FILE__).DS.'params.ini'));

Odczyt wartości parametrów szablonu odbywa się poprzez następujący kod:

$opcja1 = $tpl_params->get('NAZWA_OPCJI');

Dzięki temu możemy nie tylko odczytywać ważne dla nas parametry szablonu, ale też definiować w szablonie nowe opcje związane ściśle z plikami error.php i offline.php

 
 

28

października

2010

Pluginy w mod_custom

Moduł mod_custom to bardzo użyteczne narzędzie, jednak brakuje mu jednej ważnej rzeczy: jego zawartość nie jest parsowana pod kątem zawartości pluginów. Na szczęście można łatwo ten problem rozwiązać.

Wystarczy zmodyfikować sposób generowania styli modułów. Style te znajdują się w pliku html/modules.php większości szablonów, które takie style stosują.

Musimy zmodyfikować funkcję modChrome_NAZWASTYLU - na pewno znajduje się w niej linijka podobna do tej:

echo $module->content;

Odpowiada ona za wyświetlanie treści modułu. My musimy zmienić ją na następujący kod:

echo ($module->module == 'mod_custom') ? JHTML::_('content.prepare', $module->content) : $module->content;

Powyższy kod sprawdza typ modułu i w wypadku gdy jest to moduł typu mod_custom do generowania treści wykorzystywanie jest parsowanie pluginów, w przeciwnym wypadku treść generuje się tak samo jak przed modyfikacją.

W wypadku gdy szablon nie używa własnych styli modułów musimy odbyć małą podróż wgłąb kodu źródłowego szablonu system - predefiniowane style modułów dla Joomla! znajdują się w pliku templates/system/html/modules.php.

Uwaga! ten sposób parsowania kodu pluginów jest dużo lepszy niż niektóre inne metody. Widziałem przykładowo rozwiązanie bazujące na modyfikacji pliku index.php, gdzie po zdarzeniu onAfterRender był umieszczony kod parsowania pluginów. Problem polegał na tym, że przy takim wywołaniu pluginy nie mogą już modyfikować sekcji head szablonu (problem tego typu już opisywałem).

 
 

12

października

2010

Parametry w tytule modułu

Dobór stylu modułu poprzez dodanie konkretnego sufiksu w opcjach modułu to standard. Ostatnio coraz częściej korzystam z innego udogodnienia, które gwarantuje mi dużo większą elastykę przy określaniu stylistyki modułu - przekazywanie parametrów w tytule modułu.

Koncepcja ta ma dwie główne zalety:

  1. Możemy przekazywać parametry, których wartościom daleko do nazewnictwa klas CSS;
  2. Parametry te są widoczne od razu w menadżerze modułów.

Jak to działa ?

Decydujemy się, że chcemy stworzyć styl modułu, który np. w swoim prawy górnym rogu wyświetla ikonkę kanału RSS. Oczywiście adres tego kanału jest różny dla różnych modułów, a dodatkowo chcemy mieć taką ikonkę przy różnych modułach. Zatem logiczne jest, że lepiej stworzyć ze stylu modułu swoisty kontener na te dane, zamiast przerabiać kilka modułów by dodać im taką funkcjonalność.

Jedyny problem to właśnie przekazywanie parametrów takiemu stylowi modułu - i tutaj rozwiązanie jest dość proste: dodajemy po tytule modułu np. w nawiasach klamrowych adres kanału RSS:

Tytuł naszego modułu {http://adres.rss.pl}

Teraz wystarczy tak zdefiniować funkcję generującą styl modułu, by wydobyła ów adres z tytułu modułu - czyli w skrócie musimy wykonać kilka elementarnych operacji z użyciem wyrażeń regularnych:

$title = $module->title;
preg_match('/\{.+?\}/', $title, $address);
$title = preg_replace('/\{.+?\}/', '', $title);

W powyższym wypadku wystarczy potem dokonać sprawdzenia:

if(isset($address[0])

by wiedzieć czy w ogóle podano parametr, a jeżeli tak to właśnie zmienna $address[0] przechowuje jego wartość.

Oczywiście sami możemy sobie zdefiniować sposób podawania parametrów - równie dobrze możemy wykorzystywać nawiasy kwadratowe (choć w wypadku adresów internetowych to zły pomysł, gdyż takie znaki mogą występować w adresie) czy po prostu ciąg symboli lub symbol stanowiący separator tytułu od parametrów.

Daje to nam naprawdę duże możliwości - przede wszystkim jest to rozwiązanie elastyczne. Oczywiście moglibyśmy przekazywać parametry stylowi modułu poprzez atrybuty znacznika jdoc:include, ale to nam nie daje możliwości powiązania wartości parametru z modułem tylko z pozycją modułu.

Na koniec jeszcze ciekawa koncepcja - przeniesienie sufiksów modułów właśnie do parametrów przekazywanych w tytule. Dzięki temu od razu w menadżerze modułów zobaczymy jaki sufiks nadano konkretnemu modułowi. Przy czym odradzam takie rozwiązanie przy projektach udostępnianych na szerszą skalę - wielu użytkowników jest przyzwyczajonych do opcji "module suffix" ;)

 
 

29

września

2010

Cufon - typowe problemy

Możliwość osadzania czcionek w CSS wciąż jest dość odległą perspektywą ze względu na brak wsparcia w przeglądarkach używanych przez znaczną część internautów. Czasem jednak istnieje potrzeba wykorzystania w layoucie czcionki znacząco odbiegającej wyglądem od grupy czcionek, które są powszechnie dostępne na komputerach internautów. Pewnym rozwiązaniem tego problemu jest skrypt Cufon.

Od razu muszę napisać, że osobiście nie jestem zwolennikiem tego typu rozwiązań, ale w chwili obecnej jest to jeden z nielicznych i względnie uniwersalny sposób obejścia problemu z nietypowymi czcionkami.

Jako, że sporo szablonów, które współtworzyłem zawiera wsparcie dla Cufon-a chciałbym zwrócić uwagę na kilka kwestii, które są często poruszane przez klientów/developerów.

Bez JavaScript ani rusz

Cufon to skrypt, zatem możemy zapomnieć o naszych pięknych czcionkach zdobiących layouty w przeglądarkach, które mają wyłączony JavaScript. Na szczęście osób wyłączających skrypty jest mało, zwłaszcza w stosunku do osób, które mają przeglądarki nieobsługujące osadzania czcionek.

Z pustego i Salomon nie naleje

Bardzo często poruszanym problemem jest brak konkretnych liter charakterystycznych dla różnych języków. W tym wypadku trzeba pamiętać o dwóch kwestiach:

  1. duża część czcionek ma dość ograniczoną ilość znaków - dlatego z reguły trzeba wybrać inną czcionkę, która znaki charakterystyczne dla danego języka wspiera;
  2. gdy mamy już taką czcionkę to musimy ją jeszcze odpowiednio wygenerować - generator plików czcionek dla Cufon-a zawiera sekcję "Include the following glyphs (if available)", która pozwala nam na wybór stosownej grupy znaków do załączenia w pliku czcionki. Należy przy tym bezwzlędnie pamiętać, że każda dodatkowa grupa znaków znacząco zwiększa rozmiar pliku czcionki - stąd należy starać się jak najbardziej ograniczyć ilość znaków w pliku czcionki by nie spowolnić procesu ładowania strony.

Rozmiar ma znaczenie

Cufon działa naprawdę szybko i z reguły w przeglądarkach typu Firefox czy Chrome wręcz nie zauważamy, kiedy zwykły tekst jest podmieniany na tekst napisany inną czcionką. Należy jednak pamiętać, że dużo zależy tu od rozmiaru plików czcionek - jeżeli będą one duże to przynajmniej podczas pierwszego ładowania użytkownik łatwo dostrzeże podmianę tekstu. Dlatego tym bardziej dla potrzeb tzw. pierwszego wrażenia, warto zadbać o rozmiary wygenerowanych dla Cufon-a czcionek.

Problematyczny line-height

Jednym z najważniejszych parametrów dla tekstu jest wysokość linii określana w CSS poprzez właściwość line-height. Niestety Cufon ma pewne problemy z tym parametrem - w wypadku gdy np. nasz szablon jest napisany w (X)HTML Transitional w większości przeglądarek zmiana wysokości linii po prostu nie zadziała. Osobiście w takiej sytuacji stosowałem czasem marginesy dla elementów typu cufon, które są generowane przez skrypt do reprezentacji liter.

Inne efekty CSS też powodują problemy

Generalnie jeżeli potrzebujemy dla tekstu zastosować jakieś bardziej nietypowe efekty takie jak przezroczystość, cienie itd. to warto najpierw zapoznać się z częścią dokumentacji odnoszącą się do stylowania tekstu wygenerowanego przez Cufon.

Cufon a inne skrypty

Cufon nie bazuje na żadnym frameworku stąd jest raczej bezproblemowy w wypadku gdy stosujemy go na stronach wykorzystujących np. MooTools czy jQuery. Warto za to pamiętać, że tekst stworzony przez Cufon nie jest dynamiczny. Czyli jeżeli będziemy potrzebowali zmodyfikować tekstową zawartość kontenera wypełnionego tekstem z Cufon-a, to po tej modyfikacji tekst wyświetli się z zastosowaniem czcionki wynikającej ze styli CSS. Aby tekst był dalej zapisany naszą czcionką musimy wywołać po zmianach następujący kod:

Cufon.refresh();

Funkcja refresh pobiera opcjonalnie jako argument selektor więc w sytuacji gdy mamy dużo tekstów wygenerowanych przez Cufon-a, możemy wyrenderować czcionkę tylko dla konkretnego elementu.

Podsumowanie

I to by było na tyle w kwestii często występujących problemów, które mogą się pojawić podczas wykorzystywania Cufon-a na naszych stronach. Zainteresowanym polecam oficjalną dokumentację projektu.

 
 

24

września

2010

Gdy MooTools powoduje problemy

Ponieważ mam możliwość obserwowania ogromnej ilości stron bazujących na Joomla! wiem, że od czasu wprowadzenia do tego CMS-a nowej wersji MooTools 1.2.* często pojawia się problem kompatybilności.

Niektórzy developerzy już dostosowali swoje produkty do MooTools 1.2.*, inni nie. Ja osobiście w nowych wersjach modułów planowałem dodanie w opcjach parametru wyboru wersji MooTools używanej w module.

Jednak po dokładnej analizie doszedłem do wniosku, że nie jest to idealne rozwiązanie. Czasem zdarza się, że jakiś komponent na stronie gdzie normalnie używa się MooTools 1.1 "na siłę" dołącza nowszą wersję MooTools i wtedy problem mamy gotowy.

Dlatego postanowiłem skorzystać w tej sytuacji z dobrodziejstw istnienia obiektu MooTools, który przechowuje informacje o wersji frameworka. Wystarczy stworzyć dwa skrypty: jeden dla MooTools 1.1 a drugi dla wersji 1.2 i następnie w głównym skrypcie naszego szablonu/modułu dodać następujący kod:

window.addEvent('load', function() {
        if(MooTools.version.contains('1.1')){
                new Asset.javascript('moo11.js');
        } else {
                new Asset.javascript('moo12.js');
        }
});

Należy pamiętać o tym, że skrypty w plikach moo11.js i moo12.js powinny być tak napisane by wykonywały się od razu (czyli już bez dodawania funkcji obsługi zdarzenia onLoad).

Oczywiście zamiast zdarzenia load możemy wykorzystać zdarzenie domready, ale to już zależy od tego co dokładnie robi skrypt, bo w niektórych wypadkach domready nie spełni naszych oczekiwań.

 
 

18

sierpnia

2010

Operacje na kompletnej sekcji head szablonu

Proces renderowania szablonu w Joomla! składa się generalnie z dwóch etapów. Zrozumienie jak on przebiega ma często kluczowe znaczenie dla działania niektórych skryptów. W dzisiejszym wpisie opiszę dość ciekawy i sądzę, że ciekawy przypadek, kiedy to potrzebujemy uzyskać dostęp do finalnej (powstałej po wszystkich operacjach) wersji sekcji head naszego szablonu.

Problem na jaki się ostatnio natknąłem, polegał na tym, że potrzebowałem zmienić kolejność kilku plików CSS dodawanych przez niektóre moduły do sekcji head szablonu.

Niestety - ze względu na ideę parsowania szablonów w Joomla! nie da się tego zrobić tworząc zwykły kod operujący na sekcji head dokumentu - w czasie gdy będzie się on wykonywał, pliki CSS/JS dodawane przez moduły/komponenty nie będą jeszcze w niej dostępne. Dlaczego ? Joomla! parsuje szablon w dwóch etapach - najpierw parsowany jest kod szablonu wraz z towarzyszącym mu kodem PHP, a dopiero w drugim etapie następuje podmiana wstawek jdoc na właściwie im fragmenty strony. Co za tym idzie dopiero wtedy wykonywany jest kod modułów i komponentów.

W wielkim skrócie proces ten wygląda następująco:

  1. Pierwsze parsowanie szablonu i kodu PHP w nim zawartego
  2. Wystąpienie zdarzenia onAfterDispatch
  3. Drugie parsowanie szablonu: podmiana wstawek jdoc i wykonanie kodu modułów/komponentów
  4. Wystąpienie zdarzenia onAfterRender
  5. Zwrócenie do przeglądarki finalnego kodu strony z bufora

Jeżeli w naszym szablonie wykonujemy jakiś kod operujący na sekcji head to występuje on na etapie pierwszym. Natomiast część danych w sekcji head pojawia się dopiero na etapie trzecim. Co za tym idzie jedyne miejsce w którym możemy operować na kompletnej sekcji head jest etap czwarty.

I w tym momencie mamy dwa wyjścia:

I na tym drugim rozwiązaniu się skupimy gdyż wymaga ono aż 2 linii kodu ;) Musimy mianowicie stworzyć obiekt klasy JDispatcher, który odpowiada za generowanie zdarzeń i rejestrację funkcji obsługi zdarzeń, a następnie musimy zarejestrować dodatkową funkcję dla zdarzenia onAfterRender. Cały kod to:


$dispatcher = JDispatcher::getInstance();
$dispatcher->register('onAfterRender', 'NASZA_FUNKCJA');

Oczywiście NASZA_FUNKCJA to nazwa funkcji, która ma zostać podczas zdarzenia wywołana. W ten oto sposób część kodu naszego szablonu wykona się znacznie później w procesie renderowania szablonu.

Pora na złą wiadomość - po drugim etapie renderowania szablonu nie jesteśmy już w stanie modyfikować sekcji head z użyciem standardowych metod API - po tym etapie jedyne co możemy modyfikować to zawartość bufora, która ma zostać zwrócona do przeglądarki. Aby dostać się do rzeczonego bufora korzystamy z klasy JResponse:


$buf = JResponse::getBody();

Aby zapisać efekty naszej pracy na zmiennej $buf korzystamy z metody setBody:


JResponse::setBody($buf);

I w ten oto sposób mamy zapisane modyfikacje pełnej sekcji head naszego szablonu.

 
 

16

sierpnia

2010

Panel Joomla! 1.6 i trochę AJAX-a

Zawsze mnie intrygowało czemu w panelu administracyjnym Joomla! nie ma ajaksowych elementów. To ciągłe odświeżanie strony przy pracy zwyczajnie drażni. Dlatego też postanowiłem w wolnym czasie powoli sobie usprawniać samodzielnie panel Joomla!. Na pierwszy ogień poszły wszechobecne tabele: od zarządzania modułami/użytkownikami etc. Dzięki prostemu skryptowi sortowanie kolumn, filtrowanie oraz stronicowanie wreszcie działają tak jak powinny: bez przeładowywania strony :)

Jeszcze nie stworzyłem żadnego plugina, zatem dodanie moich rozwiązań do najnowszej wersji beta Joomla! wymaga paru minut, ale myślę, że warto je poświęcić.

Zacznijmy od pliku JavaScript - poniżej jego kod źródłowy:


window.addEvent('domready',function(){   
    new JAjax(); 
});

var JAjax = new Class({
    main: null,
    preloader: null,
    preloaderFX:null,
    $this: null,
    
    initialize: function(){
        $this = this;
        this.main = document.id('element-box');
        this.preloader = new Element('div').setProperty('class','gk-preloader');
        this.preloaderFX = new Fx.Tween(this.preloader, { duration:250, wait:false }).set('opacity', 0);
        this.preloader.inject(document.id('element-box'), 'inside');
        this.initUI();
    },
    
    initUI: function(){
        if($$('form .adminlist')[0]){
            document.adminForm.setProperty('action', document.adminForm.getProperty('action')+'&tmpl=ajax');
            $this.filter();
            $this.sort();
            $this.pagination();
        }    
    },
    
    sort: function(){ 
       $$('form .adminlist')[0].getElements('thead a').each(function(el){
            var href = el.getProperty('href');
            href = href.replace('javascript:tableOrdering(','').replace(');','').replace(/\'/g,'').split(',',2);
            el.setProperty('href',href[0]+','+href[1]);
            
            el.addEvent('click', function(e){    
                e.stop();
                cmd = el.getProperty('href').split(',');
                document.adminForm.filter_order.value = cmd[0];
                    document.adminForm.filter_order_Dir.value = cmd[1];
                $this.makeTableReq(); 
            });
       });
    },
    
    filter: function(){
        $$('#filter-bar select').each(function(el){
            el.removeProperty('onchange');
            el.addEvent('change', function(e){
                $this.makeTableReq(); 
            });
        });
        
        $$('#filter-bar .filter-search button').each(function(el,i){
            el.addEvent('click',function(e){
                e.stop();
                if(i == 1) document.id('filter_search').value='';
                $this.makeTableReq();                 
            });
        });
    },
    
    pagination: function(){
        document.id('limit').removeProperty('onchange');
        document.id('limit').addEvent('change', function(e){
            $this.makeTableReq();
        });
        
        $$('tfoot .pagination a').each(function(el){
            var limit = el.getProperty('onclick');
            el.removeProperty('onclick');
            el.addEvent('click', function(e){
               e.stop();
               document.adminForm.limitstart.value = limit.replace(/[^0-9]+/g,''); 
               $this.makeTableReq();
            });
        });
    },
    
    makeTableReq: function(){ 
        new Request.HTML({
            url: document.adminForm.getProperty('action'),
            onComplete: function(nodes, elms, code){
                $$('#element-box .m')[0].innerHTML = code;
                $this.preloaderFX.start('opacity', 0);
                $this.initUI();
            },
            onRequest: function(){
                $this.preloaderFX.start('opacity', 0.85); 
            }    
        }).post(document.adminForm);
    }
});

Tworzymy plik tools.js i umieszczamy go w katalogu administrator/templates/bluestork/js/.

Następnie tworzymy plik tools.css i umieszczamy go w katalogu administrator/templates/bluestork/css/:


#element-box{ 
    position:relative; 
}

.gk-preloader{ 
    background:#fff; 
    height:100%; left:0; 
    position:absolute; 
    top:0; 
    width:100%; 
    z-index:1; 
}

Ponieważ osobiście nie dotarłem jeszcze do odpowiednika format=raw w Joomla! 1.6 problem rozwiązałem inaczej tworząc plik ajax.php w katalogu administrator/templates/bluestork/ :


<jdoc:include type="message" />
<jdoc:include type="component" />

Na koniec w pliku index.php szablonu bluestork pod koniec sekcji head dodajemy dwie linijki:


<script type="text/javascript" src="templates/<?php echo  $this->template ?>/js/tools.js"></script>
<link rel="stylesheet" type="text/css" href="templates/<?php echo  $this->template ?>/css/tools.css" />    

I gotowe :)

Od teraz klikając na tytuły kolumn w tabelach, przyciski stronicowania/zmiany ilości elementów czy korzystając z filtrowania, wszystko powinno się ładować asynchronicznie. Planuję dalsze usprawnienia w niedalekiej przyszłości, ale powoli: trzeba przy Joomla! działać ostrożnie by przypadkiem nie popaść w konflikty z jakimiś zewnętrznymi komponentami :)

 
 

16

lipca

2010

Joomla! 1.5.19 i MooTools 1.2.4

W najnowszej wersji Joomla! dodano plugin nazywany MooTools 1.2.4 upgrade plugin. Jest on domyślnie wyłączony i działa on na zasadzie - potrzebujesz MooTools 1.2.4 na swojej stronie ? - to włącz ten plugin. Osobiście jednak uważam, że dodanie takiego dodatku do Joomla! jest bardzo złym i nieprzemyślanym posunięciem.

Powiedzmy sobie otwarcie - jeżeli ktoś chciał korzystać z MooTools 1.2.4 na swojej stronie opartej o Joomla! to i tak to od dawna robił. Joomla 1.5.* od początku bazowała na MooTools 1.1.* i dodawanie takiej opcji do tego CMS-a w tym momencie oznacza jedno: developerzy rozszerzeń muszą (no dobrze - powinni) napisać spory kawałek warstwy client-side swoich produktów od nowa i dodatkowo tak rozwiązać problem by w zależności od wybranej wersji MooTools na witrynie dane rozszerzenie automatycznie wybierało z której wersji skryptu ma korzystać.

Pytanie podstawowe jakie się nasuwa - na co to komu ? Duża część rozszerzeń i tak prawdopodobnie nie zostanie zaktualizowana więc prawdopodobnie nowy tryb pracy będzie wykorzystywany sporadycznie. Bo o ile pogodzić jQuery i MooTools nie jest specjalnie trudno to niestety ale dwie różne wersje tego samego frameworka raczej nie chcą ze sobą współpracować.

Mam cichą nadzieję, że nie będzie podobnych przebojów z Joomla! 1.6 i MooTools 1.2.4 oraz MooTools 1.3.

 
 

13

lipca

2010

Joomla! 1.6 i własny styl formularzy w panelu administracyjnym

W Joomla! 1.5 pola formularza danego rozszerzenia można było łatwo modyfikować poprzez własne style CSS i skrypty JavaScript dołączając je np. w opisie danego rozszerzenia. Nie było to może rozwiązanie piękne, ale działało skutecznie. W Joomla! 1.6 niestety nie mamy już do dyspozycji opisu w miejscu gdzie występuje formularz konfiguracji danego rozszerzenia.

Na szczęście Joomla! udostępnia nam inny mechanizm, który możemy wykorzystać na nasze potrzeby - możliwość tworzenia własnych rodzajów pól formularza.

Własne pola formularza tworzymy na bazie klasy JFormField. Przykłady standardowych pól dostępnych w Joomla! możemy sobie przejrzeć w katalogu libraries/joomla/form/fields/.

Stworzymy więc własny rodzaj pola formularza, który pozwoli nam dołączać skrypty i style do danej strony. W tym celu tworzymy w katalogu z naszym rozszerzeniem folder admin/elements/ i umieszczamy w nim plik asset.php:

<?php

defined('JPATH_BASE') or die;

jimport('joomla.form.formfield');

class JFormFieldAsset extends JFormField {
        protected $type = 'Asset';

        protected function getInput() {
                $doc = JFactory::getDocument();

                if($this->element['extension'] == 'js') {
                        return $doc->addScript(JURI::root().$this->element['path']);
                } else {
                        return $doc->addStyleSheet(JURI::root().$this->element['path']);        
                }
        }
}

?>

Powyższa klasa definiuje pole formularza, które będzie miało następującą budowę:

<field type="asset" extension="ROZSZERZENIE" path="ŚCIEŻKA" />

Gdzie ROZSZERZENIE przyjmuje wartość js lub css natomiast ŚCIEŻKA to adres żądanego pliku. Na przykład: "templates/NAZWA_SZABLONU/admin/js/skrypt.js".

I już prawie jesteśmy u celu - pozostaje nam jeszcze poinformować skrypt gdzie będą znajdowały się dodatkowe klasy pól formularza - w tym celu w elemencie fieldset, który będzie zawierał nasz własny rodzaj pola formularza dodajemy atrybut addfieldpath (w Joomla! 1.5 był to atrybut addPath) ze ścieżką do naszego katalogu z dodatkowymi klasami, na przykład: "/templates/NAZWA_SZABLONU/admin/elements".

Po tym zabiegu możemy już śmiało tworzyć własne style CSS i skrypty dla formularzy w panelu administracyjnym znacząco rozszerzające ich zwyczajne możliwości.

 
 

12

lipca

2010

Joomla! i LESS

Szablony dla Joomla! mają tą cechę, że z reguły są dość rozbudowane, zwłaszcza jeżeli chodzi o kod CSS: tworząc szablon trzeba ostylować standardowe podstrony, dodatkowe moduły, komponenty. Im bardziej chcemy zadbać o detale tym bardziej rośnie ilość kodu. Kodu w którym pewne rzeczy się powielają. Dlatego od pewnego czasu jestem zwolennikiem wykorzystywania do tworzenia szablonów prekompilatorów CSS. Jednym z takich narzędzi jest LESS. Pokażę jak bezboleśnie zaimplementować LESS w swoim szablonie oraz jak wykorzystać jego możliwości.

LESS pierwotnie został stworzony w języku Ruby, jednak nie stanowi to żadnego problemu gdyż istnieją jego implementacje w innych językach m.in. w PHP (lessphp) i JavaScript (LESS.js). Wersja JavaScript jest mniej wymagająca pod względem dołączenia do szablonu, natomiast jeżeli wyłączymy JavaScript w przeglądarce to niestety mamy problem. Dlatego bezpieczniej skorzystać z wersji LESS, która działa po stronie serwera.

Tutaj mała uwaga - osobiście polecam korzystanie z LESS tylko na etapie tworzenia szablonu i jego ewentualnych modyfikacji. Dlaczego ? Nie ma sensu by LESS przetwarzał nasze pliki *.less przy każdym wywołaniu strony lub co określony czas (przy wykorzystaniu cache). Dlatego po zakończeniu prac najlepiej zamienić prezentowany kod na odwołania do wygenerowanych z użyciem LESS plików *.css.

Co nam daje LESS ?

LESS rozszerza składnię CSS-a o wiele nowych możliwości. Zacznijmy od mojej ulubionej czyli zmiennych. Większość layoutów ma to do siebie, że bazuje na kilku kolorach. W LESS zmienne tworzymy korzystając z zapisu:

@nazwa_zmiennej: WARTOŚĆ;

Przykład:

@kolor1: #000;
@kolor2: #fff;

#nawigacja{
        color: @kolor1;
        background: @kolor2;
}

.code{
        color: @kolor2;
        background: @kolor1;
}

W powyższej sytuacji gdybyśmy chcieli odwrócić kolory w elemencie #nawigacja oraz elementach z klasą code wystarczy zmienić wartości zmiennych. Niesamowicie przydatne gdy mamy ogromny plik CSS z mnóstwem powtarzających się kolorów.

Druga ważna funkcjonalność - zagnieżdżanie elementów. Zamiast pisać:

#nawigacja {}
#nawigacja a {}
#nawigacja a:hover {}

Zapisujemy:

#nawigacja {
        /* tu style dla elementu #nawigacja */
        color: #000;
        margin: 10px;

        a {
                /* tu style dla linków w #nawigacja */
                color:#aaa;
                display:block;
                
                /* a tu style dla pseudoklas */
                :hover {
                        text-decoration:none;
                }
        }
}

Taki zapis jest o wiele czytelniejszy bo często niektórzy próbują kod formatować następująco:

#nawigacja {}
        #nawigacja a {}
        #nawigacja a:hover {}

By podkreślić, że selektory tyczą się elementów zagnieżdżonych w innym elemencie.

Inna warta wspomniena rzecz to możliwość ponownego wykorzystana raz już stworzonego kodu:

.test{
        color:#000;
        background:#fff;
}

Teraz możemy osadzić właściwości color i background w innym elemencie pisząc po prostu:

.test2{
        .test;
}

Możemy też się odwoływać bezpośrednio do elementów zagnieżdżonych czy nawet do konkretnych wartości właściwości lub zmiennych, a nawet tworzyć klasy z parametrami.

Możliwości LESS są naprawdę duże - zainteresowanych odsyłam do dokumentacji lessphp

My tymczasem wróćmy do meritum tego posta czyli użycia LESS w Joomla!.

Jak wykorzystać LESS w Joomla! ?

Aby użyć LESS polecam najpierw w katalogu zawierającym style CSS szablonu utworzyć katalog less/, który będzie zawierał pliki źródłowe *.less do przetworzenia. W katalogu ze stylami umieszamy plik lessphp.inc.php pobrany ze strony projektu lessphp.

W pliku index.php naszego szablonu dołączamy plik lessphp.inc.php:

require_once('css/lessc.inc.php');

oraz definiujemy następującą funkcję:

function generate_less($file, $template_name = 'beez5'){
        try {
            lessc::ccompile(JPATH_THEMES.DS.$template_name.DS.'css'.DS.'less'.DS.$file.'.less', JPATH_THEMES.DS.$template_name.DS.'css'.DS.$file.'.css');
            echo '<link rel="stylesheet" type="text/css" href="templates/'.$template_name.'/css/'.$file.'.css" />';
        } catch (exception $e) {
            exit('LESS ERROR:'.$e->getMessage()."\nFILE:".$file.'.less');
        }
}

Oczywiście musimy pamiętać też o zapewnieniu stosownych uprawnień do zapisu w katalogu css/ i zmienieniu w parametrach funkcji nazwy szablonu na nazwę naszego szablonu.

Dzięki temu możemy odwoływać się do plików CSS poprzez zapis:

<?php generate_less('template'); ?>

Powyższy zapis wygeneruje z pliku css/less/template.less plik css/template.css i umieści odnośnik do niego w miejscu wywołania.

Parę uwag na koniec

Jedną z bolączek LESS jest dosyć małe wsparcie dla składni w edytorach. Osobiście korzystam z edytora Coda i tego rozwiązania.

LESS.js ma jeszcze tą wadę, że wszystkie generowane przez niego style są osadzane bezpośrednio w kodzie strony - stąd jeżeli mamy w plikach *.less odnośniki do grafik tła to musimy je definiować względem głównego dokumentu strony a nie położenia pliku *.less (oczywiście w wypadku naszych plików *.less odniesienia do plików tła tworzymy tak jakby się znajdowały one w katalogu css/ a nie css/less/).

 
 

29

czerwca

2010

Jak NIE należy pisać rozszerzeń dla Joomla!

Podczas pracy z Joomla! widziałem już sporo ogólnodostępnych komponentów i modułów w akcji. Z reguły jest miło i przyjemnie do jednego momentu - chwili w której trzeba trochę zmodyfikować styl/strukturę modułu lub komponentu. W poniższym wpisie chciałbym zaprezentować pięć najbardziej drażniących motywów z którymi wielokrotnie się spotkałem w czasie pracy.

1. Brak katalogu tmpl/ w module

Podstawowa bolączka wielu modułów, wynikająca prawdopodobnie po części z niewiedzy, po części z lenistwa programisty. Nieświadomym przypominam, że katalog tmpl/ zawiera pliki ze strukturą generowaną przez konkretny moduł, które można w łatwy sposób nadpisać w danym szablonie. Zaleta ? Przenosząc szablon na inną instalację Joomla! nie musimy tworzyć własnych wersji plików modułu, które trzeba przenosić z szablonem - wszystko mamy w jednym miejscu. Przy tworzeniu szablonów dla większego grona klientów jest to wręcz jedyne sensowne rozwiązanie (bo typowy klient nie lubi gdy poza zainstalowaniem szablonu każemy mu jeszcze podmienić jakieś pliki/katalogi).

2. jQuery

Joomla! 1.5.* od początku wykorzystywała framework MooTools po stronie back-endu i ma wbudowane dla niego wsparcie po stronie front-endu. Dlatego zupełnie nie rozumiem ludzi, którzy tworzą rozszerzenia dla Joomla! z użyciem jQuery. A już zupełnie nie trawię ludzi, którzy tworzą rozszerzenia dla Joomla! w jQuery i nie stosują funkcjonalności noConflict. Problemy na bardziej złożonej stronie są wtedy wręcz gwarantowane, a o dodatkowym transferze nie wspomnę (nie ma to jak dwa frameworki JavaScript o zbliżonych możliwościach, zastosowane na jednej witrynie). Podsumowując - w światku Joomla! , a szczególnie świecie ogólnodostępnych rozszerzeń lepiej zapomnieć o swojej miłości do jQuery - dla dobra użytkowników.

3. Atrybut style

Kolejny "klasyk" - osobiście rozumiem, że nieraz trzeba zastosować atrybut style do określenia szerokości danego modułu, czy wysokości kontenerów - przy elastyce Joomla! jest to wręcz niezbędne by uniknąć gigantycznej sekcji head z masą znaczników style. Nie rozumiem natomiast zupełnie jak można w ten sposób definiować takie rzeczy jak styl obramowania czy kolor czcionki. Widziałem takie cuda i to o zgrozo także w płatnych komponentach.

4. Bardzo dziwne skrypty

W komponencie Jomsocial ktoś wpadł na pomysł zastosowania skryptów do tworzenia tooltipów. I wszystko działałoby pięknie i ładnie, gdyby nie to, że wszystkie style tych tooltipów były generowane przez... skrypt JavaScript. Fantastyczna sprawa, szczególnie w wypadku tworzenia efektu zaokrąglonych rogów dla tych tooltipów, które w wypadku IE były generowane jako elementy w języku VML i nijak nie szło się ich pozbyć bez ingerencji w kod komponentu.

5. CSS-owy egoizm

Pora na "kwiatek" z komponentu K2 w której zawarto w stylu CSS zupełnie bezstresowo taki oto zapis:

.even{...}
.odd{...}

A teraz proszę sobie wyobrazić co mogło się stać z wszystkimi szablonami i modułami używającymi tych klas ;)

Ja wiem, że to kilka znaków więcej, ale naprawdę warto wyposażyć swoje moduły/komponenty w głównym kontenerze w jakąś charakterystyczną klasę / identyfikator, która może i wydłuży ciut kod CSS:

.dziudek .even{...}
.dziudek .odd{...}

Ale zaoszczędzi masy nerwów i analizowania potencjalnym użytkownikom ;)

Poza rankingiem

I na koniec absolutny klasyk - nieczytelny kod. Wielu ludzi sobie chyba nie zdaje sprawy, że zamiast klamerek w PHP można stosować takie słowa kluczowe jak:

Mógłbym w tym miejscu wkleić kilka znakomitych kawałków nieczytelnego kodu, ale po prostu wręcz nie mogę na nie patrzeć. Naprawdę warto na koniec swojej pracy rzucić okiem w kod i zastanowić się czy jest on chociaż odrobinę czytelny dla innego użytkownika.

Podsumowanie

Podsumowując to zacne zestawienie - warto poświęcić odrobinę czasu i zastanowić się czy to co puszczamy w świat zadziała w ogóle u innych użytkowników, a jeżeli tak to warto się zastanowić czy ciężko jest dostosować nasze dzieło do różnych szablonów. Bo nawet najbardziej funkcjonalny moduł/komponent nie będzie użyteczny dopóki nie będzie się dawał względnie łatwo zmodyfikować.

 
 

01

czerwca

2010

Uwaga na CKForms!

Jeżeli ktoś z czytelników bloga korzysta na swojej stronie opartej na Joomla! z komponentu CKForms to apeluję o wyłączenie tego komponentu dla własnego bezpieczeństwa.

Dziś kolega zaprezentował mi dziurę w tym komponencie, która pozwala na zrobienie właściwie wszystkiego z serwerem na którym postawiona jest strona z wspominanym komponentem. Informacja została wysłana do zespołu tworzącego CKForms, ale jak dotąd nie uzyskano odpowiedzi ani tym bardziej aktualizacji komponentu, którego ostatnia wersja z tego co widzę pochodzi z 20 marca.

 
 

18

kwietnia

2010

Problemy z UTF-8

Gdy zajmujemy się wdrażaniem rozszerzeń dla Joomla!, które są używane do wyświetlania tekstów w różnych językach może pojawić się problem z kodowaniem znaków lub błędnym obliczaniem pozycji znaków w tekście. Wszystkiemu winne są funkcje PHP służące do operowania na ciągach znaków.

W czym tkwi problem ?

Standardowe funkcje PHP takie jak np. substr, stripos nie są przystosowane do operowania na ciągach znaków w których znak może być reprezentowany jako więcej niż jeden bajt. W UTF-8 niektóre znaki są reprezentowane właśnie w taki sposób. Stąd powstało rozszerzenie dla PHP - Multibyte String - jego cechą charakterystyczną są funkcje z prefiksem "mb_". Co jednak jeżeli przygotowujemy rozszerzenie dla szerszego grona użytkowników z których nie każdy posiada zainstalowane u siebie takie rozszerzenie ?

JString

Rozwiązaniem problemu w takiej sytuacji jest klasa JString - jest ona swoistą nakładką na bibliotekę PHP UTF-8, która wchodzi w skład standardowych bibliotek dostarczanych razem z Joomla!.

Po zaimportowaniu klasy JString:

jimport('joomla.utilities.string');

możemy swobodnie korzystać z funkcji operujących na ciągach znaków, które poradzą sobie z UTF-8.

Pełna lista metod tej klasy znajduje się na tej stronie. Nie ma sensu ich tutaj kolejno opisywać gdyż są to po prostu odpowiedniki funkcji znanych z PHP. Jedyną niestandardową metodą jest transcode, która bazuje na rozszerzeniu iconv i pozwala na konwersję ciągu znaków z jednego kodowania na inne (w tym wypadku z UTF-8 na ISO-8859-2):

JString::transcode('UTF-8', 'ISO-8859-2', $text);

Wszystkie metody klasy JString są statyczne więc przykładowo wywołanie metody substr ogranicza się do zapisu:

JString::substr($text, 0);

Dzięki funkcjonalnościom tej klasy unikniemy sytuacji w której użytkownik z Grecji żali się nam, że nasz moduł wyświetlający skróty artykułów zamiast znaków jego alfabetu wyświetla znaki zapytania :)

 
 

11

kwietnia

2010

JBrowser

Dosyć często widuję w kodzie różnego rodzaju rozwiązań dla Joomla!, że jego twórcy próbują odkrywać koło na nowo - tak jest np. w wypadku detekcji przeglądarek. Tak jakby mało kto sobie zdawał sprawę z tego, że framework na którym bazuje Joomla! udostępnia całkiem sensowne rozwiązanie jakim jest klasa JBrowser.

Napisałem, że JBrowser to całkiem sensowne rozwiązanie, ponieważ ma oczywiście kilka wad - z moich testów wynika, że np. nie rozróżnia ona Chrome i Safari, a Safari w iPhone jest traktowane tak samo jak jego desktopowe odpowiedniki - wszystkie trzy przeglądarki są rozpoznawane jako "konqueror". Natomiast świetnie sobie radzi z najważniejszym zagadnieniem jakim jest detekcja różnych wersji IE, bo to w zasadzie główne zastosowanie detekcji przeglądarek (poza detekcją przeglądarek mobilnych). Podobny problem tyczy się listy wspieranych przez przeglądarkę udogodnień - jako, że klasa JBrowser była stworzona jakiś czas temu to nie poinformuje nas, że nowsze wersje Opery wspierają format SVG. Ale te problemy da się oczywiście wyeliminować dzięki dość szerokiej gamie metod oferowanych przez tą klasę.

Tworzenie obiektu klasy JBrowser

Aby rozpocząć pracę z klasą JBrowser musimy najpierw ją zaimportować, gdyż nie wchodzi ona w skład klas standardowo używanych przez rozszerzenia Joomla!. Aby to zrobić umieszczamy w naszym kodzie linijkę:

jimport('joomla.environment.browser');

Obiekty tej klasy możemy tworzyć przez standardowy konstruktor, lub z użyciem metody getInstance, osobiście polecam ten drugi sposób, chyba, że mamy jakiś powód dla którego musimy uzyskać niemodyfikowany wcześniej obiekt klasy JBrowser - wtedy lepiej skorzystać ze standardowego konstruktora.

$browser = JBrowser::getInstance();

Zarówno konstruktor jak i metoda getInstance pobierają dwa argumenty - ciąg user agent oraz zawartość nagłówka HTTP_ACCEPT. Oba te argumenty są oczywiście opcjonalne i mają wpływ na pozostałe składowe obiektu.

Jeżeli chcemy określi wspomniane dwa parametry już w czasie istnienia obiektu to możemy skorzystać z funkcji match, która działa tak samo jak getInstance z podanymi dwoma argumentami:

$browser->match("ciąg user agent", "ciąg HTTP_ACCEPT");

Tablice _features, _quirks i _robots

Obiekt klasy JBrowser zawiera trzy interesujące tablice:

I oczywiście mamy też kilka metod związanych z tymi tablicami. Możemy sprawdzić czy dane rozwiązanie jest wspierane przez przeglądarkę poprzez metodę getFeature:

$browser->getFeature('svg');

Jeżeli dodatkowo wykryjemy, że np. mamy do czynienia z Operą w nowszej wersji to wpierw możemy jej ustawić wsparcie dla SVG poprzez metodę setFeature:

$browser->setFeature ('svg');

Domyślna wartość drugiego argumentu tej metody to true, stąd go nie podaję.

Tak samo wygląda sytuacja z obsługą tablicy _quirks - mamy metody getQuirk i setQuirk, które pobierają argumenty w identycznej ilości i typie. Dodatkowo mamy do dyspozycji metody hasFeature i hasQuirk, które sprawdzają czy dana pozycja znajduje się w danej tablicy.

Aby stwierdzić czy aktualna przeglądarka odwiedzająca naszą witrynę jest botem jednej z wyszukiwarek wystarczy wywołać metodę isRobot:

if($browser->isRobot()) {
    // kod wykonywany gdy przeglądarka jest botem
}

Tutaj polecam też zapoznanie się z listą dostępną na http://www.user-agents.org/ i ewentualne uzupełnienie standardowej listy o dodatkowe pozycje by lepiej wykrywać boty wyszukiwarek.

Detekcja platformy i przeglądarki

Oczywiście głównym wątkiem związanym z klasą JBrowser jest sama detekcja rodzaju przeglądarki - związane z tym jest kilka metod, pierwszą z nich jest getAgentString, która zwraca nam cały ciąg user agent:

$user_agent = $browser->getAgentString();

I tą metodę będziemy wykorzystywać do detekcji wszelkich przeglądarek nierozpoznawanych standardowo przez klasę JBrowser. Przykładowo dla detekcji iPhone wykonamy kod:

$is_iphone = false;

if(preg_match('/iPhone/',$browser->getAgentString())){
    $is_iphone = true;
}

Aby poznać nazwę przeglądarki i platformy użytkownika korzystamy z metod getBrowser i getPlatform:

echo 'Przeglądarka:' . $browser->getBrowser(). '<br />';
echo 'OS:' . $browser->getPlatform();

Możemy też uzyskać informacje o wersji protokołu HTTP:

$browser->getHTTPProtocol();

Oraz dokładne informacje o wersji przeglądarki:

echo $browser->getMajor() . '<br />';
echo $browser->getMinor() . '<br />';
echo $browser->getVersion();

W wypadku IE 8.0 uzyskamy po uruchomieniu powyższego kodu następujące trzy linijki:

8
0
8.0

Inne metody klasy JBrowser

Wracając jeszcze do przykładu z iPhone - zamiast korzystać z dodatkowej zmiennej $is_iphone, możemy to zrobić bardziej elegancko, poprzez metody setBrowser i isBrowser:

if(preg_match('/iPhone/',$browser->getAgentString())){
    $browser->setBrowser('iPhone');
}

Natomiast późniejszego rozpoznania iPhone dokonamy poprzez:

if($browser->isBrowser('iPhone')){
    // kod wykonywany tylko na iPhone
}

Na koniec pozostała nam jeszcze metoda isSSLConnection, która sprawdza czy bieżące połączenie jest połączeniem szyfrowanym - dzięki temu możemy wyświetlić na stronie stosowny komunikat gdy wiemy, że na danej stronie połączenie powinno być szyfrowane a nie jest.

 
 

09

marca

2010

Zmiana tła strony w zależności od podstrony

Dzisiaj szybkie skryptowe rozwiązanie dość często spotykanego problemu zmiany tła w zależności od podstrony, który powinien w jakimś stopniu zniknąć w Joomla! 1.6, gdzie będzie istniała możliwość zmiany ustawień szablonu dla konkretnych podstron (obecnie trzeba tworzyć kopie danego szablonu i przypisywać go do konkretnych podstron).

Do osiągnięcia pożądanych rezultatów musimy zrobić dwie rzeczy: stworzyć w katalogu images/ naszego szablonu katalog backgrounds/ wypełniony grafikami tła, których chcemy użyć oraz dodać poniższy skrypt w sekcji head naszego szablonu (najlepiej na samym jej końcu):

<?php
    // tablica powiązań Itemid <-> grafika tła
    $bg_images = array(
        "53" => "1.jpg",
        "54" => "2.jpg",
        "default" => "3.jpg" // grafika domyślna
    );

    $itemID_value = JRequest::getCmd('Itemid');
    $bg_image = (isset($bg_images[$itemID_value])) ? $bg_images[$itemID_value] : $bg_images["default"];
    $url =& JURI::getInstance();
    
?>

<style type="text/css">
body{
background:#fff url('<?php echo $url->root(); ?>templates/<?php echo $this->template; ?>/images/backgrounds/<?php echo $bg_image; ?>') center 0!important;
}
</style>

Jak to działa ?

Powyższy skrypt dysponuje tablicą powiązań $bg_images, która definiuje powiązania pomiędzy wartościami zmiennej Itemid z adresu i obrazkiem tła przypisanym do danej wartości Itemid. Skrypt wczytuje wartość Itemid z adresu i na tej podstawie określa czy do danej wartości został przypisany jakiś szczególny typ tła, w przeciwnym wypadku zostanie zastosowana grafika domyślna.

Za ustawienie tła odpowiada osadzony styl CSS, w którym można oczywiście pozmieniać parametry pozycjonowania tła, koloru tła i jego powielania ;)

Oczywiście nic nie stoi na przeszkodzie by określać także inne parametry tła w ten sposób, a nawet dodawać całe style CSS

 
 

04

marca

2010

Wbudowane szablony w Joomla!

Tworząc szablon dla Joomla! możemy zdefiniować nie tylko wygląd strony i jej podstron, ale także wygląd standardowych komunikatów o błędach czy informacji o pracach konserwacyjnych witryny. W razie potrzeby możemy też zdefiniować kilka własnych szablonów spełniających określone warunki - np. potrzebne do wymiany danych z użyciem AJAX-a. A wszystko to dzięki jednej zmiennej znajdującej się w adresie - tmpl.

Z reguły szablony posiadają w głównym katalogu jedynie plik index.php, czasem plik component.php - są one wykorzystywane do generowania treści strony w zależności od wartości zmiennej tmpl w adresie (domyślna jej wartość to index). W pliku component.php definiujemy wygląd strony zawierającej wyłącznie aktualny komponent. Jeżeli przyjrzymy się zawartości katalogu templates/system/ to zauważymy, że istnieją jeszcze dwa pliki tego typu: error.php oraz offline.php. Pierwszy z nich odpowiada za komunikaty o błędach wyświetlane np. w wypadku wpisania nieistniejącego adresu, a drugi odpowiada za wygląd strony informującej o pracach konserwacyjnych. Oba pliki także możemy nadpisać w katalogu głównym naszego szablonu i w ten sposób uzyskać stylowanie spójne z wyglądem naszego projektu.

Tak naprawdę największe możliwości daje nam możliwość stworzenia własnych szablonów tego typu. Jeżeli ktoś się zastanawiał jak zaimplementować skrypty korzystające z AJAX-a w Joomla! to odpowiedzią jest: odpowiedni komponent + odpowiedni szablon.

Komponent w tym wypadku wygeneruje nam odpowiednie dane (załóżmy, że będą one w formacie XML), a szablon wygeneruje nam zawartość komponentu bez całej zbędnej otoczki jaką stanowi w tym wypadku np. nagłówek dokumentu.

Wystarczy stworzyć w głównym katalogu naszego szablonu plik ajax.php o następującej zawartości:

<?php

    $document =& JFactory::getDocument();
    $document->setMimeEncoding('application/xml');
    
    defined( '_JEXEC' ) or die( 'Restricted access' );

?>

<jdoc:include type="component" />

Pierwsze dwie linijki powodują zmianę wyjściowego typu MIME dokumentu (jeżeli chcemy otrzymywać dane w formacie JSON to wystarczy zmienić atrybut metody setMimeEncoding), kolejna linijka uniemożliwia zdalne wywoływanie naszego szablonu, a treść naszego szablonu stanowi zawartość komponentu zależna od parametrów zawartych w adresie strony.

Od teraz każde wywołanie strony gdzie w adresie znajdzie się zmienna tmpl o wartości ajax spowoduje wykorzystanie naszego nowego szablonu. Przykładowo adres:

index.php?option=com_ajax&tmpl=ajax

Spowoduje wygenerowanie danych udostępnianych na stronie komponentu com_ajax z użyciem szablonu ajax.php.

Usuwając zmienną tmpl z adresu wyświetlimy zawartość komponentu w tradycyjny sposób.

W ten sposób możemy łatwo tworzyć szablony odgórnie określające format wyjścia danych z komponentu. Można też teoretycznie korzystać ze zmiennej format w adresie, ale wymaga ona zdefiniowania w komponencie odpowiednich widoków odpowiadających danemu formatowi wyjścia, a dzięki prezentowanemu sposobowi robimy wszystko tylko raz zamiast robić to dla każdego komponentu oddzielnie.

Przy okazji warto też wspomnieć, że dla danego szablonu można dosyć w łatwy sposób zmieniać formaty wyjścia - np. w wypadku szablonu ajax.php pożądanymi formatami byłyby JSON i XML - nie ma sensu tworzyć dwóch oddzielnych szablonów - wystarczy zdefiniować dodatkową zmienną np. output i w zależności od jej wartości generować odpowiedni typ MIME dla dokumentu.

Na koniec przykładowy kod takiego szablonu z użyciem klasy JRequest:

<?php

$format = JRequest::getCmd('output') == 'json' ? 'application/json' : 'application/xml'; 

$document =& JFactory::getDocument();
$document->setMimeEncoding($format);
defined( '_JEXEC' ) or die( 'Restricted access' );

?>

<jdoc:include type="component" />

Oczywiście jak widać w ramach lenistwa - gdy podamy jakąkolwiek wartość zmiennej output (lub jej nie podamy) inną niż json wtedy otrzymamy dane w formacie XML.

Na koniec dodam jeszcze, że warto rozważyć stosowanie wspomnianych szablonów np. do tworzenia mobilnych wersji szablonów czy różnych odmian szablonu odgórnie pozbawionych pewnych pozycji modułów.

 
 

25

lutego

2010

DomReady i MooTools 1.11

Joomla! 1.5 oraz większość jej rozszerzeń w skryptach JavaScript korzysta z MooTools 1.11, które jak już kiedyś wspominałem ma czasem problem z obsługą zdarzenia DomReady w przeglądarkach IE.

Jeżeli nie chcemy oglądać błędów postaci:

Wiadomość: HTML Parsing Error: 
Unable to modify the parent container element before the 
child element is closed (KB927917)
Wiersz: 0
Znak: 0
Kod: 0

To proponuję dodać poniższy fragment kodu na końcu pliku mootools.js z katalogu media:

Element.Events.domready = {
        add: function(fn){
                if (window.loaded){
                        fn.call(this);
                        return;
                }
                var domReady = function(){
                        if (window.loaded) return;
                        window.loaded = true;
                        window.timer = $clear(window.timer);
                        this.fireEvent('domready');
                }.bind(this);
                if (document.readyState && window.webkit){
                        window.timer = function(){
                                if (['loaded','complete'].contains(document.readyState)) domReady();
                        }.periodical(50);
                } else {
                        window.addListener("load", domReady);
                        document.addListener("DOMContentLoaded", domReady);
                }
        }
};

Zamiast szukać wszystkich skryptów z linijką:

window.addEvent("domready", function(){

I zamieniać jej na:

window.addEvent("load", function(){

To rozwiązanie ma ten plus, że w normalnych przeglądarkach (innych niż IE) zdarzenie domready będzie cały czas działało jako domready, a tylko w IE zostanie od razu zamienione na zdarzenie load.

 
 

23

lutego

2010

Dla pewności

Dziś krótki wpis z dosyć istotną wskazówką związaną ze stylowaniem rozszerzeń dla Joomla!. Otóż Joomla! oferuje nam dostęp do sekcji head dokumentu, dzięki czemu z poziomu modułu/komponentu możemy w łatwy sposób dodać skrypt lub styl CSS. I niektórzy developerzy z tej możliwości chętnie korzystają żeby np. uniknąć osadzania elementów link wewnątrz sekcji body witryny (co zresztą powoduje błędy walidacji). Sam dostęp i operacje na sekcji head szablonu dla Joomla! opiszę w jednym z najbliższych wpisów, gdyż jest to dość ważny i szeroki temat, a póki co mały trick, który może być przydatny gdy tworzymy np. szablon w wersji dla urządzeń mobilnych.

Gdy tworzymy jakiś szablon okrojony z większości gadżetów - taki jak na przykład layout dla iPhone, chcemy mieć pewność, że załadujemy tylko te style CSS, których potrzebujemy (można by tu jeszcze uwzględnić kwestie oszczędności transferu). Jak wspominałem niektóre rozszerzenia np. K2 dodają style bezpośrednio do sekcji head - jeżeli nie chcemy tworzyć ręcznie znaczników typu title, meta i po prostu bardzo potrzebujemy w naszym szablonie zapisu:

<jdoc:include type="head" />

a dodatkowo chcemy mieć pewność, że żaden zewnętrzny komponent nie będzie ładował w naszym szablonie swoich styli CSS, to musimy się ich pozbyć. Jak to zrobić ?

Rozwiązaniem jest poniższy kod, który najlepiej umieścić gdzieś pod koniec kodu naszego szablonu:

        <?php
                $document =& JFactory::getDocument();
                $headData = $document->getHeadData();
                $headData["styleSheets"] = array();
                $document->setHeadData($headData);
        ?>

Oczywiście jeżeli mamy zainstalowane na serwerze PHP5 to możemy pozbyć się operatora =& z pierwszej linijki.

Prezentowany skrypt wczytuje do zmiennej zawartość sekcji head naszego szablonu, czyści tablicę styli CSS, a nastepnie zapisuje sekcję head w nowej wersji. Dzięki temu mamy pewność, że użytkownik wchodząc na podstronę z komponentem nie zobaczy stylowania charakterystycznego dla danego komponentu, co w niektórych wypadkach bez usunięcia styli wyglądałoby dość tragicznie na telefonach.

I jeszcze mała uwaga na koniec - w powyższym kodzie sekcję head rozumiemy jako pewną strukturę przechowującą informacje o różnych znacznikach umieszczanych potem na stronie poprzez zapis:

<jdoc:include type="head" />

Owa struktura nie jest równoznaczna całej zawartości naszej sekcji head, gdyż poza tym zapisem możemy sami ręcznie dodać własne style CSS lub skrypty, które w tej strukturze nie będą widoczne.

 
 

17

lutego

2010

Własne style modułów

Pewne fragmenty kodu w szablonie powtarzają się dość często - pomiędzy kodem tworzącym układ szablonu a kodem generowanym przez moduł z reguły znajduje się jeszcze dodatkowa otoczka w postaci dodatkowych kontenerów. Używane są do różnych celów - do prezentacji tytułu modułu, do stworzenia zaokrąglonych rogów wokół modułu czy też otoczki znanej z trybu debugowania szablonu. Joomla! standardowo oferuje sześć takich styli - ich spis wraz z dokładnym opisem i strukturą znajdziemy na tej stronie.

Czasem jednak potrzebujemy czegoś więcej - dodatkowych elementów lub zupełnie innej struktury niż ta oferowana standardowo. Oczywiście w Joomla! nie zapomniano o zapotrzebowaniu na takie rozwiązania - możemy stworzyć dowolną ilość własnych styli modułów, które spełnią nasze potrzeby.

Teoria

Zacznijmy może od tego jak określamy styl jaki ma być użyty dla danej pozycji modułów.

W kodzie szablonu tworząc nową pozycję modułów korzystamy z zapisu:

<jdoc:include type="modules" name="NAZWA_POZYCJI" style="STYL" />

To właśnie atrybut style w powyższym znaczniku odpowiada za przypisanie konkretnego stylu do danej pozycji modułów.

Przykładowo zapis:

<jdoc:include type="modules" name="topnav" style="xhtml" />

przypisze do pozycji z modułami o nazwie topnav styl xhtml.

Oczywiście należy tutaj pamiętać o jednej bardzo ważnej rzeczy - wspomniana "otoczka" zostanie dołączona do każdego modułu ODDZIELNIE. Innymi słowy - jeżeli będziemy mieli styl, który otacza moduły kontenerem z klasą "module" to w sytuacji gdy na jednej pozycji mamy 2 lub więcej modułów zostanie wygenerowany kod:

<div class="module">
    <!-- kod pierwszego modułu na danej pozycji -->
</div>
<div class="module">
    <!-- kod drugiego modułu -->
</div>

Zamiast:

<div class="module">
    <!-- kod pierwszego modułu na danej pozycji -->
    <!-- kod drugiego modułu -->
</div>

Dlatego raczej nie należy traktować styli modułów jako narzędzia do dodawania dodatkowego kodu dla konkretnych fragmentów layoutu, no chyba, że mamy pewność iż na danej pozycji będzie opublikowany tylko jeden moduł.

Powyższy problem oczywiście nie istnieje jeżeli korzystamy z osadzania modułów poprzez:

<jdoc:include type="module" name="NAZWA_MODUŁU" style="STYL" />

Najważniejsze to wiedzieć gdzie w ogóle umieścić kod odpowiadający za nasze style modułów - w tym wypadku jest to plik html/modules.php w katalogu głównym naszego szablonu (katalog html w szablonach odpowiada za przechowywanie wszelkich plików nadpisujących zainstalowane rozszerzenia).

Definicję nowego stylu tworzymy poprzez utworzenie funkcji o określonej deklaracji:

function modChrome_NAZWA( $module, &$params, &$attribs )

Funkcja taka nie zwraca żadnej wartości - od razu wyświetla kod modułu otoczony odpowiednimi kontenerami.

Pierwszy argument to obiekt modułu, który będzie wyświetlony, drugi argument przechowuje odwołanie do konfiguracji naszego modułu (obiekt klasy JParameter) a trzeci argument jest ściśle powiązany ze zmienną $options z poprzedniego wpisu, ale o tym za chwilę.

Obiekt modułu przechowuje kilka ważnych z punktu widzenia stylu modułu informacji, z których trzy będą dla nas kluczowe:

Klasa JParameter posiada metodę get, która zwraca nam wartość parametru o danej nazwie. Na przykład:

$params->get('test', 0);

Zwróci nam wartość parametru "test", a jeżeli on nie istnieje to zostanie zwrócona domyślna wartość podana jako drugi parametr - 0.

Jeżeli zaś chodzi o tablicę $attribs to ma ona postać taką jak podawaliśmy jako wartość tablicy $options w wpisie lub stanowi ona tablice dodatkowych atrybutów znacznika jdoc:include. Daje to nam szerokie pole do popisu gdyż możemy utworzyć znacznik o następującej postaci:

<jdoc:include type="module" name="NAZWA_MODUŁU" style="STYL" textColor="#000" backgroundColor="#fff" />

W powyższym wypadku tablica $attribs przyjmie postać:

Array ( 
    [name] => NAZWA_MODUŁU 
    [style] => STYL 
    [textColor] => #000
    [backgroundColor] => #fff 
)

Jak więc widać możemy tworzyć w pełni konfigurowalne style modułów, dzięki czemu nie trzeba tworzyć kilku odmian tego samego stylu - wystarczy podać odpowiednie parametry.

Tworzymy własny styl modułu

Stworzymy prosty styl modułu, który będzie posiadał następujące cechy:

Nasz styl nazwiemy "simple".

Poniżej pełny kod naszego stylu wraz z opisem.

function modChrome_simple($module, &$params, &$attribs)
{
        // Sprawdzamy czy zdefiniowano atrybut 'id' - jeżeli tak
        // to generujemy kod atrybutu z jego wartością
        $id = (isset($attribs['id'])) ? ' id="'.$attribs['id'].'"' : '';
        
        // Pobieramy wartość parametru sufiksu modułu
        $suffix = $params->get('moduleclass_sfx');
        
        // Zaczynamy generowanie kodu otaczajączego moduł
        // od głównego kontenera
        echo '<div class="module'.$suffix.'"'.$id.'>';
        
        // sprawdzamy czy włączono pokazywanie tytułu modułu
        if ($module->showtitle != 0)
        {
                // wyświetlamy tytuł
                echo '<h3><span>'.$module->title.'</span></h3>'; 
        }

        // generujemy treść modułu otoczoną kontenerem
        echo '<div class="module_content">';
        echo $module->content;
        echo '</div>';

        // domykamy główny kontener
        echo '</div>';
}

Jak widać do pobrania wartości opcji konfiguracyjnej określającej sufiks modułu wystarczy następujący kod:

$params->get('moduleclass_sfx')

Sufiksy są niezwykle przydatne, gdyż na bazie jednego układu/stylu modułów, możemy określić dowolną liczbę stylowań np. dla klas:

itd.

Sprawdzanie czy pokazywanie tytułu jest włączone to wręcz obowiązkowa pozycja każdego stylu modułów wykorzystującego pokazywanie tytułu modułu.

Samą treść otaczamy oczywiście w jeszcze jeden kontener "module_content" by ułatwić oddzielenie jej od tytułu modułu.

Zapis:

<jdoc:include type="modules" name="header" style="simple" id="test" />

Spowoduje otoczenie modułów z pozycji "header" kontenerem z id "test" (i oczywiście pozostałym kodem naszego stylu).

Inne ciekawe możliwości

Warto pamiętać, że kod zaprezentowany w tym wpisie to tylko wierzchołek góry lodowej możliwości jakie oferuje nam nadpisywanie styli modułów - dzięki nadpisywaniu możemy uzyskać naprawdę mnóstwo efektów - zwijane bloki modułów, zaawansowane stylowanie nagłówków modułów czy odpowiednie filtrowanie zawartości modułu zawartej w $module->content.

W wypadku bardziej wyszukanych potrzeb warto zastanowić się nad wykorzystaniem dodatkowej klasy i szablonów w celu zwiększenia czytelności kodu. Taką klasę po prostu dołączamy do pliku modules.php

Warto przejrzeć kody źródłowe różnych szablonów by zobaczyć jak ich autorzy wykorzystali możliwość definiowania własnych styli modułów - może to być źródłem wielu ciekawych rozwiązań, które często nie tylko poszerzają możliwości szablonu, ale po prostu ułatwiają jego tworzenie i późniejsze utrzymanie.

 
 

14

lutego

2010

Moduły bez użycia pozycji modułów

Joomla! dzięki swojej elastyce nie ogranicza nas do korzystania wyłącznie z pozycji modułów dostępnych w szablonie - czasem pojawia się potrzeba umieszczenia modułu bezpośrednio w kodzie szablonu.

Zaplecze teoretyczne

Zanim przystąpimy do praktycznej części, przedstawię odrobinę teorii wymaganej do zrozumienia kodu, który stworzymy.

Joomla! pozwala tworzyć nam różne konfiguracje dla zainstalowanych modułów - przykładowo dzięki temu możemy utworzyć kilka menu dla naszej strony na bazie standardowego modułu mod_mainmenu. Każda taka instancja danego modułu posiada swoją nazwę - tytuł. Standardowo daną instancję modułu przypisujemy do jednej z pozycji i wybranych podstron na których dany moduł ma się wyświetlać. Możemy też określić poziom dostępności modułu dla użytkowników witryny.

W wypadku gdy tworzymy szablony i umieszczamy w nim pozycje modułów, możemy określić stylowanie modułów na danej pozycji - np. xhtml, który tworzy dodatkowe kontenery wokół standardowej zawartości modułu.

W naszych działaniach skupimy się na klasie JModuleHelper dostępnej we frameworku Joomla! - oferuje ona kilka metod, które będą nam potrzebne do osiągnięcia zamierzonego efektu - getModule oraz renderModule. Pierwsza z nich służy do pobrania obiektu modułu na bazie nazwy i (opcjonalnie) tytułu, natomiast druga służy do wygenerowania kodu modułu na bazie dodatkowych parametrów.

Zaletą takiego rozwiązania jest to, że tak osadzony moduł pojawi się na naszej stronie zawsze (na każdej podstronie).

Wariant 1

W tym wariancie stworzymy kod, który wygeneruje nam kod modułu bez odwoływania się do bazy danych. Wadą tego rozwiązania jest to, że aby zmienić jakiekolwiek ustawienia modułu musimy zmieniać je w pliku - wariant drugi ten problem zniweluje.

Sam kod wczytujący wybrany moduł jak widać nie jest zbyt długi:

<?php

    $module = JModuleHelper::getModule( 'mod_mainmenu' ); // 1
    $module->params = 'menuname=mainmenu'; // 2
    $options = array( 'style' => 'none' ); // 3
    echo JModuleHelper::renderModule( $module, $options ); // 4

?>

W pierwszej linii wczytujemy obiekt standardowego modułu mod_mainmenu z wykorzystaniem klasy JModuleHelper. Warto tu wspomnieć, że gdyby klasa ta była z jakiegoś powodu niedostępna to należy na początku naszego kodu dodać linijkę:

jimport( 'joomla.application.module.helper' );

która wczyta nam klasę JModuleHelper.

W drugiej linijce określamy parametry naszego modułu - jest to ciąg par NAZWA_PARAMETRU=WARTOŚĆ_PARAMETRU rozdzielonych znakiem nowej linii. Gdybyśmy chcieli ustawić kilka parametrów wystarczy napisać:

parametr1=wartosc1\nparametr2=wartosc2\nparametr3=wartosc3

Wszystkie nieuwzględnione parametry przyjmą wartości domyślne.

Oczywiście nazwy parametrów i format wartości musimy wydedukować na bazie opcji modułu w panelu administracyjnym (nazwy pól z opcjami to nazwy parametrów przechowywanych w bazie danych) lub z pliku instalacyjnego XML danego modułu.

Jeżeli mamy w planach przeprowadzać bardziej wyszukane operacje na parametrach - warto zapoznać się z klasą JParameter - w jednym z kolejnych wpisów poświęcę jej więcej uwagi.

Trzecia linijka to tablica asocjacyjna zawierająca dodatkowe opcje wykorzystywane podczas generowania kodu modułu. Każdej pozycji modułów można przypisać oddzielny sposób generowania kodu modułów - możemy przykładowo dodać dodatkowe kontenery wokół treści naszego modułu. W naszym wypadku atrybut style powoduje, że kod modułu zostanie wygenerowany bez żadnej dodatkowej "otoczki". Możemy tam także ustawić jeden ze standardowych styli chrome - ich lista dostępna jest tutaj.

Jeżeli w pliku modules.php w katalogu html szablonu zdefiniujemy własne style chrome, wtedy możemy dodać dodatkowe parametry wykorzystywane przez te style - ale o tym w jednym z kolejnych wpisów, bo sam temat jest dość długi. Wracając do naszego kodu - pora na ostatnią linijkę - wywołujemy w niej statyczną metodę klasy JModuleHelper - renderModule, jako argumenty przyjmującą obiekt modułu oraz jego dodatkowe atrybuty dotyczące sposobu generowania kodu. Metoda ta zwraca wygenerowany kod modułu, który dzięki funkcji echo wyświetlamy na naszej stronie.

Wariant 2

W tym wypadku napiszemy kod, który pozwoli nam wygenerować kod modułu, którym możemy zarządzać z poziomu panelu administracyjnego - ów moduł będzie po prostu nieopublikowany na stronie (poza miejscem gdzie wstawimy nasz kod).

<?php

    $db =& JFactory::getDBO(); // 1
    $db->setQuery('SELECT title, module, params FROM #__modules WHERE id = 1'); // 2
    $item = $db->loadRow(); // 3
    $module = JModuleHelper::getModule( $item[1], $item[0] ); // 4
    $module->params = $item[2]; // 5
    $options = array( 'style' => 'none' ); // 6
    echo JModuleHelper::renderModule( $module, $options ); // 7

?>

Dwie ważne kwestie odnośnie powyższego kodu:

  1. Celem skrócenia kodu przyjmujemy, że rekord o danym ID istnieje w bazie danych - jeżeli nie mamy takiej pewności to aby uniknąć problemów warto sprawdzić po wykonaniu zapytania ile rekordów zwróciło zapytanie
  2. W ramach zgodności z PHP 4 w pierwszej linijce mamy zastosowane przypisywanie przez referencję. Jeżeli działamy w środowisku PHP 5 możemy (a nawet wypada) zmienić tą linijkę na:

    $db = JFactory::getDBO();
    

    ponieważ w PHP 5 przypisywanie obiektów odpowiada przypisywaniu obiektów przez referencję znanemu z PHP 4. W niektórych wypadkach ta zmiana będzie wręcz konieczna, jeżeli mamy ustawiony odpowiednio wysoki poziom ostrzeżeń w pliku php.ini i chcemy uniknąć komunikatów o używaniu przestarzałych operatorów.

Przejdźmy teraz do analizy kodu - na początku w pierwszej linii tworzymy referencję do obiektu klasy JDatabase (a dokładniej do jednej z klas dziedziczących po tej klasie - najczęściej JDatabaseMysql).

Następnie w drugiej linii definiujemy kod zapytania do wykonania - w naszym wypadku pobierzemy zawartość kolumn title, module i params dla rekordu posiadającego id równe 1 w tabeli jos_modules (Prefiks "#__" jest automatycznie zamieniany na domyślnie ustawiony prefiks tabeli w bazie danych podany podczas instalacji).

W kolejnej linii (3) wczytujemy z bazy danych pierwszy rezultat naszego zapytania (tak jak wspominałem tu może wystąpić potrzeba pewnej walidacji ilości zwróconych rekordów pominięta ze względu na uproszczenie skryptu).

W kolejnej linii wczytujemy obiekt naszego modułu - podobnie jak w poprzednim wariancie, tym razem jednak odwołujemy się do tablicy $item, która przechowuje zawartość kolumn wczytanego rekordu z bazy danych. Przy okazji ustawiamy też opcjonalny parametr $title metody getModule, związany z tytułem modułu.

W piątej linii ustawiamy parametry modułu na bazie wczytanych parametrów z bazy danych (tu mamy spore pole do popisu - więcej w "Inne ciekawe możliwości").

I na koniec dwie ostatnie linijki (6 i 7) - mamy tutaj to samo co mieliśmy w wariancie pierwszym więc nie ma sensu powielać opisu.

Teraz wystarczy stworzyć potrzebny nam moduł, skonfigurować go i nie publikować na naszej stronie (no chyba, że chcemy wyświetlić ten moduł dwa razy).

Inne ciekawe możliwości

W tym miejscu chciałbym zwrócić uwagę na dodatkowe możliwości, które mogą być wskazówką dla kolejnych ciekawych rozwiązań, ale wymagają już samodzielnego zgłębienia niektórych tematów.

  1. Warto przede wszystkim zauważyć, że powyższe przykłady kodu dają nam ciekawą możliwość generowania kodu modułów w innym module - różnego rodzaju moduły z zakładkami (tabami) wykorzystują tą możliwość do grupowania modułów.
  2. Możliwość określenia parametrów modułu na poziomie kodu jest niezwykle przydatna z tego względu, że możemy modyfikować pewne wybrane parametry, a co za tym idzie możemy przykładowo zapewnić możliwość dostosowania opcji modułu dla konkretnego użytkownika - możemy na przykład zdecydować na podstawie plików cookies jaka kategoria artykułów zostanie użyta do wygenerowania treści zawartej w module.
  3. Nie musimy wygenerowanego kodu modułu od razu wypisywać - możemy przypisać go do zmiennej i przed wyświetleniem zmodyfikować w żądany sposób np. z użyciem wyrażeń regularnych.
  4. Warto poznać możliwości klasy JParameter, która jest używana do analizy i modyfikowania parametrów wszelkiego rodzaju rozszerzeń dla Joomla!.
  5. Kod generujący zawartość modułu możemy oczywiście otoczyć dodatkowym kodem w PHP, który ograniczy wyświetlanie modułu - oczywiście sam kod z ograniczeniami zależy już od indywidualnych potrzeb - może to być ograniczenie do wyświetlania na stronach z określonym Itemid w adresie lub wyświetlanie modułu tylko dla określonych użytkowników naszej witryny. Możliwości są naprawdę duże i wykraczają znacznie poza to co standardowo oferuje Joomla!
  6. W wypadku wariantu drugiego możemy dość szybko stworzyć sobie kod kopiujący daną instancję modułu tak by zmieniać tylko wybrane ustawienia (swoisty odpowiednik opcji "kopiuj" w menadżerze modułów panelu administracyjnego) - wystarczy tylko trochę zabawy z klasą JParameter.
  7. Sposoby selekcji modułów w wariancie drugim nie muszą się oczywiście ograniczać do selekcji po ID - analiza budowy tabeli jos_modules (zakładając zastosowanie standardowego prefiksu tabeli podczas instalacji) nasunie na pewno kilka innych ciekawych możliwości selekcji modułów na potrzeby tego wariantu kodu.
 
 

Miniblog