<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><title>prawie jak programista</title><link>http://weblog.herok.pl/</link><description>Wpisy z dziennika internetowego Jogger, wspomaganego przez Jabbera</description><lastBuildDate>Sun, 20 May 2012 02:09:21 +0200</lastBuildDate><generator>JoggerPL</generator><item><title>Google's April fool's Day!</title><link>http://weblog.herok.pl/2012/04/01/google-s-april-fool-s-day/</link><description>Multitasking chrome
Mapy 8-bit
Zamów filmy z Youtube na DVD lub VHS!
W analytics możemy także odtworzyć dźwiękowo wykres odwiedzin...
Coś pominąłem?
Edit:
Google Cars at NASCAR
</description><pubDate>Sun, 01 Apr 2012 13:33:47 +0200</pubDate><guid>http://weblog.herok.pl/2012/04/01/google-s-april-fool-s-day/</guid><category>Ogólne</category><category>google</category></item><item><title>Drupal 6: imagecache nie generuje grafik</title><link>http://weblog.herok.pl/2011/04/13/drupal-6-imagecache-nie-generuje-grafik/</link><description>Spisuję, bo trzeci raz natrafiam na problem i trzeci raz zapominam jak go rozwiązać.
Problem wygląda następująco - po instalacji imagecache (+imageapi jako zależność) system nie potrafi wygenerować zdjęć przetworzonych przez moduł, w miejscu zdjęć otrzymujemy komunikat:
Error loading image: http://moj-adres/do/pliku.jpg
Trzy możliwe rozwiązania:

jeśli strona leży w podkatalogu (a nie bezpośrednio w głównej domenie) sprawdź plik .htaccess w tym katalogu i ustaw odpowiednio dyrektywę RewriteBase
plik .htaccess w sites/default/files powinien zawierać poniższe dyrektywy: SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
Options None
Options +FollowSymLinks Jeśli rożni się - usuń go, i wejdź w Zarządzanie &gt; Konfiguracja witryny &gt; System plików (admin/settings/file-system), a drupal utworzy nowy, poprawny plik.
Last, but not least.. Upewnij się czy włączyłeś którykolwiek moduł imageapi, GD2 lub Imagemagick

Teraz przynajmniej będę wiedział gdzie szukać rozwiazania ;)
</description><pubDate>Wed, 13 Apr 2011 21:45:41 +0200</pubDate><guid>http://weblog.herok.pl/2011/04/13/drupal-6-imagecache-nie-generuje-grafik/</guid><category>drupal</category><category>open-source</category><category>php</category><category>web</category><category>drupal 6</category><category>drupal</category></item><item><title>Szybki troubleshooting django-haystack</title><link>http://weblog.herok.pl/2011/04/07/szybki-troubleshooting-django-haystack/</link><description>Szybka porada, która może okazać się pomocna, jeśli w starciu z django-haystack otrzymujesz błędy typu:
cannot import name
gdzie name jest nazwą jakiegoś modułu.
Problemem może być zapętlony import - przynajmniej taką przyczynę zdają się sugerować w dokumentacji. Możesz spróbować ustawić
HAYSTACK_ENABLE_REGISTRATIONS=False
w settings.py i ręcznie ustawiać SearchIndex dla każdej aplikacji, z której korzysta wyszukiwarka.
Ja z natury bywam leniwy i wolałem spróbować teraz naprawić zamiast później pisać więcej kodu przy następnych modułach, zatem przez 2 dni grzebałem linijka po linijce, szukając które to polecenie nie chce przepuścić autodiscover haystacka. No i dogrzebałem się - zamiast używać używać django.utils.translation.ugettext_lazy, miałem w pliku models.py zaimportowany zwykły ugettext. I tyle. Po zmianie na ugettext_lazy w models.py i innych importowanych przez moduł models.py wszystko zaczęło śmigać.
A dlaczego zwykły ugettext się wywala ? Na to już jestem zbyt leniwy ;)
</description><pubDate>Thu, 07 Apr 2011 20:31:30 +0200</pubDate><guid>http://weblog.herok.pl/2011/04/07/szybki-troubleshooting-django-haystack/</guid><category>Ogólne</category><category>Techblog</category><category>django</category><category>haystack</category><category>debugging</category></item><item><title>PUP Lipno - brawa po raz drugi</title><link>http://weblog.herok.pl/2011/03/22/pup-lipno-brawa-po-raz-drugi/</link><description>Od listopada dziewczyna załatwia sprawy związane ze stażem. Staż chciała w bardzo interesującej dla niej placówce na Śląsku, a jako, że przynależy do Urzędu Pracy w Lipnie (kuj-pom), toteż od początku zadawała pytania jakie są szanse na uzyskanie stażu na drugim końcu Polski i zawsze informowano, że położenie geograficzne nie ma znaczenia, dyrektor rozpatrzy każde podanie osobno.
Panie z placówki, gdzie chciała odbyć staż, były na tyle uprzejme, że same starały się załatwić wszystkie sprawy z tym związane - kontaktowały się również z rzeczonym urzędem, gdzie nadal zapewniano, że nie ma przeszkód, aby odbyć staż poza swoim województwem.
Dziewczyna pobrała zatem wniosek ze strony urzędu, zaniosła do placówki, tam skompletowano wymagane dokumenty i wtedy telefonicznie dowiedziano się, że wnioski wprawdzie są do pobrania, ale problemem jest standardowy brak środków na staże i należy czekać do nowego roku, aż pojawią się nowe środki.
OK, czekamy cierpliwie. W międzyczasie jeszcze przypominanie się w urzędzie, że to ta od stażu na Śląsku i kiedy będą wnioski. Padła w końcu informacja, że środki razem z wnioskami miały być dostępne od marca, zatem uzgodniła z placówką, że osobiście dostarczy wnioski obu stronom kiedy tylko się pojawią do pobrania. Jak to wyglądało z dostępnością wniosków pisałem już poprzednio (swoją drogą do tej pory informacja na stronie się nie zmieniła), ściągnęliśmy je ostatecznie listem priorytetowym, aby szybko zebrać komplet dokumentów (aż dziw bierze, że nie było potrzeby odnawiać tych dokumentów!) i oddać osobiście do urzędu.
Tam dowiedziała się jakaż to już sterta wniosków wpłynęła (po tygodniu od ich rzekomego &quot;pojawienia się&quot;) i że owszem wniosek zostanie rozpatrzony przez samego dyrektora. Z panem dyrektorem nie miała szansy się zobaczyć &quot;bo nie ma go i nie wiadomo kiedy będzie&quot;, ale przyjmuje petentów w wybrane dni.
Zatem dziewczyna przygotowana jak do rozmowy kwalifikacyjnej pojechała tego dnia przekonać go jaka to dla niej szansa i ile ten staż pozwoli jej zdobyć ciekawego doświadczenia w poważanej placówce itd., itp.
Co na to pan dyrektor? Tak, poinformował, że wniosek został od razu odrzucony bo pochodzi spoza województwa.
Pięć miesięcy oczekiwania na łaskawą decyzję w dupie, sam nie wiem czy przez widzimisię decydenta czy przez niewiedzę/ignorancję tamtejszego personelu.
P.S. Ambitna, zafascynowana kulturą Śląska dziewczyna, z wykształceniem historycznym poszukuje pracy w okolicach Katowic lub Rybnika.
</description><pubDate>Tue, 22 Mar 2011 13:15:18 +0100</pubDate><guid>http://weblog.herok.pl/2011/03/22/pup-lipno-brawa-po-raz-drugi/</guid><category>Ogólne</category><category>pup</category><category>urząd pracy</category><category>porażka</category></item><item><title>Brawa dla PUP Lipno!</title><link>http://weblog.herok.pl/2011/03/10/brawa-dla-pup-lipno/</link><description>Kolejny przykład podejścia tyłem do klienta w jednej z wielu prężnie działających placówek publicznych - Urząd pracy w Lipnie do niedawna informował, że wnioski o staż na rok 2011 będą dostępne do pobrania w okolicach 7-go marca na ich stronie.
Jako, że wnioski do dziś nie pojawiły się tam, gdzie miały, telefonicznie można się już dowiedzieć, że Dyrektor nie wyraził zgody na udostępnienie ich tamże.
Zapewne także brak jego zgody nie pozwolił na umieszczenie stosownej informacji na tej stronie, gdzie nadal wisi informacja, że wnioski pojawią się jak będą fundusze. No bo po co ? Jeszcze ktoś byłby skłonny dupę zawracać jakimiś tam stażami!
Teraz chociaż mają szansę, że co najmniej jedna osoba daruje sobie zawracanie im dupy, skoro musi wracać 400km do swojego ulubionego urzędu pracy tylko po to, aby odebrać osobiście wymagane wnioski i znowu gnać 400km w drugą stronę, aby złożyć je w placówce, gdzie stara się o staż. Żeby było szybciej i pewniej oczywiście.
Wpis przepełniony osobistym wkurwieniem. Bo mogę, a co!
</description><pubDate>Thu, 10 Mar 2011 12:07:23 +0100</pubDate><guid>http://weblog.herok.pl/2011/03/10/brawa-dla-pup-lipno/</guid><category>Ogólne</category><category>pup</category><category>urząd pracy</category><category>porażka</category></item><item><title>Django i uniwersalne linki do edycji obiektu</title><link>http://weblog.herok.pl/2011/02/21/django-i-uniwersalne-linki-do-edycji-obiektu/</link><description>Napisałem sobie tag wyświetlający mały boks wyświetlany na każdej podstronie, gdzie user może edytować wyświetlany obiekt. Chodzi o coś takiego:

