18
sierpnia
2010
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:
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
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 :)