eRIZ’s weblog

PHP, webdesign, Linux, Windows i inne, bo nie samym chlebem człowiek żyje
Serdecznie zapraszam do udziału w ANKIECIE

Tworzymy własny sidebar - “widżety” i :hover w IE

Jak zapewne zauważyliście, mój sidebar w końcu przeszedł pewną metamorfozę. Zgodnie z tym, co sobie obiecałem zmieniłem go, bo był “niedopracowany”. Teraz postanowiłem go “ułebodwazerować” i podzieliłem go na jakby widżety - są one raczej klasyczne. Dodatkowymi są: kalendarz, najnowsze wpisy, losowe wpisy. Co do samej postaci list z linkami - żeby nie straciły na zawartości i aby nie zajmowały tyle miejsca, to postanowiłem opisy pokazywać dopiero po wskazaniu myszą linka.

Teraz opiszę jak utworzyć każdy z takich widgetów u siebie.

Najpierw trochę teorii.

Wszystkie opisy, które pokazują się po wskazaniu myszą na link są oparte na pseudoklasie :hover. Wszystko by było OK, gdyby nie jeden problem: IE. Nie wiem, jak to jest w siódemce, ale we wszystkich pozostałych na użycie :hover bez jakichkolwiek hacków przeglądarka zezwala tylko dla elementu <a>. Nie ma sensu pchać osobnego skryptu tylko po to, aby pod IE się mógł opis pojawiać. Owszem, można by było wszystko wcisnąć link, ale specyfikacja XHTML nie zezwala na umieszczanie linków w linkach. Poza tym, inne obiekty niepotrzebnie by zmieniały wygląd. IE obsługuje technologię zwaną behaviours. Wykorzystał to autor whatever:hover, która pozwala na “normalne” używanie pseudoklas :hover czy :focus we wszystkich elementach. Ma tylko jedną wadę - nie możemy ich użyć w dynamicznie tworzonych obiektach (np. poprzez AJAX).

Aby uaktywnić csshover w IE:

  1. Skopiuj plik csshover.htc do jakiegoś katalogu. WXP od wersji z Service Packiem 2 wymaga również, aby serwer wysyłający plik ustawiał kodowanie na text/x-component htc. Jeśli Twój dziennik stoi na serwerze obsługiwanym przez Apache, to utwórz plik .htaccess w katalogu z csshover i wpisz:

    1. AddEncoding text/x-component htc

    Wolna linijka na końcu .htaccess jest niezbędna.

    Może się również zdarzyć, że nie ma możliwości skorzystania z .htaccess lub dziennik posiadamy na serwerze innym niż Apache. Wówczas zmieniamy nazwę csshover.htc na csshover.php i na jego początku dodajemy:

    1. <?PHP
    2. header('Content-type: text/x-component htc');
    3. ?>
  2. W sekcji <head> dopiszemy komentarz warunkowy:
    1. <!--[if IE]><style type="text/css">
    2. body {behavior: url('<?php bloginfo('template_directory'); ?>/csshover.htc');}
    3. </style>
    4. <![endif]-->

    W zależności od potrzeb zmieniamy ścieżkę do pliku albo rozszerzenie.

To tyle jeśli chodzi o obsługę przez przeglądarki, teraz przejdę do opisu wykonania poszczególnych wstawek:

Znowu trochę teorii

WordPress standardowo udostępnia nam pewien wachlarz funkcji, które możemy wykorzystać w swoim szablonie. W naszym wypadku okażą się one po prostu… niewystarczające. Tak, dobrze napisałem. Oryginalne funkcje narzucają nam jeden ustalony schemat np. list z linkami, który możemy co najwyżej oddzielić jednym tagiem. Ale co w sytuacji, gdy chcemy zrobić coś więcej? Właśnie, niezbyt wiele. Mamy jednak możliwość wykorzystania własnych skryptów PHP w szablonach. I właśnie z tego skorzystamy. ;)