Tag, wywołany na stronie np. object_detail, wyświetla w wybranym miejscu linki do edycji i usunięcia wyświetlanego obiektu, utworzenia nowego obiektu tego typu lub widoku listy w Panelu Admina Django. Linki są wyświetlane zależnie od uprawnień. Jeśli jako argument podamy obiekt QuerySet, wyświetlone zostaną linki do utworzenia nowego obiektu typu zawartego w QuerySet oraz Listy aktualnych obiektów w Panelu Admina.
Składnia tagu:
{geshi }{% admin_links object %}{/geshi}Jako object podajemy instancję Modelu lub obiekt QuerySet.

Zatem, do dzieła! Najpierw plik w templatetags:
{geshi lang=python}
# admin_tags.py

from django import template
from django.core.urlresolvers import reverse
from django.db.models.query import QuerySet

register = template.Library()

@register.inclusion_tag('_admin_links.html', takes_context=True)
def admin_links(context, obj):
    
    if type(obj) == QuerySet:
        model = obj.model
        delete_url = change_url = None
    else:
        model = obj
    
    app_label = model._meta.app_label
    model_name = model._meta.module_name
    
    url_parts = (app_label,model_name)
    user = context.get('user')
    
    if not type(obj) == QuerySet:
        if user.has_perm(&quot;%s.can_change&quot;% model_name):
            change_url = reverse(&quot;admin:%s_%s_change&quot; % url_parts , args=(model.id,))
        else:
            change_url = None
            
        if user.has_perm(&quot;%s.can_delete&quot;% model_name):
            delete_url = reverse(&quot;admin:%s_%s_delete&quot; % url_parts , args=(model.id,))
        else:
            delete_url = None
    
    if user.has_perm(&quot;%s.can_add&quot;% model_name):
        add_url = reverse(&quot;admin:%s_%s_add&quot; % url_parts )
    else:
        add_url = None
    
    list_url = reverse(&quot;admin:%s_%s_changelist&quot; % url_parts )
    
    return {
        'user': user,
        'MEDIA_URL': context.get('MEDIA_URL'),
        'verbose_name': model._meta.verbose_name,
        'delete_url': delete_url,
        'change_url': change_url,
        'add_url': add_url,
        'list_url': list_url,
    }