Większość kodu (zapytania SQL), to skopiowane kawałki oryginalnych funkcji WordPressa.

Kalendarz

Tu nie ma się za bardzo nad czym rozwodzić. Zresztą, kod produkowany przez WordPress jest dobrze dopracowany i odpowiednio skonstruowanym CSS-em można z nim zrobić praktycznie wszystko. Co trzeba do kalendarza? Ano, tylko tyle: :P

  1. <?PHP
  2. get_calendar(2);
  3. ?>

Liczba w parametrze funkcji określa ilość znaków, która będzie wyświetlana w nagłówkach tabeli przy dniach tygodnia (np. 1 - “w”, 2 - “wt”).

Blogroll/własne linki, itp.

Tu sytuacja będzie troszkę bardziej skomplikowana, ale niekoniecznie. Po prostu będzie więcej PHP.

  1. <ul id="blogroll" class="box"><?PHP
  2. $links = $wpdb->get_results('SELECT * FROM '.$wpdb->links.' WHERE link_category=1 ORDER BY link_name');
  3. foreach($links as $l){
  4.         echo '<li><a class="title" href="'.$l->link_url.'"'.($l->rel!='' ? 'rel="'.$l->rel.'"' : '').'>'.$l->link_name.'</a><small>'.$l->link_description.'</small></li>';
  5. }
  6. ?></ul>

Spowoduje to wyświetlenie listy z linkami oraz ich opisami. Domyślnie sortowane według nazwy, ale nic nie stoi na przeszkodzie, aby filtrować wyniki na podstawie innego klucza. Ich lista jest dostępna w WordPress Codex.

OK, lista już jest, ale wygląda z lekka paskudnie. Najpierw zaopatrujemy się w ikonki, np. Silki.

Tak wygląda mniej więcej mój kawałek arkusza stylów:

  1. ul.box
  2. {list-style-type: none; padding: 0; margin: 0; border: 1px solid #DDD; width: 100%; }
  3.  
  4. ul.box small
  5. { display: none; padding: 3px 0; color: #333; border-top: 2px solid #71BDFF; margin: 2px 0; }
  6.         
  7. ul.box li
  8. { background: #F7F7F7 url('gfx/asterisk_yellow.png') no-repeat; padding: 3px; padding-left: 23px; }
  9.  
  10. ul.box li a.title
  11. { display: block; color: #555; font-weight: bold; text-decoration: none; border: none; width: 100%; }
  12.  
  13. ul.box li:hover
  14. { background-color: #CCE6FF; }
  15.  
  16. ul.box li:hover small
  17. { display: block; }
  18.  
  19. ul.box a.title:hover
  20. { text-decoration: underline; }
  21.  
  22. ul.box li:hover a.title
  23. { color: #000; }
  24.  
  25. ul.box p
  26. { margin: 0; }

Dostosuj i wklej do swojego CSS-a i tyle. :)

Dla bardziej wnikliwych: Elementem “trzymającym” opis jest tag <small> w stanie spoczynku jest on ukrywany, natomiast - przy najechaniu kursorem na element listy - zmieniana jest własność display w selektorze ul.box li:hover small. Niekoniecznie musi być to widoczność obiektu. Wszystko zależy od wyobraźni, przepis masz. ;)

Następne przykłady będą w większości wykorzystywały powyższego CSS-a i koncepcję opisów. Zmieniał będzie się tylko kod PHP.

Ostatnie wpisy

  1. <ul class="box" id="lastEntries"><?PHP
  2. $posts = $wpdb->get_results('SELECT ID, post_date, post_title, comment_count FROM '.$wpdb->posts.' WHERE post_status="publish" ORDER BY post_date DESC LIMIT 5');
  3.  
  4. foreach($posts as $l){
  5.         echo '<li><a class="title" href="'.get_permalink($l->ID).'">'.$l->post_title.'</a><small><strong>Kategorie:</strong> ';
  6.         $cats = get_the_category($l->ID);
  7.  
  8.         $cc = count($cats);
  9.         for($a=0;$a<$cc;$a++){
  10.                 $cats[$a] = '<a href="'.get_category_link($cats[$a]->cat_ID).'">'.$cats[$a]->cat_name.'</a>';
  11.         }
  12.  
  13.         echo implode(', ', $cats);
  14.  
  15.         echo '<br /><strong>Komentarzy: </strong> '.$l->comment_count.'<br /><strong>Data:</strong> ';
  16.  
  17.         echo date('Y-m-d @ H:i:s', strtotime($l->post_date));
  18.  
  19.         echo '</small></li>';
  20. }
  21. ?></ul>

Podstawowe funkcje PHP są wyróżnione, natomiast inne (pochodzące z WP) są opisane w Codeksie. Możemy również zmienić liczbę wyświetlanych wpisów zmieniając LIMIT 5 na dowolną liczbę.

Losowe wpisy

Składnia identyczna jak wyżej, ale zmienia się linijka z zapytaniem do bazy:

  1. $posts = $wpdb->get_results('SELECT ID, post_date, post_title, comment_count FROM '.$wpdb->posts.' WHERE post_status="publish" ORDER BY RAND() LIMIT 5');

Kategorie

  1. <ul class="box" id="cats"><?PHP
  2. //nie mylić z kotami ;P
  3. $cats = $wpdb->get_results('SELECT cat_ID, cat_name, category_description, category_count FROM '.$wpdb->categories.' ORDER BY cat_name');
  4.  
  5. foreach($cats as $c){
  6.         echo '<li><a class="title" href="'.get_category_link($c->cat_ID).'">'.$c->cat_name.'</a><small><strong>Opis:</strong> '.($c->category_description!='' ? $c->category_description : '(brak opisu)');
  7.        
  8.         echo '<br /><strong>Wpisów: </strong>'.$c->category_count;
  9.        
  10.         echo '</small></li>';
  11. }
  12. ?>
  13. </ul>

Archiwa

  1. <ul class="box" id="archives"><?PHP
  2. //nazwy miesięcy
  3. global $month;
  4.  
  5. $archives = $wpdb->get_results('SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM '.$wpdb->posts.' WHERE post_date < "'.current_time('mysql').'" AND post_date != "0000-00-00 00:00:00" AND post_status = "publish" GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC');
  6.  
  7. foreach($archives as $a){
  8.         echo '<li><a class="title" href="'.get_month_link($a->year, $a->month).'">'.sprintf(__('%1$s %2$d'), $month[zeroise($a->month,2)], $a->year).'</a><small><strong>Wpisów:</strong> '.$a->posts.'</small></li>';
  9. }
  10. ?>
  11. </ul>

Last.fm

Last.fm daje domyślnie listy oparte na obrazkach. Ma to swoje zalety jak i wady. Powiedziałbym, że tych drugich jest więcej:

  • Zmniejszona dostępność (syntezatory TTS nie potrafią, jeszcze, odczytać tekstu z obrazków),
  • Minimalne wręcz możliwości ustalania wyglądu własnego notowania.

Co można zrobić? Początkowo wydaje się, że nic. Błąd, da się. :) Mianowicie, po zalogowaniu do last.fm mamy link Narzędzia i Źródła danych. Właśnie one przyjdą nam z pomocą. W poniższym skrypcie wykorzystamy plik TXT dla ostatnio odgrywanych utworów. Dostępny jest on pod adresem http://ws.audioscrobbler.com/1.0/user/UŻYTKOWNIK/recenttracks.txt.

Napiszemy teraz skrypt, który będzie ów plik cache’ował. Dlaczego?

  • Na stronie możemy przeczytać, że dozwolone jest maksymalnie jedno wysłanie żądania do źródła danych na sekundę. Niby dużo, ale przy większej liczbie odwiedzin możemy mieć problemy.
  • Serwery są często przeciążone.

A więc do roboty. W katalogu szablonu tworzymy plik lastfm.cache.php z zawartością:

  1. <?PHP
  2. function cacheTracks(){
  3.         //gdzie UŻYTKOWNIK, to wstaw swój login na last.fm
  4.         $data = file_get_contents('http://ws.audioscrobbler.com/1.0/user/UŻYTKOWNIK/recenttracks.txt');
  5.        
  6.         if($data===false OR $data==''){
  7.                 return file_get_contents(TEMPLATEPATH.'/lastfm.cache');
  8.         }
  9.  
  10.         $p = file_put_contents(TEMPLATEPATH.'/lastfm.cache', $data);
  11.         //jeśli nie posiadamy PHP 5, to usuń poprzednią linijkę i odkomentuj:
  12.         #$p = fopen(TEMPLATEPATH.'/lastfm.cache', 'w');
  13.         #flock($p, LOCK_EX);
  14.         #fwrite($p, $data);
  15.         #fclose($p);
  16.        
  17.         return $data;
  18. }
  19.  
  20. if(!file_exists(TEMPLATEPATH.'/lastfm.cache')){
  21.         $data = cacheTracks();
  22. }else{
  23.         //zamiast 3600 wstaw czas w sekundach, na który ma być cache'owana lista.
  24.         if(time()-filemtime(TEMPLATEPATH.'/lastfm.cache')>3600){
  25.                 $data = cacheTracks();
  26.         }else{
  27.                 $data = file_get_contents(TEMPLATEPATH.'/lastfm.cache');
  28.         }
  29. }
  30.  
  31. if($data!=''){
  32.         $data = explode("\n", trim($data));
  33.         $tracks = array();
  34.         foreach($data as $r){
  35.                 $a = explode(',', $r);
  36.                 $track['time'] = $a[0];
  37.                 unset($a[0]);
  38.                 $track['name'] = implode(', ', $a);
  39.                 $track['name'] = explode(' '.chr(226).' ', $track['name']);
  40.                 $tracks[] = $track;
  41.         }
  42. }else{
  43.         $tracks = array();
  44. }
  45. ?>

Następnie, utwórz plik lastfm.cache i ustaw mu prawa dostępu do zapisu

Teraz w sidebarze:

  1. <ul class="box" id="lastfm">
  2. <?PHP
  3. require TEMPLATEPATH.'/lastfm.cache.php';
  4. if(count($tracks)>0){
  5.         foreach($tracks as $t){
  6.                 echo '<li><a href="http://lastfm.pl/music/'.urlencode($t['name'][0]).'/_/'.urlencode($t['name'][1]).'" class="title">'.$t['name'][1].'</a><small><strong>Wykonawca:</strong> <a href="http://lastfm.pl/music/'.urlencode($t['name'][0]).'">'.$t['name'][0].'</a><br /><strong>Odtwarzany</strong>: '.date('Y-m-d H:i:s', $t['time']).'</small></li>';
  7.         }
  8. }
  9. ?>
  10. </ul>

Del.ic.je

Teraz to samo dla Delicji.

Serwis pobieranie danych umożliwia tylko za pośrednictwem JSON/RSS. Wybrałem JSON. Postępowanie praktycznie podobne do last.fm, ale nie do końca. Po pierwsze, musimy posiadać bibliotekę, która umożliwi rozkodowanie danych JSON do postaci zmiennych PHP. Mamy dwa wyjścia: PHP5 posiada wbudowaną obsługę JSON-a, ale 4 nie. Opiszę tu wersję dla czwórki i w komentarzach sposób przejścia na PHP5.

Jeśli mamy do dyspozycji PHP5 (5.2.0) z rozszerzeniem JSON, to możemy ten punkt pominąć.

PHP4: Ściągamy źródło JSON-PHP i zapisujemy w folderze skina pod nazwą json.php. Teraz tworzymy pliki analogicznie jak w przypadku last.fm, ale o innych nazwach - delicious.cache, delicious.cache.php.

Dla delicous.cache ustaw prawa do zapisu.

delicious.cache.php:

  1. <?PHP
  2. function cacheDelicious(){
  3.         //LOGIN - Twój login, count=10 - liczba zasysanych delicji
  4.         $data = file_get_contents('http://del.icio.us/feeds/json/LOGIN?count=10&raw');
  5.  
  6.         if($data===false OR $data==''){
  7.                 return file_get_contents(TEMPLATEPATH.'/delicious.cache');
  8.         }
  9.        
  10.         if($data==''){
  11.                 return;
  12.         }
  13.        
  14.         //jeśli PHP z JSON, to wywal 2 poniższe linijki
  15.         require 'json.php';
  16.         $JSON = new Services_JSON;
  17.         //...dla 5 zmień to na json_decode...
  18.         $data = $JSON->decode($data);
  19.        
  20.         //... a te cztery na file_put_contents(TEMPLATEPATH.'/delicious.cache', serialize($data));
  21.         $p = fopen(TEMPLATEPATH.'/delicious.cache', 'w');
  22.         flock($p, LOCK_EX);
  23.         fwrite($p, serialize($data));
  24.         fclose($p);
  25.        
  26.         return $data;
  27.        
  28. }
  29.  
  30. if(!file_exists(TEMPLATEPATH.'/delicious.cache')){
  31.         $data = cacheDelicious();
  32. }else{
  33.         if(time()-filemtime(TEMPLATEPATH.'/delicious.cache')>3600){
  34.                 $data = cacheDelicious();
  35.         }else{
  36.                 $data = file_get_contents(TEMPLATEPATH.'/delicious.cache');
  37.                 $data = unserialize($data);
  38.         }
  39. }
  40. ?>

Teraz w sidebarze:

  1. <ul class="box" id="delicious"><?PHP
  2. require TEMPLATEPATH.'/delicious.cache.php';
  3. if(count($data)>0){
  4.         foreach($data as $t){
  5.                 $tags = array();
  6.                 foreach($t->t as $u){
  7.                         //zamień UŻYTKOWNIK na swój login
  8.                         $tags[] = '<a href="http://del.icio.us/UŻYTKOWNIK/'.$u.'">'.$u.'</a>';
  9.                 }
  10.                
  11.                 $tags = implode(', ', $tags);
  12.                
  13.                 echo '<li><a href="'.$t->u.'" class="title">'.$t->d.'</a><small>'.($t->n!='' ? $t->n.'<br />' : '').'<strong>Tagi: </strong>'.$tags.'</small></li>';
  14.         }
  15. }
  16. ?></ul>

Dostosowanie do własnych potrzeb

Jak zapewne zauważyłeś(aś), przy każdym <ul> oprócz nazwy klasy znajduje się również ID. Posłuży ono nam do upiększania poszczególnych list, np. do zmiany ikonek:

  1. #archives li
  2. { background-image: url('gfx/calendar_view_day.png') !important;}

Myślałem jeszcze o Delicjach, ale muszę trochę posiedzieć nad tym, bo tam do dyspozycji jest tylko JSON. Kto wie, może kiedy będę miał czas… :) - korciło, korciło i jest. :)

Naniosłem parę poprawek.

18 komentarzy

dopisz swój :: trackback :: RSS z komentarzami

RSS z komentarzami :: trackback

Skomentuj

Możesz używać znaczników XHTML. Dozwolone są następujące tagi: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Wszystkie komentarze przechodzą przez moderację oraz filtry antyspamowe. Nie zostanie opublikowany komentarz, jeśli:

  • Jego treść obraża kogokolwiek.
  • W treści znajdują się wulgaryzmy i słownictwo ogólnie uznane za nieprzyzwoite.
  • Mam wątpliwości co do autora wpisu (Wszelkie anonimy są kasowane - niezależnie od zawartości - wpisz prawdziwy e-mail. Jeśli usunąłem, Twoim zdaniem, komentarz niesłusznie - daj znać). Zdarza się, iż sprawdzam kim jest komentujący.
  • Zawiera jakąkolwiek formę reklamy.

Szufladka