{/geshi}Poniższy kod wrzucamy do pliku templates/_admin_links.html:
{geshi lang=html}{% if user.is_staff %}
	
		{{ verbose_name }}:
		
			{% if change_url %} edytuj{% endif %}
			{% if delete_url %} usuń{% endif %}
			{% if add_url %} dodaj{% endif %}
			{% if list_url %} lista{% endif %}
		
	
{% endif %}
{/geshi}i dorzucamy jakiś styl. Przykład poniżej:
{geshi lang=css}#content{
	position: relative;
}
#content .admin-links{
	background: none repeat scroll 0 0 #FAFAFA;
	border: 1px solid #DDDDDD;
	display: none;
	font-size: 12px;
	font-weight: bold;
	list-style: none inside none;
	margin: 0;
	padding: 3px;
	position: absolute;
	right: 0;
	top: 0;
	-moz-box-shadow: 1px 2px 2px #777;
	-webkit-box-shadow: 1px 2px 2px #777;
	box-shadow: 1px 2px 2px #777;
	padding-left:8px;
	opacity:0.7;
	color: #666;
}
#content:hover .admin-links{
	display: block
}
#content .admin-links:hover{
	opacity:1;
}
#content:hover .admin-links ul{
	display: inline;
}
#content .admin-links li{
	display: inline;
	margin: 0;
	padding: 0;
}
#content .admin-links li a{
	border: 1px solid #fff;
	background: #fff;
	padding: 3px 5px;
}
#content .admin-links li a:hover{
	border-color: #bcd;
	background: #cde;
}
#content .admin-links li img{
	position: relative;
	top: 3px;
}
{/geshi}Uwagi mile widziane ;)
</description><pubDate>Mon, 21 Feb 2011 14:39:14 +0100</pubDate><guid>http://weblog.herok.pl/2011/02/21/django-i-uniwersalne-linki-do-edycji-obiektu/</guid><category>Ogólne</category><category>Techblog</category><category>django</category><category>admin-links</category></item><item><title>Toshiba Camileo H20 - mikro-recenzja</title><link>http://weblog.herok.pl/2010/03/02/toshiba-camileo-h20-mikro-recenzja/</link><description>Z racji, że ubzdurałem sobie ostatnio potrzebę posiadania kamery wideo, m.in. w celu rejestracji poczynań pewnego dwuletniego stworzonka pałętającego się po domu, zacząłem przeglądać oferty kamer w wszelkich marketach i na Alledrogo.
Pomyślałem też (mylnie, jak się później okazało), że skoro wkraczamy w epokę HD, to może warto zastanowić się nad najtańszą opcją takiej właśnie kamery, wszak jakość jej obrazu w najgorszym przypadku nie może być gorsza od jakości kamery SD (tak też wyczytałem na jednym z for o tematyce HD).
Z taką to myślą nabyłem ostatecznie kamerkę Toshiba Camileo H20 w pobliskim markecie za cenę niecałych 600 złotych polskich.

Pierwsze wrażenie: plastik-fantastik. Strach mocniej ścisnąć dłonią bo zgniotę coś i po gwarancji. Ale nic to, tym się kręci filmy, a nie rzuca.
Radość z zakupu minęła całkowicie po uruchomieniu kamery w nieco ciemniejszym pomieszczeniu. O ile przy dobrym świetle obraz jest dobrej jakości, o tyle w pokoju wieczorem obraz był zbyt ciemny, niewyraźny i pojawiły się paskudne pionowe granatowe paski na filmie. Ostrzenie w takich warunkach trwa dobre kilka sekund.
Kamera posiada pamięć 128MB, resztę dopełnia slot SD/SDHC. Nie miałem zalecanej karty SDHC więc użyłem SD z aparatu. Po włączeniu kamera nie widzi karty. Trzeba przy włączonej kamerze wyjąć i ponownie włożyć kartę, wtedy kamera ją zauważa. Próbowałem z trzema innymi kartmai, z każdą działa podobnie. Możliwe, ze w przypadku kart SDHC będzie inaczej, nie jestem w stanie sprawdzić.
Również suwak zoomu jest &quot;okrojony&quot; - nie ma regulacji szybkości przybliżania, przechylenie suwaka powoduje jednostajne przybliżanie/oddalanie niezależnie od siły jego przechylenia.
Po dwóch dniach, kiedy już oswoiłem się z obsługą i jakością filmów, postanowiłem odchylić ekranik o 180 stopni, wszak po to został stworzony. Obraz ładnie został odwrócony &quot;do góry nogami&quot;, lecz po przywróceniu oryginalnego położenia ekranu obraz nadal pozostał odwrócony.
Ostatecznie trzeciego dnia wróciłem z kamerą do sklepu, gdzie dowiedziałem się, że to nie pierwszy podobny przypadek z tym modelem. Z powodu braku nowego egzemplarza w magazynie otrzymałem zwrot pieniędzy. Jestem zadowolony ;)
Plusy:
- cena
- prostota obsługi
Minusy:
- problemy z kartą (może z SDHC jest inaczej, nie wiem)
- kiepska matryca (choć jak na swoją cenę, to pewnie całkiem w porządku)
- brak regulacji szybkości zoomu (po co w takim razie suwak? wystarczyłyby przyciski)
- paskudne paski w ciemnym otoczeniu.
- kiepskiej jakości zdjęcia (wiem, że nie do tego służy kamera)
Podsumowując: od najtańszej kamery na rynku w swym segmencie nie można spodziewać się cudów. Obraz przy dziennym oświetleniu jest ogólnie dobry, aczkolwiek ostrość nie jest &quot;żyleta&quot;. Możliwe, że trafił mi się jakiś pechowy egzemplarz i inne nie będą mieć problemów z kartą czy z odchylanym ekranem, lecz ja już tego nie sprawdzę. :)
Teraz zastanawiam się nad zwykłym kompaktem z względnie dobrą jakością kręconych filmów, na moje potrzeby starczy, a może nawet otrzymam lepszej jakości zdjęcia niż dotychczas :)
</description><pubDate>Tue, 02 Mar 2010 13:30:57 +0100</pubDate><guid>http://weblog.herok.pl/2010/03/02/toshiba-camileo-h20-mikro-recenzja/</guid><category>Ogólne</category><category>recenzja</category><category>kamera</category><category>toshiba</category></item><item><title>save() w django-generic-confirmation</title><link>http://weblog.herok.pl/2010/01/21/save-w-django-generic-confirmation/</link><description>Za pomocą tytułowej aplikacji można łatwo stworzyć swego rodzaju autoryzację treści - odwiedzający dodaje treść, która pojawia się dopiero po potwierdzeniu przez moderatora lub po potwierdzeniu autentyczności podanego adresu email.
Skorzystałem u siebie z tego rozwiązania, bo nie widzę sensu wyważać otwartych drzwi, tym bardziej, że aplikacja wygląda na dopracowaną, a, co równie ważne - dodanie aplikacji nie wymaga praktycznie żadnych zmian w kodzie - pomijając przypadek opisany poniżej.
Podczas dodawania aplikacji do odpowiednich formularzy napotkałem drobny problem, mianowicie potrzebowałem w formularzu nadpisać metodę save(), aby dodać nowe przetworzone dane do modelu. Problem polega na tym, że metoda save() zmodyfikowanego formularza (bo nie korzystamy z ModelForm, ale ichniejszego DeferredForm) wywoływana jest w momencie tworzenia zastępczego obiektu, a w dokumentacji nie ma wzmianki o tym, co zrobić w przypadku, kiedy chcę przetworzyć dane w momencie faktycznego dodawania obiektu do bazy.
Po kilku minutach grzebania wewnątrz kodu aplikacji znalazłem potrzebną metodę - save_original()wykonuje te same zadania, co save() w oryginalnej klasie.

Swoją drogą całkiem przyjemna ta aplikacja. Potwierdzanie formularzy może odbywać się przez wykorzystanie sygnałów wysyłanych po utworzeniu obiektu z formularza lub za pomocą odpowiedniej metody w obiekcie formularza, co zostało ładnie opisane w dokumentacji.
Druga ciekawa sprawa, o której juz wspomniałem wcześniej - rzeczywisty obiekt jest zapisywany do bazy dopiero w momencie uzyskania potwierdzenia, wcześniej przechowywany jest w zastępczym obiekcie w formie zpicklowanej (zapeklowanej?).
P.S. aby połechtać swoją próżność nieskromnie pochwalę się, że ostatnia rewizja (r16) została wydana z moją (jakże ogromną;) ) pomocą, bo, jak widać - tylko ja używam aplikacji w połączeniu ze znakami non-latin-1 ;)
</description><pubDate>Thu, 21 Jan 2010 13:00:56 +0100</pubDate><guid>http://weblog.herok.pl/2010/01/21/save-w-django-generic-confirmation/</guid><category>django</category><category>python</category><category>web</category><category>django</category></item><item><title>Recovery Partition na Lenovo w trzech aktach</title><link>http://weblog.herok.pl/2009/12/22/recovery-partition-na-lenovo-w-trzech-aktach/</link><description>Niedawno otrzymałem od znajomego laptopa Lenovo N200 z zadaniem przywrócenia Windowsa Vista po poprzedniej instalacji Ubuntu.
1. Recovery bezpośrednio z GRUB
Zadanie wygląda prosto - dodać do GRUBa odpowiednią opcję do uruchomienia systemu z ukrytej partycji Lenovo. Zadanie pozornie o tyle prostsze, że instalator Ubuntu wykrył ukrytą partycję Recovery i dodał odpowiadającą jej pozycję do menu. Zatem bez zbędnej pracy po 3 minutach miałem uruchomioną aplikację do odzyskiwania systemu*.
Problem pojawił się chwilę później - aplikacja Recovery nie radzi sobie z partycjami
innymi od NTFS/FAT32, przez co przywracanie kończone było błędem, nawet po wyborze opcji przywrócenia do stanu fabrycznego (wyczyszczenie wszystkich partycji).
Szukamy dalej...
2. Usunięcie partycji, wyczyszczenie MBR
Brzmi równie prosto - usunę najpierw partycje Linuksa i wyczyszczę MBR, wtedy instalator Recovery musi ruszyć. Partition Magic / Acronis Disk Suite / GParted / whatever i sprawa załatwiona. Tylko jak mam ustawić odpowiedni MBR bez płyty z Vistą pod ręką, a ten z XP podobno nie pomoże ?
Dalej...
3. Akcja właściwa
Grzebiąc na forach znalazłem link do pobrania dyskietki startowej zerującej MBR, zarówno w wersji pod XP, jak i Vista. Problemem jeszcze był fakt, że laptop nie posiada stacji dyskietek (w tych czasach notebook bez flopa ? kto to widział...???), ale to już nie problem - bootowalne CDRW i ręczne odpalenie polecenia z &quot;dyskietki&quot; (ręczne, bo autoexec.bat próbował coś zapisać na płytę..) załatwiło sprawę.
Vista przywrócona (co ważne - od razu bez Norton AV ;), kopia zapasowa utworzona dzięki Clonezilla, gra i buczy :).
*Co mnie zaskoczyło w aplikacji Recovery - rozwiązanie to oferuje pozornie spore możliwości: dostęp do BIOSu, tworzenie własnych kopii, możliwość pominięcia folderów z ważnymi dokumentami w czasie przywracania, wybór sposobu odzysku (ustawienia fabryczne, wyczyszczenie tylko C:), a w szczególności możliwość odznaczenia domyślnie zainstalowanych aplikacji (Norton AV, etc...). Co się jednak później okazało - odzyskiwanie jest okrojone w ten sposób, że nie kopiuje całej partycji (jak Norton Ghost czy clonezilla), ale pozwala &quot;zabezpieczyć&quot; wybrane foldery, choć i tak nie mogłem wybrać miejsca zapisu kopii. Opcja dostępu do BIOSu po kliknięciu wyświetla komunikat, że BIOS nie jest tu dostępny, trzeba uruchomić ponownie komputer ;)
</description><pubDate>Tue, 22 Dec 2009 19:44:44 +0100</pubDate><guid>http://weblog.herok.pl/2009/12/22/recovery-partition-na-lenovo-w-trzech-aktach/</guid><category>Ogólne</category><category>lenovo</category><category>windows</category><category>vista</category><category>mbr</category></item><item><title>Moduł Rules dla Drupala</title><link>http://weblog.herok.pl/2009/11/06/modul-rules-dla-drupala/</link><description>Kolejny znakomity musthave dla Drupala - moduł Rules!
Potrzebujesz skomplikowanych zasad podczas utworzenia nowej treści, wyświetlania powiadomień dla użytkownika po wystąpieniu zdarzenia, lub chociażby potwierdzenia adresu email w momencie dodania go do pola CCK ?
Ten moduł rozwiąże ten i większość innych problemów związanych z uwarunkowaniem akcji od działania usera i nie tylko jego.
Ja szukałem właśnie możliwości potwierdzenia adresu email podczas tworzenia zawartości przez niezalogowanego użytkownika. W ciągu ok. godziny od poznania modułu miałem gotową akcję, wszystko działa idealnie :).
</description><pubDate>Fri, 06 Nov 2009 13:57:19 +0100</pubDate><guid>http://weblog.herok.pl/2009/11/06/modul-rules-dla-drupala/</guid><category>drupal</category><category>open-source</category><category>web</category><category>drupal</category><category>rules.module</category><category>rules</category></item><item><title>Drupal: boksy na stronie głównej</title><link>http://weblog.herok.pl/2009/09/28/drupal-boksy-na-stronie-glownej/</link><description>W miarę prosty sposób na boksy z linkami, jakie czasem widzi się na stronie głównej W okrojonej wersji wygląda to mniej-więcej tak:

Jak widać, box składa się z krótkiego hasła i jego rozwinięcia, myślałem więc o stworzeniu zwykłych drupalowych boksów na stronie, lecz trochę to nieintuicyjne - trzeba ręcznie edytować adresy linków oraz pamiętać o składni HTML.
Ostatecznie do stworzenia takich boksów udało się jednak wykorzystać menu, które to oprócz tekstu odnośnika posiada możliwość wprowadzenia jego opisu, zatem otrzymujemy 2 pola tekstowe, wystarczy je tylko wyświetlić na stronie.
Menu primary-links może się przydać do innych rzeczy, zatem użyjemy secondary-links. W ustawieniach menu secondary-links dodajemy kilka pozycji uzupełniając pola Tekst odnośnika oraz Opis:

W pliku template.php w katalogu z szablonem dodajemy nową funkcję odpowiedzialną za generowanie struktury HTML boksów. Funkcja pobiera utworzoną przed chwilą listę elementów menu i formatuje ją pobierając zarówno treść odnośnika jak i opis:
{geshi lang=php}
function boxy_links() {
	$links = menu_secondary_links();
	$c = 1;
	$output = &quot;\n&quot;;
	if ($links) {
		foreach ($links as $link) {
			// zawartosc boksu
			$content = '';
			$content .= '' . $link['title'] . '';
			$content .= '' . $link['attributes']['title'] . '';
			$content .= '';
			// owiniecie LI z klasami
			$class = 'boxy-item-'.$c ;
			if($c==1) $class .= ' first';
			if($c==count($links)) $class .= ' last';
			$output .= '' . $content. ''; 
			$c++;
		};
		$output .= '';
	}
	return $output ;
}
{/geshi}Generowanie kodu mamy gotowe, teraz trochę styli - do pliku ze stylem CSS skórki doklejamy na końcu:
{geshi lang=css}
ul.boxy-links{
	list-style: none;
}
ul.boxy-links li{
	background:#EEEEEE none repeat scroll 0 0;
	display:inline-block;
	margin:0 10px 0 0;
	padding:5px;
	width: 200px;
}
ul.boxy-links li.last{
	margin-right:0;
}
ul.boxy-links li h4{
	border-bottom:2px solid #6688AA;
	margin: 0;
	padding:3px 5px;
}
ul.boxy-links li h5{
	margin: 0;
	padding:3px 5px;
}
{/geshi}Jeszcze tylko wstawić to na stronę. W szablonie strony głównej (u mnie jest to page-front.tpl.php) w wybranym miejscu wywołujemy naszą funkcję:
{geshi lang=php}

	

{/geshi}Gotowe. Wystarczy dostosować wygląd w CSS i boksy nadają się do uzytku ;)
</description><pubDate>Mon, 28 Sep 2009 11:00:54 +0200</pubDate><guid>http://weblog.herok.pl/2009/09/28/drupal-boksy-na-stronie-glownej/</guid><category>drupal</category><category>php</category><category>Techblog</category><category>web</category><category>drupal</category><category>menu</category><category>linki</category><category>frontpage</category></item><item><title>Menedżer haseł</title><link>http://weblog.herok.pl/2009/09/06/menedzer-hasel/</link><description>Za sprawą ostatniej afery z Wykopem wszyscy teraz grzmią na temat bezpiecznego przechowywania i tworzenia haseł. Pomyślałem, że warto podzielić się moim rozwiazaniem.
Ciekawe pomysły dotyczące zapamiętywania lub tworzenia haseł podali już m.in. Piotr Konieczny, kutek, czy zal; ja używam kombinacji kilku haseł, zależnie od stopnia &quot;ważności&quot; serwisu, lecz do samego zapamiętania wspomagam się menedżerem haseł. Rozwiązanie o tyle bezpieczne, że bazę haseł trzymam tylko na swoim komputerze i na prywatnym pendrive.
Podczas wyboru menedżera przetestowałem kilka rozwiązań, już nawet nie pamiętam nazw innych programów, ostatecznie jednak pozostałem przy najwygodniejszym dla mnie - KeePass. Aplikację wydano na większość popularnych systemów operacyjnych, mogę zatem korzystać z niej w domu na Linuksie, oraz w pracy, mając do dyspozycji system Windows, wystarczy, że mam pod ręką plik z bazą haseł.

Okno edycji hasła, źródło: http://keepass.info/
Programik posiada obsługę wtyczek, możliwość importu i eksportu haseł, grupowania haseł według kategorii, dodawania własnych notatek do każdej pozycji, a także tzw. AutoType - możliwość uzupełniania hasła bezpośrednio do pól w przeglądarce jednym kliknięciem. Jest też dostępny w formie Portable - można go trzymać np. na pendrive oraz ma wbudowany generator haseł. To tylko wybrane ficzery, więcej można przeczytać na stronie domowej.
Sama baza z hasłami szyfrowana jest 256bitowym algorytmem AES/Rijndael lub Twofish, chroniona może być za pomocą hasła, pliku-klucza lub połączenia obu rozwiązań.
Moim zdaniem jest to najwygodniejszy sposób na gromadzenie wrażliwych danych, a na pewno jest bezpieczniejszy od zapamiętywania haseł w przeglądarce (nie mówię, że z tego nie korzystam:), czy zapisywania haseł w notatniku (też się zdarzało :)...
</description><pubDate>Sun, 06 Sep 2009 14:35:40 +0200</pubDate><guid>http://weblog.herok.pl/2009/09/06/menedzer-hasel/</guid><category>Ogólne</category><category>web</category><category>wykop</category><category>bezpieczeństwo</category><category>hasła</category><category>password manager</category></item><item><title>Microsoft się otwiera na świat?</title><link>http://weblog.herok.pl/2009/08/06/microsoft-sie-otwiera-na-swiat/</link><description>W GReaderze przeczytałem, że do map Binga dodano ogromną ilość nowych zdjęć satelitarnych. W linkowanym artykule był odnośnik do strony wymagającej Microsoftowego &quot;pogromcę&quot; technologii Flash - Silverlight.
Widząc błękitny banerek z zachętą &quot;Install Microsoft Silverlight&quot; postanowiłem przekonać się jak się zachowa strona instalacyjna tegoż dzieła na moim Archu. Tak dla zasady, aby udowodnić jaki to linuksowy fanatyk ze mnie :-).
Tak jak się spodziewałem - na stronie Silverlight przywitał mnie radosny komunikat:
Microsoft Silverlight may not be supported on your computer's hardware or operating system.
Radość z przewidzianego problemu była niezmierna... Ale, ale! po kilku sekundach, kiedy już precyzyjnie przeskanowano architekturę mojego systemu operacyjnego pojawił się drugi komunikat z linkiem!
Experience this in Silverlight
Install the free Plug-in
Klikam, oczom nie wierzę! Strona została przekierowana do Moonlight - otwartej implementacji Silverlighta!
Microsoft wkońcu zaczyna zauważać alternatywny stworzone przez społeczeństwo... Świat się kończy...
</description><pubDate>Thu, 06 Aug 2009 18:12:58 +0200</pubDate><guid>http://weblog.herok.pl/2009/08/06/microsoft-sie-otwiera-na-swiat/</guid><category>Ogólne</category><category>open-source</category><category>Microsoft</category><category>sliverlight</category><category>moonlight</category><category>opensource</category></item><item><title>Nie byłem u ów kolegi, bynajmniej wczoraj</title><link>http://weblog.herok.pl/2009/07/09/nie-bylem-u-ow-kolegi-bynajmniej-wczoraj/</link><description>Już dawno chciałem napisać o tym, aż wkońcu motywacją stał się ten wpis.
W sieci coraz więcej błędów językowych, dyslektyków, dysgrafów, czy jeszcze innych dysignorantów. Nasz język jaki jest, każdy widzi, trzeba się z tym pogodzić. Nie jestem żadnym fanatycznym purystą językowym (co więcej, daję głowę szefa, że w tym wpisie także znajdą się błędy ;) ), ale szlag mnie trafia kiedy ktoś używa zwrotów, których albo nie potrafi napisać poprawnie, albo nie rozumie.

Poszedłem do ów kolegi
Ów - zaimek jak każdy inny. Dlaczego zatem niektórzy nie są w stanie pojąć, że odmieniamy go również tak jak inne zaimki ? Dla niezorientowanych poniżej przedstawiłem przykład odmiany, przyklejając dla ułatwienia nasz zaimek do rzeczownika student.

Pisownia zaimka ów


Mianownik
ów student


Dopełniacz
owego studenta


Celownik
owemu studentowi


Biernik
owego studenta


Narzędnik
owym studentem


Miejscownik
owym studencie


Wołacz
ów studencie




Dziś będzie ładna pogoda, bynajmniej do południa
Zwrot bynajmniej bardzo często mylony jest z przynajmniej, jednak oznacza coś zupełnie innego. Jeśli ktoś ma problemy z zapamiętaniem różnicy, wystarczy przyjąć, że bynajmniej oznacza tyle samo, co wcale. I wszystko staje się prostsze!
Jeśli przypomnę sobie więcej, dopiszę później.

Na koniec porada wujka Andrzeja: Jeśli czegoś nie rozumiesz - nie używaj.
czekam na wytykanie moich błędów.. :)
</description><pubDate>Thu, 09 Jul 2009 10:06:03 +0200</pubDate><guid>http://weblog.herok.pl/2009/07/09/nie-bylem-u-ow-kolegi-bynajmniej-wczoraj/</guid><category>Ogólne</category></item><item><title>Zenphoto Hint: IO Error</title><link>http://weblog.herok.pl/2009/07/08/zenphoto-hint-ie-error/</link><description>Podczas składania prostej galerii opartej o zenphoto natknąłem się na problem z wysyłaniem zdjęć domyślnie ustawionym sposobem (samo wysłanie pliku nie wymaga przeładowania strony). Otóż po wysłaniu każdego pliku galeria zwraca &quot;wszystko mówiący&quot; komunikat IO Error.
Problem okazał się nie być związany bezpośrednio z zenphoto, lecz ze skryptem wysyłania plików, którym to jest uploadify.
Dopiero na ichniejszym forum znalazłem rozwiązanie - zabezpieczenia Apache blokują wysyłanie POST, nawet nie mam ochoty szukać o co w tym głębiej chodzi.

Zatem dodajemy do .htaccess:
{geshi lang=rc}

	SecFilterEngine Off
	SecFilterScanPOST Off

{/geshi}I wszystko zaczyna działać jak należy.

</description><pubDate>Wed, 08 Jul 2009 14:22:26 +0200</pubDate><guid>http://weblog.herok.pl/2009/07/08/zenphoto-hint-ie-error/</guid><category>open-source</category><category>web</category><category>apache</category><category>zenphoto</category></item><item><title>Wyszukiwanie najbliższej daty w Django / SQL</title><link>http://weblog.herok.pl/2009/05/26/wyszukiwanie-najblizszej-daty-w-django-sql/</link><description>Zdarzyła mi się ostatnio potrzeba wyszukania w ORM Django obiektów o czasie utworzenia najbliższemu wybranej dacie. W czeluściach internetu nie znalazłem zbyt wiele sensownego na temat szukania najbliższych wartości, nie było też żadnej automagicznej funkcji wykonującej podobne zadanie, więc zdecydowałem się napisać do tego własny kawałek kodu SQL.
Jedynym sensownym rozwiązaniem, na jakie wpadłem jest użycie DATEDIFF jako &quot;wyznacznika odległości&quot; od poszukiwanej daty.
I tak, wspomniana funkcja pozwala nam w prosty sposób obliczyć różnicę dni pomiędzy wybranymi obiektami typu DATE lub DATETIME:
{geshi lang=python}
SELECT DATEDIFF('2009-05-01','2009-04-12');
12
{/geshi}Praktyczny przykład z bazy danych:
{geshi lang=python}
SELECT companies_ownership.start_at,
    DATEDIFF(companies_ownership.start_at,'2009-04-3') as diff
    FROM companies_ownership;
+------------+------+
| start_at   | diff |
+------------+------+
| 2009-01-01 |  -92 |
| 2009-03-04 |  -30 |
| 2009-03-04 |  -30 |
| 2009-04-04 |    1 |
| 2009-04-04 |    1 |
| 2009-05-10 |   37 |
+------------+------+
6 rows in set (0.00 sec)
{/geshi}Jeśli potrzebujemy znaleźć np. najbliższy lub większy element, dodajemy odpowiedni warunek do zapytania:
{geshi lang=python}
SELECT companies_ownership.start_at,
    DATEDIFF(companies_ownership.start_at,'2009-04-3') as diff
    FROM companies_ownership
    WHERE companies_ownership.start_at &gt;= DATE('2009-04-3');
+------------+------+
| start_at   | diff |
+------------+------+
| 2009-04-04 |    1 |
| 2009-04-04 |    1 |
| 2009-05-10 |   37 |
+------------+------+
3 rows in set (0.00 sec)
{/geshi}Wystarczy teraz dodać LIMIT i mamy najbliższy lub większy element. Analogicznie postępujemy z najbliższym lub mniejszym - dodajemy warunek mniejszości i dodatkowo zmieniamy kolejność sortowania ORDER BY.
Jeśli jednak potrzeba nam elementu najbliższego bez względu na jego położenie, należy wyznaczyć wartość absolutną z wyniku DATEDIFF, a wynik najbliższy zeru będzie naszą szukaną wartością:
{geshi lang=python}
SELECT companies_ownership.start_at, id, ABS(DATEDIFF(start_at,'2009-04-3')) as diff
    FROM companies_ownership
    ORDER BY diff ASC
    LIMIT 1
+------------+------+
| start_at   | diff |
+------------+------+
| 2009-04-04 |    1 |
+------------+------+
1 row in set (0.00 sec)
{/geshi}Integracja z Django
Osobiście kod potrzebuję do filtrowania obiektów Ownership. Dodaję go zatem do Managera obiektu, korzystając z funkcji extra(), która posiada możliwość wstawiania własnych fragmentów kodu do zapytania ORM.
{geshi lang=python}
class OwnershipManager(models.Manager):
    def nearest_change(self, owner, date):
        # wstepne przefiltrowanie wynikow wg wybranego obiektu Owner
        qs = self.get_query_set().filter(owner = owner,)
        
        # wyszukiwanie najblizszej wartosci
        return qs.extra(select={'diff': &quot;SELECT ABS(DATEDIFF( start_at, '%s'))&quot;% date }).order_by('diff')[0:1] {/geshi}Wpis dla potomnych. Może się przyda :)
</description><pubDate>Tue, 26 May 2009 12:47:10 +0200</pubDate><guid>http://weblog.herok.pl/2009/05/26/wyszukiwanie-najblizszej-daty-w-django-sql/</guid><category>django</category><category>python</category><category>Techblog</category><category>web</category><category>django</category><category>SQL</category><category>najbliższa wartość daty</category></item><item><title>Jak zarobiłem parę peelenów na domenach</title><link>http://weblog.herok.pl/2009/04/17/jak-zarobilem-pare-peelenow-na-domenach/</link><description>Dzięki temu wpisowi nabrałem wkońcu motywacji do transferu domen. Domeny początkowo zarejestrowałem w az.pl skłoniony opinią i opisem w jakiejś gazecie (jak piszą w gazecie to oferta musi być opłacalna. ta...). Teraz wybrałem ovh.pl ze względu na cenę i możliwe późniejsze dobranie VPS'a do towarzystwa.
O dziwo operacja poszła bardzo sprawnie, wszystko załatwiłem w ciągu godziny, biorąc pod uwagę 15 minut szukania kodów authinfo (pod latarnią najciemniej). Czekam już tylko na potwierdzenie transferu domenki .net.
122 - 55 = 67 PLN w kieszeni rocznie za każdą domenę .pl.
60 - 25 = 35 PLN w kieszeni za domeny globalne.
Zaoszczędzone pieniążki pewnie pójdą na VPS. O ile nie znajdę lepszej oferty, to pewnie wybiorę najtańszą opcję w OVH, mam nadzieję, że wystarczy...
</description><pubDate>Fri, 17 Apr 2009 20:12:53 +0200</pubDate><guid>http://weblog.herok.pl/2009/04/17/jak-zarobilem-pare-peelenow-na-domenach/</guid><category>hosting</category><category>web</category></item><item><title>Python rocks!</title><link>http://weblog.herok.pl/2009/03/11/python-rocks/</link><description>Codziennie poznaję kawałek tego języka i codziennie lubię go coraz bardziej!
W związku z ostatnim pomysłem na rozszerzone logowanie użytkownika ( via pgina ) potrzebowaliśmy pobrać listę montowanych dysków z pliku startowego znajdującego się na Sambie w katalogu domowym każdego usera i zapisać choćby w formacie csv.
Teraz do sedna - 10 minut pracy (łącznie z grzebaniem w dokumentacji, wszak ekspertem jeszcze nie jestem :) ) i powstało parę linijek wykonujących dokładnie to co chciałem :)
{geshi lang=python}
import os

# sciezka z home'ami
PATH = &quot;/home&quot;
# szukany plik
BATFILE = &quot;startup.bat&quot;
# plik wysjciowy
DATAFILE = &quot;dane.csv&quot;

result = open(DATAFILE,'w')

for d in os.listdir(PATH):
    path = os.path.join(PATH, d,BATFILE)
    if os.path.exists(path):
        file = open(path,&quot;r&quot;)
        for l in file.readlines():
            if l.startswith('net use'):
                (k, v) = l.split()[2:4]
                result.write(&quot;%s,%s,%s\n&quot; % (d, k, v))
        file.close()
result.close()
{/geshi}... i w pliku wyjściowym lądują wpisy w postaci
{geshi}username,literka_dysku,sciezka{/geshi}Może nie jest to zoptymalizowane rozwiązanie (ciągle się uczę więc mogłem pominąć coś ważnego), ale najważniejsze, że spełnia swoje zadanie.
</description><pubDate>Wed, 11 Mar 2009 08:54:26 +0100</pubDate><guid>http://weblog.herok.pl/2009/03/11/python-rocks/</guid><category>python</category></item><item><title>Django - historia wersji modelu</title><link>http://weblog.herok.pl/2009/02/26/django-i-historia-wersji-modelu/</link><description>Przy obecnym (a zarazem pierwszym pisanym w django) projekcie dla klienta otrzymałem wytyczne, że wszelkie zmiany w określonych modelach powinny być archiwizowane w historii, aby później łatwo można było ustalić kto popełnił ewentualny błąd. Parę dni spędziłem na przeszukiwaniu niezbadanych czeluści internetu aby znaleźć odpowiednie rozwiązanie - bo po co pisać coś, co już prawdopodobnie zostało napisane?
Do wyboru znalazłem m.in. :

Django History - brak chyba jakiejkolwiek dokumentacji
Django FullHistory - ostatnia aktualizacja z listopada 2008 kieruje do innego projektu (poniżej), który miał być bardziej kompletny:
Django Model History - ten z kolei umarł w grudniu 2008.

Aż wkońcu prawie przypadkiem trafiłem na projekt Django Reversion , który okazał się strzałem w dziesiątkę.

Krótki, acz wystarczający podręcznik z instrukcją instalacji znajduje się w GettingStarted. Po zainstalowaniu pakietu w systemie należy oczywiście dodać wpis 'reversion' do listy INSTALLED_APPS.
Historia w Panelu Administracyjnym
Aby korzystać z historii modeli tylko w panelu administracyjnym, wystarczy zmienić dziedziczenie wybranego modelu ze standardowego django.contrib.admin.ModelAdmin na reversion.admin.VersionAdmin, czyli w pliku admin.py wpiszemy:
{geshi lang=python}
from django.contrib import admin
from reversion.admin import VersionAdmin
from projekt.models import MojModel

#nasze nowe dziedziczenie
class MojModelAdmin(VersionAdmin):
    #reszta standardowych ustawien admina
    list_display = (...)
	
# zarejestrowanie modelu
admin.site.register(YourModel, YourModelAdmin)
{/geshi}I tyle! Zmiany dokonywane w obiektach tego modelu zostaną automatycznie zarchiwizowane, a przywrócenie poprzedniej wersji będzie możliwe po naciśnięciu przycisku Historia widocznego podczas edycji obiektu.
Historia w całym projekcie
Aby archiwizację modelu wprowadzić do zwyczajnych widoków poza panelem administracyjnym, potrzebujemy zarejestrować wybrany model w module reversion :
{geshi lang=python}
from projekt.models import MojModel
import reversion
reversion.register(MojModel)
{/geshi}Najwygodniej zrobić to w pliku models.py, aby rejestracja przebiegła od razu po starcie aplikacji.

Teraz mamy do wyboru kilka sposobów tworzenia historii modelu, wszystkie opisano w dokumentacji , ja wybrałem ten, który najbardziej mi pasuje - wybrane widoki opakowuję dekoratorem revision.create_on_success i po zapisaniu obiektu dodaję opis rewizji :
{geshi lang=python}
from reversion import revision
from projekt.models import MojModel

@revision.create_on_success
def moja_funkcja(request):
    m = MojModel()
    m.save()
    revision.user = request.user
    revision.comment = &quot;Utworzono nowy model: %s&quot; % m
{/geshi}W dokumentacji znajdziemy jeszcze parę przykładów implementacji pluginu oraz szczegółowe informacje o dostępnym funkcjach API.
Tym sposobem w ciągu kilku minut otrzymujemy aplikację z archiwizacją modeli oraz możliwością prostego przywracania ich poprzednich wersji.
</description><pubDate>Thu, 26 Feb 2009 15:14:01 +0100</pubDate><guid>http://weblog.herok.pl/2009/02/26/django-i-historia-wersji-modelu/</guid><category>django</category><category>python</category><category>Techblog</category><category>web</category><category>django</category></item><item><title>Django i domyślne wyświetlanie nazwy użytkownika w Auth</title><link>http://weblog.herok.pl/2009/01/03/django-i-domyslne-wyswietlanie-nazwy-uzytkownika-w-auth/</link><description>Jako, że od pewnego czasu zgłębiam tajniki Django (najlepszą metodą - próba przeportowania aplikacji powstałej na Symfony), napotkałem ostatnio problem z aplikacją Auth i jej sposobem wyświetlania użytkownika w polach .
Otóż domyślnie, metoda __unicode__ modułu User zwraca po prostu nazwę użytkownika (username). Pole to jest wymagane oraz unikalne dla każdego z użytkowników, więc zwrócony wynik zawsze będzie jednoznacznie kierował do wybranego usera.
Co jednak w przypadku większej liczby użytkowników? Ciężko będzie zapamiętać loginy wszystkich insteresujących nas osób. Można oczywiście wykorzystać dziedziczenie i stworzyć własną klasę nadpisując metodę, lecz to rozwiązanie zadziała tylko dla naszego własnego kodu - moduły aplikacji Auth nadal będą korzystały ze standardowych klas, więc np. wewnątrz administracji Jan Kowalski nadal pozostanie tylko uzytkownik-iem.
Rozwiązaniem problemu okazało się użycie niedocenionej wcześniej funkcji lambda (bo po co mi takie dziwadło?) - za jej pomocą można nadpisać obecną metodę i zmusić system do wyświetlania np. imienia i nazwiska, lub innych, dowolnie skomplikowanych kombinacji, np.:
{geshi lang=python}
from django.contrib.auth.models import User
User.__unicode__ = lambda u: &quot;%s, %s [%s]&quot; % (u.last_name, u.first_name, u.username)
{/geshi}Tym sposobem, zamiast

uzytkownik

otrzymujemy

Kowalski, Jan [uzytkownik]

Co dziwne, nigdzie nie mogłem znaleźć rozwiązania problemu, a w trac'u django prośby o możliwość opcjonalnej zmiany wyświetlania pozostały jako wontfix.
Mam nadzieję, że komuś się przyda, ewentualnie proszę o lepsze rozwiązanie.
</description><pubDate>Sat, 03 Jan 2009 17:59:31 +0100</pubDate><guid>http://weblog.herok.pl/2009/01/03/django-i-domyslne-wyswietlanie-nazwy-uzytkownika-w-auth/</guid><category>django</category><category>python</category><category>web</category><category>django auth __unicode__</category></item></channel></rss>
