RSS
 

Narzędzia ułatwiające sprawdzanie poprawności linków

24 maj

Jedną z najważniejszych rzeczy, o które trzeba zadbać podczas tworzenia stron internetowych jest poprawność adresów url. W tym konkretnym wpisie mam na myśli to aby po kliknięciu na dany odsyłacz serwer zwracał stronę ze statusem 200, albo przynajmniej przekierowanie, które ostatecznie trafi do treści docelowej. Wszelkiego rodzaju błędy (500 czy 404) są wysoce niepożądane tak dla użytkownika strony, jak i z punktu widzenia SEO.

Zadanie to mogą ułatwić narzędzia w stylu linkcheckera lub też dodatku do firefoxa Link Evaluator.

Linkchecker to przyjemne konsolowe narzędzie umożliwiające przetestowanie linków zarówno na stronach www udostępnionych w internecie, jak i tych zapisanych na lokalnym dysku. Istnieje też proste gui do niego, które ułatwia pracę ludziom niechętnie używającym terminala. Instalacja jest prosta w przypadku Ubuntu.

sudo apt-get install linkchecker linkchecker-gui

Oprócz sprawdzania statusów zwracanych przez wszystkie odsyłacze na stronie, linkchecker ma też szereg zaawansowanych opcji w tym sprawdzanie poprawności linków pod kątem zgodności ze specyfikacją html czy css, a także przeskanowanie zwracanej przez odsyłacz treści przez program antywirusowy ClamAV.

Link Evaluator to z kolei dodatek do przeglądarki Firefox. Ma on o wiele mniejszą funkcjonalność niż wspomniane wyżej oprogramowanie nie mniej jego zaletą jest łatwość użycia, wygoda i niezależność od systemu operacyjnego. Po wejściu na daną stronę WWW wystarczy kliknąć prawy przycisk, wybrać z menu kontekstowego „Evaluate Links” i poczekać aż wszystkie url-e na danej podstronie zostaną sprawdzone. Poprane adresy podświetlane są na zielono, z kolei na czerwono zaznaczane są nieprawidłowe linki. Wybranie z menu kontekstowego opcji „Link Evaluator Control Panel” otwiera okienko z opcjami dodatku, ale też z monitorem, w którym możemy obserwować przebieg pracy programu. Znalazłem kilka dodatków do firefoxa oferujących podobną funkcjonalność ale tylko Link Evaluator działał bezbłędnie i uwzględniał przekierowania.

Testowałem też kilka narzędzi online, ale żadne nie zwracało satysfakcjonujących wyników.

 
No Comments

Posted in SEO

 

Konwersja cdr, ai, eps do svg

20 maj

Nie jestem grafikiem, a do tego pracuję na Linuksie. Nie mam tej całej palety programów graficznych jak Photoshop, Adobe Illustrator czy CorelDRAW. Zazwyczaj mi to nie przeszkadza bo jeśli nawet dostaje jakiś layout do pocięcia to zlecam tę robotę innym. Do drobnych prac graficznych starcza mi Gimp, a niekiedy nawet edytor grafiki online Pixlr.

Niekiedy jednak trafiają mi się logotypy w formacie pliku AI (Adobe Illustrator Artwork), EPS (Encapsulated PostScript – Photoshop), CDR (CorelDRAW), a nawet PDF (Portable Document Format). Gimp poradzi sobie z plikami EPS choć nie najlepiej? Lepiej w tym wypadku sprawdzi się edytor grafiki wektorowej Inkscape, który zaimportuje pliki we wszystkich wyżej wspomnianych formatach i umożliwi ich zapisanie w domyślnym formacie SVG (Scalable Vector Graphics) lub też jako PNG albo XCF (natywny format Gimpa). Export do formatu XCF – co ważne – wykonywany jest z zachowaniem warstw. Inkscape umożliwia zapis pliku w wielu jeszcze innych formatach i jest dostępny w wersjach na Linuksa, Windowsa i Mac-a.

Jeśli mamy do czynienia z większą liczbą plików do przekonwertowania to wygodniejszym rozwiązaniem okazuje się skrypt uruchamiany z linii komend. Niestety funkcjonalność Inkscape-a jest ograniczona z poziomu konsoli dlatego warto sięgnąć do innych wyspecjalizowanych w tym względzie narzędzi takich jak uniwersalny translator grafiki wektorowej – Uniconvertor lub też translator plików Post Script i PDF do innych formatów grafiki wektorowej – Pstoedit. W Ubuntu oba programy dostępne są w repozytoriach i można je zainstalować za pośrednictwem apt-get -a

sudo apt-get install python-uniconvertor pstoedit

Ich użycie jest najprostsze z możliwych i ogranicza się do podania kolejno ścieżki do pliku źródłowego i ścieżki wynikowej.

uniconvertor logo.cdr logo.svg

i analogicznie dla pstoedit

pstoedit logo.eps logo.svg
 

Kilka reflaksji na temat frameworków

27 mar

Ostatnio – po długiej przerwie – wszedłem na blog Zyxa wierząc, że znajdę tam coś ciekawego do przeczytania. Nie zawiodłem się. Znalazłem dwie recenzje najpopularniejszych w Polsce frameworków PHP tj. Symfony 1.4 okiem Zyxa i Zend Framework także okiem Zyxa.

Ostatnio przerzuciłem się na Pythona i Django, a wcześniej przez co najmniej dwa lata budowałem aplikacje w oparciu o Kohanę, jednak od czasu do czasu – w tak zwanym międzyczasie – próbowałem też coś sklecić na ZF i polubić Symfony dlatego też orientuję się przynajmniej pobieżnie w ich konstrukcji. Artykuły Zyxa i własne doświadczenie skłoniły mnie do kilku refleksji na temat ogólnej konstrukcji frameworków, sensu użycia ORM-ów a także systemów szablonów.

Architektura

Dobrze zaprojektowana struktura plików to podstawa porządnego frameworka. Każdy szanujący się projektant stara się utrzymać porządek w swoim projekcie i lubi bez zbędnego zastanawiania się wiedzieć gdzie ma czego szukać. Kohana umożliwia tworzenie modułów z których każdy może mieć swoje kontrolery, modele, widoki, pliki konfiguracyjne, a nawet biblioteki. Układ katalogów jest rozbudowany. Kaskadowość z jednej strony umożliwia elastyczność np. nadpisanie konfiguracji domyślnej, konfiguracją specyficzną dla danego modułu. (w Kohanej nie tylko konfigurację można nadpisać ale też „wymienić” klasy z zachowaniem ich dotychczasowej nazwy. Założenie to w PHP wyklucza proste użycie dziedziczenia dlatego też w Kohanie 2 było to zrobione po chamsku z użyciem evala. W wersji 3 tego frameworka mechanizm ten został bardziej elegancko zaimplementowany). Z drugiej strony przy bardziej rozbudowanych projektach człowiek zaczyna się gubić nie pamiętając niekiedy skąd się wzięła bieżąca wartość danej zmiennej konfiguracyjnej. Zaczyna się szukanie. Pół biedy, kiedy struktura katalogów jest płaska, ale jeśli musimy przeklikać się przez kilka poziomów zagłębień staje się to męczące.

Nie wiem kto na to wpadł, aby każdą klasę trzymać w osobnym pliku, nie wiem też kto wymyślił aby nazwa klasy odzwierciedlała położenie pliku w strukturze katalogów (patrz ZF, PEAR), ale doprowadziło to do powstania całej masy katalogów i podkatalogów i jeszcze większej liczby plików, z których niektóre np. zawierają jedynie jednolinijkową definicję wyjątku. W Django w poszczególnych app-sach znajdziemy z reguły pliki (init.py, views.py, models.py, urls.py, admin.py, tests.py) i to w 90% przypadków wystarcza. W Kohanie jeden moduł to kilka katalogów. Python jakoś obywa się bez autoloadera i kiedy czytam to co wyżej sam napisałem zaczynam rozumieć dlaczego. Na marginesie tylko wspomnę, że w związku z wprowadzeniem namespace-ów, sposób organizacji klas w plikach PHP się zmieni.

PHP w wersji piątej poszło wyraźnie w kierunku „magi”. Wszystkie poprzedzone podwójnym podkreśleniem metody są bardzo wygodnym rozwiązaniem i osobiście bardzo je lubię ale nadmiar czarów daje się we znaki w chwili kiedy zachodzi potrzeba prześledzenia procesów zachodzących w aplikacji. Debugowanie przesyconych „magią” klas jest znacznie utrudnione przede wszystkim przez niejednoznaczne komunikaty błędów. Dodatkową wadą użycia metod magicznych jest to, że dynamiczne settery i gettery nie będą podpowiadane przez żadne IDE typu NetBeans, czy Eclipse mimo w sumie dobrze zrealizowanej w nich funkcji podpowiadania składni.

Inną kwestią jest uniwersalność kodu. Klas Kohanej można używać tylko w ramach tego frameworka, z kolei biblioteki eZ Components lub Zend Frameworka można używać niezależnie lub w ramach zupełnie innej platformy. Sam wielokrotnie w projektach opartych na Kohanej sięgałem do wybranych komponentów Zend Frameworka. Klasy ZF są jak to zauważył Zyx dopracowane i przetestowane i jedyną ich wadą jest z reguły to, że są zbyt dobre. Zamiast w najprostszy sposób realizować banalną funkcjonalność, autorzy poszczególnych bibliotek prześcigają się w wymyślaniu wariantów zastosowań i sposobów użycia. Tak właśnie z noża powstał szwajcarski scyzoryk – fajny ale do smarowania chleba najlepszy jest zwykły nóż kuchenny. Ponoć w nowej wersji ZF ma to ulec zmianie.

ORM

Symfony zniechęciło mnie do siebie przede wszystkim ORM-em. Do Propela nawet nie startowałem, natomiast z Doctrine-m walczyłem jakiś czas. Niestety na etapie kiedy przeprowadzałem moje eksperymenty Doctrine było mocno niedopracowane w związku z czym wielokrotnie wzbudzało to moją irytacją. Z tego co pamiętam nie mogłem nawet dowolnie wskazać miejsca generowania modeli tylko było to z góry narzucone. Dlatego między innymi zarzuciłem pomysł „dokooptowania” Doctrine do Kohany.

Na tę chwilę o wiele większe doświadczenie mam z ORM-em Django uważanym za wzorcowy. Jest on o wiele bardziej dopracowany niż wyżej wspomniane PHP-owe odpowiedniki w związku z czym da się z nim w miarę sprawnie pracować. Mimo to pozostaję sceptyczny w kwestii użycia tego typu narzędzi. Opanowanie django-wego ORM-a kosztowało mnie sporo czasu, a do tego nie wyobrażam sobie aby można było efektywnie go używać nie znając wcześniej SQL-a. Przy prostych konstrukcjach jest miło i przyjemnie przy bardziej skomplikowanych użycie obiektów rzutuje przede wszystkim na wydajność, do czego przyczyniają się w dużej mierze bajery typu „lazy loading”. Zmiana ORM-owej konstrukcji w celu optymalizacji i tak poprzedzana jest napisaniem zapytania w SQL więc człowiek nie ma żadnej korzyści z użycia ORM-a, która by rekompensowała nakłady poniesione w celu jego poznania.

O wiele bardziej przemawiają do mnie składacze zapytań SQL takie jak Zend_Db – choć jak większość bibliotek Zenda jest ona przedobrzona. Mając prostą klasę do konstruowania zapytań SQL – taką która ułatwia, nie ogranicza i bynajmniej nie zmusza do uczenia się zamienników w stylu „annotate” zamiast „group by”, wspartą wzorcem projektowym DAO można stworzyć prosty w utrzymaniu, debugowaniu, elastyczny, wcale nie pracochłonny i przede wszystkim odseparowany od warstwy logiki i widoku mechanizm dostępu do bazy danych.

ORM-y mogą być w moim mniemaniu jedynie dodatkami używanymi np. do automatycznego generowania backedu (czytaj panelu administracyjnego), ale w żadnym wypadku nie powinny być jedynym ani nawet głównym sposobem pracy z danymi pobieranymi z bazy danych.

Szablony

Symfony a także Zend Framework, jak równierz Kohana używają natywnych szablonów PHP. Podobnie jak Zyx zgadzam się, że można znaleźć lepszą alternatywę dla warstwy widoku. W odróżnieniu jednak od niego nie stawiałbym na Open Power Template’a, które uważam za trudne i pracochłonne w użyciu (z samej swojej xml-owej natury), a do tego mniej elastyczne i intuicyjne niż kontestowane przez niego Smarty.

System szablonów Smarty w wersji 3 zostało wzbogacone o kilka ciekawych funkcjonalności jak np. dziedziczenie szablonów i możliwość nadpisywania bloków kodu znane z szablonów Django. W przeciwieństwie do ORM-ów uważam, że warto potrudzić się troszkę ze składnią Smarty i pogodzić się z narzutem na wydajność bo w zamian można zyskać szereg usprawnień jakich jesteśmy pozbawieni w przypadku użycia gołych szablonów PHP. Wspomnę tylko wygodniejszą składnię czy zaimplementowany cache, ale też zwiększone bezpieczeństwo wymuszone ograniczoną dostępnością funkcji PHP wewnątrz szablonu czy też encapsulacją zmiennych. Smarty 3 umożliwia też użycie natywnych szablonów PHP ale jest to alternatywa nie ograniczenie.

Do niedawna pracowałem jeszcze z Django w wersji 9.6, w której szablony w stosunku do wersji 1.2 były mocno ograniczone. Obecne szablony Django w wersji 1.x przypominają Smarty (albo na odwrót jak ktoś woli) – z tym, że Smarty 3 są po prostu lepsze. Jestem fanem składni i możliwości języka Python mimo to na polu szablonów PHP u mnie wygrywa.

Reasumując

We wpisie tym nie dążyłem bynajmniej do porównania frameworków między sobą, a już na pewno się do porównywania rozwiązań stosowanych w języku PHP czy Python. Poruszyłem trzy zagadnienia, które w mniejszym lub większym stopniu dotyczą wszystkich z wyżej wymienionych platform. Wspomniałem o tym, że jestem zwolennikiem rozwiązań uniwersalnych ale elastyczność nie może być realizowana kosztem przejrzystości i prostoty. Dałem też do zrozumienia, że mapowanie na siłę relacyjnej bazy danych do postaci obiektów jest raczej wyrazem fundamentalizmu ideologicznego niż pragmatycznym działaniem. Z kolei w użyci systemów szablonów dostrzegłem wiele zalet.

 
2 Comments

Posted in PHP, Python

 

Liczba mnoga (msgid_plural) w plikach „po” gettext-a w django

22 mar

Django jest rozbudowanym frameworkiem przewidzianym m.in. do tworzenia wielojęzykowych serwisów. Wykonanie strony w kilku wersjach językowych wymaga uwzględnienia wielu zagadnień, takich jak formaty daty, czasu, waluty a nawet oznaczania części dziesiętnych w liczbach. W poszczególnych językach różny jest porządek sortowania choćby z uwagi na znaki narodowe wzbogacone o akcenty czy ogonki – jak w naszym rodzimym, polskim języku. Sporym wyzwaniem jest też gramatyka w tym szyk zdania, przypadki i liczba mnoga.

Django jest przygotowane do pracy z gettext-em – oprogramowaniem do tworzenia tłumaczeń. To profesjonalne narzędzie uzupełnione wieloplatformowym edytorem plików poedit służącym do edycji plików „.po” (rozszerzenie plików źródłowych gettext-a) i jednocześnie kompilującym owe pliki do formatu binarnego „.mo” przyspiesza i ułatwia umiędzynaradawianie oprogramowania tworzonego w przeróżnych językach programowania. Ja z powodzeniem używałem gettexta w aplikacjach PHP i Python.

Standardowo treść pliku „.po” składa się z szeregu par zmiennych msgid i msgstr, z których pierwsza zawiera treść komunikatu oryginalnego, a druga tłumaczenie.

msgid "Komunikat oryginalny"
msgstr "Tłumaczenie komunikatu"

Istnieją jednak komunikaty wymagające uwzględnienia liczby mnogiej. Na przykład zawierające zmienną wypełnianą dynamicznie.

msgid "We offer %(num_homes)d homes for sale "

Format gettext-a uwzględnia wiele aspektów poszczególnych języków narodowych w tym m.in. różny sposób tworzenia liczby mnogiej. W przypadku języka polskiego jest to sprawa o tyle skomplikowana, że jeden rzeczownik może mieć kilka form liczby mnogiej zależnie od liczby elementów.

Mamy w ofercie 2 domy na sprzedaż
Mamy w ofercie 5 domów na sprzedaż

W takim wypadku wpis w pliku „.po” może wyglądać następująco. Uwzględniona jest wersja pojedyncza oraz wersje mnogie.

msgid "Please correct the error below."
msgid_plural "Please correct %(num_errors)d errors below."
msgstr[0] "Proszę poprawić poniższy błąd."
msgstr[1] "Proszę poprawić %(num_errors)d poniższe błędy."
msgstr[2] "Proszę poprawić %(num_errors)d poniższych błędów."

Aby gettext wiedział jaką logiką ma się posługiwać przy konstruowaniu liczby mnogiej należy go o tym poinformować. Oprócz wyżej wspomnianych zmiennych zawierających etykiety komunikatów oraz komunikaty właściwe, w plikach „.po” znajdują się też komentarze informujące m.in o tym w jakim pliku i w której linii dany komunikat się znajduje. Są też tzw. nagłówki stanowiące swego rodzaju metadane plików tłumaczeń.

Przykładowe nagłówki:

"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 0.2\n"

Większość tychże nagłówków najlepiej pozostawić w postaci oryginalnej, jednak nagłówek informujący o formacie liczby mnogiej może wymagać dodania lub też zmiany.

"Plural-Forms: nplurals=2; plural=(n != 1);\n"

Taki nagłówek jest prawidłowy m.in dla języka angielskiego, niemieckiego, hiszpańskiego itd. Nagłówek dla wersji polskiej jest już bardziej rozbudowany.

Plural-Forms: nplurals=3; \
              plural=n==1 ? 0 : \
                     n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;

Szczegółową listę nagłówków dla różnych języków i grup językowych można znaleźć w dokumentacji getext-a w części poświęconej formom liczby mnogiej.

Na zakończenie dodam jedynie, że brak tego nagłówka w pliku „.po” przy próbie jego zapisu kończy się komunikatem o błędzie krytycznym. W moim przypadku jeśli edytowany był wpis przewidujący liczby mnogie to usunięciu uległy wszystkie zmienne msgstr[0], msgstr[1] itd. znajdujące się pod msgid_plural owych wpisów. Plik był mimo to zapisywany co przy próbie ponownego otwarcia pliku „.po” kończyło się komunikatem o uszkodzeniu pliku. Można to było łatwo naprawić dodają wyżej wspomniane zmienne, jednak problem ostatecznie został rozwiązany po dodaniu odpowiedniego nagłówka.

 
No Comments

Posted in Python

 

VAT o 1% – kolejny pretekst do podniesienia cen

03 sty

Poszedłem dzisiaj do fryzjera. Przeciętny człowiek stara się zadbać o swoją głowę jeszcze przed świętami, a już na pewno przed Sylwestrem. Ja nie lubię kolejek i wolę to zrobić na spokojnie już w nowym roku, ale dzisiaj trudno mi było opanować wzburzenie. „To tylko 2 zł więcej” – usłyszałem.

No dobrze. Kwotowo to nie wiele, ale zważywszy, że poprzednio za obcięcie „głowy” płaciłem 17 zł to 2 zł więcej stanowi blisko 12%-tową podwyżkę. W grudniu znajomy kupił samochód za 50 tys. zł – gdyby tak samo wzrosły ceny samochodów to dzisiaj musiałby dać za niego 56 tys. zł (no precyzyjnie rzecz biorąc to 50 tys – 22% VAT + 12% podwyżki + 23% VAT to daje ok 56460 zł)

I teraz wziąwszy pod uwagę, że każdy sklepikarz, taksówkarz, szewc, każda stacja benzynowa, hipermarket czy Kowalski sprzedający buraki na straganie zrobił z dniem 1 stycznia to samo, to się okazuje, że zarabiam najwyżej 88% tego co w zeszłym roku.

Wzrost VAT-u o 1% spowodował kilku a nawet kilkunasto procentowy wzrost cen, a im tańszy produkt tym wzrost większy bo dajmy na to bułka za 2 zł po 12% podwyżce będzie już kosztować 2,24, ale kto da cenę 2,24? Minimum 2,30 a to już jest 15% więcej.

  • Jeśli jesteś pracownikiem – to choć Twój pracodawca podniósł ceny na wszystkie produkty – Ty nie licz na wyższą pensję.
  • Jeśli jesteś płatnikiem VAT i kupujesz produkt, od którego możesz odliczyć sobie podatek VAT to w 90% przypadków i tak cena netto będzie wyższa od tej z zeszłego roku.
  • Jeśli nie możesz odliczyć VAT-u to jesteś krową, którą wszyscy doją.

Większość z nas jest dojną krową. Mając działalność gospodarczą jesteś trochę mniej mleczną krasulą bo sam możesz podnieść ceny na swoje usługi i parę rachunków wrzucić w koszty, ale i tak dopłacasz do tego biznesu. Natomiast zarobią na tym Ci, którym i tak dotąd niczego nie brakowało.

Czy zarobi na tym Państwo? Cuż 23% to więcej od 22%, a 23% od droższych produktów i usług to dużo więcej niż 22% od tańszych produktów i usług. Z drugiej jednak strony budżet gromadzi się po to aby go wydać więc może się okazać, że te wysokie 23% ma mniejszą siłę nabywczą niż tamto słabe 22%.

 
No Comments

Posted in Priv

 

Wydobywanie tekstów i grafik z plików PDF

19 lis

Bywa że klient przysyła Ci treści do umieszczenia na stronie z krótkim komentarzem – „Tak to sobie wyobrażam”. Otwierasz maila i widzisz plik PDF. Są tam pięknie ułożone teksty okraszone kolorowymi fotografiami, wszystko elegancko poukładane i skomponowane, a Ty drapiesz się w głowę i nic nie mówisz bo szkoda słów. Jeśli jesteś webdeveloperem, specjalistą od cięcia i stylowania to szczerze Ci współczuję. Jeśli jednak Twoim jedynym zadaniem jest wydobycie z tego PDF-a wszystkich tekstów oraz grafik to jest nadzieja.

Klikacze lub marzyciele często patrzący w „okna” mogą skorzystać z jednej z rad proponowanych w artykule How to Extract Text from a PDF document lub How Can I Get Text or Images Out of a PDF File?. Alternatywą jest użycie konsoli i wierzcie mi w tym przypadku jest to o wiele wygodniejsze i szybsze rozwiązanie.

Do wyłuskania tekstów używamy programu „pdftotext” będącego częścią programu „xpdf

pdftotext ./dokument.pdf

Gdyby były problemy z polskimi literami warto zapoznać się z opcją „enc”:

pdftotext -enc Latin2 dokument.pdf

albo

pdftotext -enc UTF-8 dokument.pdf

Z obrazkami robimy identycznie tylko, że używając programu „pdfimages” także będącego konsolowym narzędziem „xpdf-a”

pdfimages ./dokument.pdf  przedrostek_obrazka

Program „pdfimages” zapisuje obrazki w formacie plików „ppm” więc trzeba je jeszcze przekonwertować do jpg-ów np. przy pomocy programu „pnmtojpeg” będącego częścią większego pakietu programów graficznych „Netpbm„.

for pic in *.ppm 
do 
    pnmtojpeg "${pic}" > "${pic/%ppm/jpg}" 
done

O ile programy „pdftotext” jak i „pdfimages” miałem już w systemie to „pnmtojpeg” trzeba było doinstalować i kiedy już to zrobiłem i przetestowałem przypomniałem sobie, że mam przecież zainstalowanego „ImageMagick-a„, którego użycie okazało się jeszcze prostsze.

mogrify -format jpg *.ppm

Pliki wynikowe uzyskane po zastosowaniu „mogrify” są większe co sugeruje mniejszą stratę na jakości, poza tym jak się ma „ImageMagick-a” to można z tymi plikami zrobić przy okazji dużo więcej np. automatycznie przeskalować, utworzyć miniatury, dodać ramki czy co nam tam jeszcze przyjdzie do głowy. Tworząc skrypt, który wszystkie wyżej przytoczone komendy zbiera w jedną zdecydowałem się właśnie na „mogrify”.

Plik extractpdf.sh

#!/bin/sh
 
if [ $# -lt 1 ]; then
    echo "Usage: `basename $0` file.pdf"
    exit 1;
fi
 
PDF=$1
FILE_NAME=`basename ${PDF%.*}`
TEMP_DIR="`dirname $PDF`/${FILE_NAME}"
 
if [ ! -e $TEMP_DIR ]; then
    mkdir -p $TEMP_DIR;
fi
pdftotext $PDF "${TEMP_DIR}/${FILE_NAME}.txt"
 
pdfimages $PDF "${TEMP_DIR}/${FILE_NAME}"
 
mogrify -format jpg ${TEMP_DIR}/*.ppm
find ${TEMP_DIR}/ -name "*.ppm" -exec rm {} \;

Skrypt oszczędza masę pisania poza tym jest uniwersalny więc nadaje się do wielokrotnego użytku. Teraz wystarczy nadać mu prawa do wykonywania

chmod +x ./extractpdf.sh

i cała praca to wywołanie skryptu z podaniem ścieżki do pliku pdf w parametrze

./extractpdf.sh ./dokument.pdf

Można pokusić się o rozbudowę tego skryptu o sprawdzanie czy wymagane programy są zainstalowane oraz zwiększyć funkcjonalność poprzez próbę użycia „pnmtojpeg” w przypadku jeśli „ImageMagick” nie jest zainstalowany.

Plik extractpdf2.sh

#!/bin/bash
 
if [ $# -lt 1 ]; then
    echo "Usage: `basename $0` file.pdf"
	exit 1;
fi
 
type -P pdftotext &>/dev/null || { echo "I require pdftotext but it's not installed.  Aborting." >&2; exit 1; }
 
type -P pdfimages &>/dev/null || { echo "I require pdfimages but it's not installed.  Aborting." >&2; exit 1; }
 
if type -P mogrify >/dev/null; then
    CONVERTER="mogrify"
else
    type -P pnmtojpeg &>/dev/null || { echo "I require the mogrify or pnmtojpeg but none of them is not installed.  Aborting." >&2; exit 1; } 
    CONVERTER="pnmtojpeg"
fi
 
PDF=$1
FILE_NAME=`basename ${PDF%.*}`
TEMP_DIR="`dirname $PDF`/${FILE_NAME}"
 
if [ ! -e $TEMP_DIR ]; then
    mkdir -p $TEMP_DIR;
fi
 
pdftotext $PDF "${TEMP_DIR}/${FILE_NAME}.txt"
 
pdfimages $PDF "${TEMP_DIR}/${FILE_NAME}"
 
if [ $CONVERTER = 'mogrify' ]; then
    mogrify -format jpg ${TEMP_DIR}/*.ppm
    find ${TEMP_DIR}/ -name "*.ppm" -exec rm {} \;
else
    for PIC in ${TEMP_DIR}/*.ppm
    do
        pnmtojpeg "${PIC}" > "${PIC/%ppm/jpg}"
        rm $PIC
    done
fi

UWAGA!!! Użycie „type -P” wymaga skorzystania konkretnie z powłoki „bash” gdyż w „sh” program „type” nie ma opcji „-P” i traktuje ją jak ścieżkę do pliku co kończy się komunikatem błędu (-P: not found). Dlatego pomimo, że plik dalej nazywa się extractpdf.sh to zamiast „#!/bin/sh” należy wpisać „#!/bin/bash”.

To oczywiście nie wyczerpuje naszych możliwości. Zaproponowane przeze mnie narzędzie to niezbędne minimum. Stworzenie tego skryptu kosztowało mnie trochę czasu ale jest to praca jednorazowa. Podobnie miałem ze skryptem do generowania miniatur za to obecnie stale mam go w swoim arsenale i wierzcie mi, że suma sumarum zaoszczędził mi już naprawdę wiele czasu.

 
1 Comment

Posted in Bash

 

Domknięcia, funkcje anonimowe przestrzenie nazw i dekoratory

19 lis

Pojęcie dekoratora w języku Python to coś więcej niż wzorzec projektowy. To elegancki sposób na zwiększenie możliwości danej funkcji czy metody doskonale wykorzystujący unikalne cechy języka Python. Mechanizm działania dekoratorów oraz sposób ich użycia doskonale opisał Kent S Johnson (opracowanie to można znaleźć także w języku polskim).

Prostym i użytecznym przykładem dekoratora może być np. funkcja mierząca czas wykonania funkcji udekorowanej.

import time
 
def timeit(method):
    """Mierzy czas wykonania funkcji"""
 
    def check_time(*args, **kw):
        ts = time.time()
        result = method(*args, **kw)
        te = time.time()
        log(method.__name__, args, kw, ts, te)
        return result
 
    def log(name, args, kw, time_start, time_end):
        msg = '%r (%r, %r) %2.10f sec' % (name, args, kw, time_end - time_start)
        print msg
 
    check_time.__name__ = method.__name__
    return check_time

Użycie dekoratora w Pythonie to syntaktyczna perełka.

@timeit
def say_hello(param):
    print param
    time.sleep(0.5)

W PHP osiągnięcie czegoś takiego jest po prostu niemożliwe. Od wersji PHP 5.3 język został wzbogacony o nowe cechy takie jak domknięcia, funkcje anonimowe i przestrzenie nazw. Czy owe mechanizmy umożliwiają implementację czegoś co choćby przypominało pythonowy dekorator?

W PHP nie da się zdefiniować funkcji, a potem przypisać jej inną nazwę.

def foo():
    print 'OK'
 
faa = foo
 
faa() # w wyniku otrzymamy: OK

no chyba że użyjemy funkcji anonimowej (ang. anonymous function)

$foo = function() {echo 'OK';};
$faa = $foo;
$faa(); // w wyniku otrzymamy: OK

Dekorowanie funkcji anonimowych przy użyciu domknięć

Możemy zapomnieć (na razie) o dekorowaniu tradycyjnych funkcji ale udekorowanie funkcji anonimowej staje się potencjalnie możliwe.

$foo = function() {echo 'OK';};
 
$foo = function() use ($foo) {
    echo 'Jest '; return $foo();
};
 
$foo(); // w wyniku otrzymamy: Jest OK

Domknięcia (ang. closures) stoją u podstaw programowania w Pythonie, są też od zawsze obecne i powszechnie używane np. w Javascript-cie.

var Example = function()
{ 
    this.public = function() 
    { 
        return "This is a public method"; 
    }; 
 
    var private = function() 
    { 
        return "This is a private method"; 
    };
};
 
Example.public()  // returns "This is a public method" 
Example.private() // error - doesn't work

Ich implementacja w PHP to kwestia dyskusyjna podobnie jak ich użyteczność. Z uwagi na określone cechy PHP domknięcia w tym języku wydają się nie być odpowiednikiem tego mechanizmu znanym z innych języków programowania. W przeprowadzanym przeze mnie teście przydały się jednak i osobiście dostrzegam jeszcze kilka innych ich praktycznych zastosowań. Wracając jednak do głównego wątku tego wpisu.

W miarę prosty sposób udało się udekorować funkcję „foo”. Domknięcie można zamknąć w funkcji dzięki czemu nadaje się do wielokrotnego użytku.

function timeit($func) {
    return function() use ($func) {
        // pobranie wszystkich argumentów funkcji
        $args = func_get_args();
 
        // utworzenie funkcji pomocniczej
        $microtime_float = function()
        {
            list($usec, $sec) = explode(" ", microtime());
            return ((float)$usec + (float)$sec);
        };
 
        // rozpoczęcie pomiaru czasu
        $time_start = $microtime_float();
 
        // wywolanie funkcji
        $result = call_user_func_array($func, $args);
 
        // zakończenie pomiaru czasu i wyświetlenie wyników
        $time_end = $microtime_float();
        $time = $time_end - $time_start;
        printf ("\nfunction was executed during %01.6f seconds\n", $time);
 
        // zwrócenie resultatu działania funkcji
        return $result;
    };
}

Zaprezentuje może użycie „dekoratora” timeit na funkcji wymagającej parametrów.

$func_say_hello = function(param) {
	return 'Hello '. $param;
};
 
$func_say_hello = timeit($func_say_hello);
echo $func_say_hello('World');

Funkcje anonimowe są, a raczej będą stosunkowo rzadko używane, podobnie zresztą jak funkcje lambda w Pythonie, których są odpowiednikiem. Pomiar czasu wykonania np. funkcji sortującej to z pewnością przydatna sprawa, ale wzbogacanie funkcji anonimowych o nowe możliwości przy pomocy „dekoratorów” wydaje się być mało przydatne. Czy zatem można udekorować zwykłą funkcję?

Czy przestrzenie nazw pomagają w dekorowaniu funkcji?

Po co nam przestrzenie nazw (ang. namespaces)? Załóżmy, że mamy w pliku functions.php zdefiniowaną przestrzeń nazw „lib” i funkcję „say_hello”

<?php
namespace lib {
    function say_hello($param)
    {
        return 'Hello '. $param;
    }
}

Gdybyśmy w pliku index.php dołączyli plik functions.php moglibyśmy używać zdefiniowanych w nim funkcji.

<?php
include './functions.php';
echo \lib\say_hello('World');

Wywołanie say_hello(‘World’); bez podania poprzedzającego go namespace-a zakończyłoby się błędem. Niestety PHP nie wspiera jak na razie możliwości aliasowania funkcji czy stałych zamkniętych w przestrzeni nazw dlatego też chęć odwoływania się do funkcji „say_hello” bezpośrednio musi zostać poprzedzona definicją nazwijmy to „atrapy”, która przy okazji pozwoli udekorować opakowywowaną funkcję.

include './functions.php';
 
function timeit($func) {...}
 
function say_hello($param)
{
    $func_say_hello = '\lib\say_hello';
    $func_say_hello = timeit($func_say_hello);
    $result = $func_say_hello($param);
}
echo say_hello('World');

Zwróćmy uwagę na to, że funkcje „say_hello” i „\lib\say_hello” to nie to samo i równie dobrze moglibyśmy nie używać przestrzeni nazw i osiągnęlibyśmy to samo (tylko nazwy funkcji byłyby inne)

function timeit($func) {...}
 
function say_hello($param) {...}
 
function decorated_say_hello($param)
{
    $func_say_hello = 'say_hello';
    $func_say_hello = timeit($func_say_hello);
    $result = $func_say_hello($param);
}

Z punktu widzenia wygody użycia to czy użyjemy przestrzeni nazw czy nie to mamy to samo. I tak chcąc zmierzyć czas wykonania funkcji „say_hello” musimy zmienić jej wywołanie w kodzie na „decorated_say_hello”. Nie da się udekorować zdefiniowanych funkcji w pythoniczny sposób.

Aliasy – nowe możliwości dekorowania klas

W przypadku funkcji moje rozważania to jedynie gimnastyka mózgu, jednak w przypadku klas teoretyczne dywagacje – jak się zaraz okaże – przybiorą praktyczną formę.

Wspominałem wyżej o aliasach. Tego czego nie da się zrobić z funkcjami i stałymi w PHP > 5.3 da się uczynić z klasami.

Załóżmy, że plik functions.php wygląda tak

namespace lib {
	class Say
	{
		public static function hello($param)
		{
			return 'Hello '. $param;
		}
 
		public function goodbye($param)
		{
			return 'Goodbye '.$param;
		}
	}
}

Do pliku index.php możemy dołączyć functions.php i przypisać klasie „/lib/Say” alias Say

include './functions.php';
 
function timeit($func) {...}
 
use \lib\Say as Say;
 
echo Say::hello('Adam');
$say = new Say();
echo $say->goodbye('Ewa');

Nadanie aliasu sprawia, że możemy używać klasy tak jakby była zdefiniowana bez użycia przestrzeni nazw. Jeśli w tym momencie przyjdzie nam ochota zmierzenia czasu wykonania metod klasy „Say” (a właściwie „\lib\Say”) bez zmiany wywołań musimy zrobić dwie rzeczy. Zakomentować linijkę nadającą alias oraz udekorować klasę.

include './functions.php';
 
function timeit($func) {...}
 
//use \lib\Say as Say;
 
class Say
{
	private $_obj;
 
	public function  __construct() {
		$this->_obj = new \lib\Say();
	}
 
	public static function hello($param)
	{
		$func = '\lib\Say::hello';
		$func = \deco\timeit($func);
		return $func($param);
	}
 
	public function goodbye($param)
	{
		$func = function($obj, $param) {
			return $obj->goodbye($param);
		};
 
		$func = \deco\timeit($func);
		return $func($this->_obj, $param);
	}
}
 
echo Say::hello('Adam');
$say = new Say();
echo $say->goodbye('Ewa');

Wywołanie metod się nie zmieniło, ale wzbogaciliśmy naszą klasę o funkcjonalność pozwalającą nam zmierzyć czas wykonania zarówno metody statycznej klasy, jak i metody instancji. Do wygody użycia dekoratora „@timeit” w Pythonie jest jeszcze bardzo daleko. Udekorowaliśmy zaledwie dwie metody, a trzeba było napisać tyle kodu. Z każdą kolejną metodą przybędzie dodatkowego kodu w dekoratorze. Czy da się ten przyrost ograniczyć?. Dzięki metodom magicznym da się zrobić bardziej uniwersalny dekorator.

include './functions.php';
 
function timeit($func) {...}
 
//use \lib\Say as Say;
 
class Say
{
	private $_obj;
 
	public function  __construct() {
		$this->_obj = new \lib\Say();
	}
 
	public static function  __callStatic($name, $arguments)
	{
		$func = '\lib\Say::'.$name;
		return self::_addDecorators($func, $arguments);
	}
 
	public function __call($name, $arguments)
	{
		$func = function() {
			$params = func_get_args();
			$obj = array_shift($params);
			$method = array_shift($params);
			return call_user_func_array(array($obj, $method), $params);
		};
 
		array_unshift($arguments, $name);
		array_unshift($arguments, $this->_obj);
		return self::_addDecorators($func, $arguments);
	}
 
	private static function _addDecorators($func, $arguments)
	{
		$func = \deco\timeit($func);
		return call_user_func_array($func, $arguments);
	}
}
 
echo Say::hello('Adam');
$say = new Say();
echo $say->goodbye('Ewa');

Konkluzje

  1. To naprawdę działa – doznałem intelektualnego orgazmu.
  2. Nie stosujcie takich rozwiązań w prawdziwym projekcie

Próba debugowania takiego kodu skazana jest z góry na porażkę. Przeprowadzony przeze mnie eksperyment dostarczył mi najlepszego dowodu na to, że nie ma sensu implementować na siłę mechanizmów znanych z innych języków. PHP ma swoją naturę, która czasami jest ograniczeniem, ale często stanowi też jego zaletę.

 
No Comments

Posted in PHP, Python

 

Pogodzenie Python-a z PHP na Apache2 (mod_wsgi + mod_php)

09 paź

Wstęp

Poradników i tutoriali opisujących konfigurację Apache z PHP i MySQL pod Linuxem (LAMP) jest cała masa. W Ubuntu sprowadza się to do wywołania w konsoli prostego polecenia

sudo apt-get install php5 php5-mysql mysql-server apache2 libapache2-mod-auth-mysql

… i mamy wszystko co potrzeba. Więcej na ten temat znajdziemy choćby w artykule Instalacja Apache + PHP5 + MySQL.

Instalacja frameworka Django i preferowanego przez pythonowców serwera baz danych Postgres jest równie prosta – przynajmniej pod Ubuntu.

sudo apt-get install python-django postgresql python-psycopg2 postfix python-imaging python-docutils

Django ma jedną z najlepszych dokumentacji, a o Postgresie pisze niemal co drugi bloger, który miał coś z nim do czynienia tak więc zainteresowanych po prostu odeślę do wciąż aktualnego i w miarę kompletnego wpisu Instalacja PostgreSQL 8.3 w Ubuntu 8.04.

O ile hostowanie skryptów PHP na Apache-u jest rzeczą naturalną zarówno w środowisku deweloperskim jak i produkcyjnym o tyle w przypadku Pythona a konkretnie aplikacji Django już nie. Django posiada świetny wbudowany serwer stworzony na podstawie klasy BaseHttpServer ze standardowej biblioteki Pythona, który świetnie sprawdza się do szybkiego testowania aplikacji o niepełnej funkcjonalności. Restartuje się automatycznie przy każdej zmianie w kodzie a ponadto ponieważ uruchamiany jest w terminalu wyświetla na wyjściu wszystkie instrukcje print.

Użycie serwera wbudowanego w środowisku produkcyjnym jest zabronione licencją, a poza tym istnieje jeszcze kilka innych obiektywnych powodów aby tego nie robić, jak choćby bezpieczeństwo i stabilność, czy wydajność. Jak się okazuje także dla celów developerskich warto czasem podjąć trud konfiguracji serwera zewnętrznego.

W przypadku Apache-a w kontekście hostowania skryptów napisanych w języku Python najczęściej stosuje się dwa rozwiązania tj. mod_python lub mod_wsgi. Oba z modułów nie powodują konfliktów z mod_php, tak więc chcąc na swoim serwerze obsługiwać zarówno aplikacje PHP i Python można zastosować dowolne z wymienionych rozwiązań.

Ponoć konfiguracja Apache2 + mod_python jest częściej stosowana, lepiej przetestowana i udokumentowana, ja jednak zdecydowałem się na WSGI z uwagi na elastyczność jaką udało mi się uzyskać.

Założenia

W przypadku środowisk produkcyjnych kluczową kwestią jest bezpieczeństwo i stabilność serwera. W środowisku developerskim, w którym skrypty uruchamiane są jedynie na komputerze lokalnym osobiście stawiam na elastyczność kosztem nawet bezpieczeństwa i stabilności. Nie lubię grzebać w plikach konfiguracyjnych Apache-a i edytowanie pliku /etc/apach2/httpd.conf lub któregoś z plików w katalogu /etc/apache2/sites-available/ za każdym razem kiedy tworze nowy projekt lub zmieniam jego nazwę. W przypadku projektów realizowanych w języku PHP wystarczy, że w moim katalogu workspace stworzę nowy katalog np. „nowy_projekt_php”, wrzucę do niego plik index.php i wywołam w pasku adresu przeglądarki

http://localhost/nowy_projekt_php
Podobny efekt chciałem uzyskać w przypadku projektów realizowanych w Django.

Instalacja mod_wsgi

Moduł WSGI możesz zainstalować ze źródeł, co zostało przystępnie opisane w artykule Ubuntu Hardy – mod_wsgi Installation, ale w Ubuntu można skorzystać z repozytorium:

sudo apt-get install libapache2-mod-wsgi

Aby aktywować moduł (w Ubuntu 10.04 – można pominąć)

sudo a2enmod mod-wsgi

W przypadku instalacji z repozytorium wystarczy już tylko restart serwera.

sudo /etc/init.d/apache2 restart

Konfiguracja Apache

Integracja mod_wsgi z Django jest świetnie opisana zarówno na stronach dokumentacji Django, jak też (może nawet lepiej) na stronie rozszerzenia specjalnie poświęconej temu zagadnieniu w artykule pod pt. Integration With Django.

Ja jednak zaproponuję jeszcze inne rozwiązanie na które natknąłem się na forum w wątku Ubuntu + Apache2 + WSGI (mod_wsgi) with LAMP i lekko zmodyfikowałem na swoje potrzeby.

Aby osiągnąć wyżej opisane założenie w najprostszy sposób należy:

Stworzyć Virtualhosta w pliku httpd.conf

sudo vim /etc/apache2/httpd.conf

<VirtualHost *>
    ServerName testhost
    DocumentRoot /home/myuser/workspace/

    <Directory /home/myuser/workspace/>
        Options Indexes FollowSymLinks MultiViews ExecCGI

        AddType application/x-httpd-php .php .phtml
        AddType application/x-httpd-php-source .phps
        AddHandler application/x-httpd-php .php .phtml
        AddHandler cgi-script .cgi
        AddHandler wsgi-script .wsgi

        Order allow,deny
        allow from all
    </Directory>
</Virtualhost>

W przeciwieństwie do rozwiązania zaproponowanego na forum proponuję usunąć dyrektywę „AllowOverride None” dzięki czemu możliwe będzie skorzystanie z mod_rewrite.

Trzeba jeszcze poinformować serwer, że domyślnym plikiem w aplikacji napisanej w Pythonie będzie index.wsgi

sudo gedit /etc/apache2/mods-enabled/dir.conf

<IfModule mod_dir.c>
    DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm index.wsgi
</IfModule>

W katalogu /home/myuser/workspace/ utwórzmy nowy podkatalog nowy_projekt_python a w nim plik index.wsgi o treści:

def application(environ, start_response):
    status = '200 OK' 
    output = 'Hello World!'
 
    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
 
    return [output]

Jeśli po wywołaniu


http://localhost/nowy_projekt_python

widać „Hello World!” to znaczy, że wszystko jest ok, jeśli nie to może wystarczy zrestartować serwer ;)

Twórcy Django dążą do tego aby ich framework był pythonowy jak to tylko możliwe dlatego np. konfigurację trzymają w plikach py a nie np. w xml albo yml-u. Dzięki takiemu podejściu możemy mieć pewność, że po wrzuceniu do katalogu nowy_projekt_python aplikacji Django i modyfikacji pliku index.wsgi, zobaczymy w przeglądarce to co byśmy widzieli po uruchomieniu serwera wbudowanego.

Zmodyfikujmy index.wsgi

import os, sys
sys.path.append('/home/myuser/workspace')
sys.path.append('/home/myuser/workspace/nowy_projekt_python')
 
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
 
import django.core.handlers.wsgi
 
_application = django.core.handlers.wsgi.WSGIHandler()
 
def application(environ, start_response):
	if environ['wsgi.url_scheme'] == 'https':
		environ['HTTPS'] = 'on'
	return _application(environ, start_response)

Przydałby się jeszcze plik .htaccess

<IfModule mod_rewrite.c>
    # Turn on URL rewriting
    RewriteEngine On

    # Installation directory
    RewriteBase /nowy_projekt_python/

    # Protect hidden files from being viewed
    <Files .*>
        Order Deny,Allow
        Deny From All
    </Files>

    # Allow any files or directories that exist to be displayed directly
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

    # Rewrite all other URLs to index.wsgi/URL
    RewriteRule .* index.wsgi/$0 [PT]

</IfModule>

Zaproponowane tutaj rozwiązanie jest jedynie punktem wyjścia do stworzenia swojej własnej uniwersalnej konfiguracji środowiska developerskiego do pracy z projektami PHP i Python. Zapewne przydałoby się parę rzeczy inaczej skonfigurować lub też dodać kilka opcji jak np. dyrektywę zapobiegającą wyświetlaniu treści plików python w przeglądarce. Zachęcam do eksperymentowania i dzielenia się swoim doświadczeniem.

 
2 Comments

Posted in PHP, Python

 

Obsługa __call w kontrolerach Kohana 3

03 paź

Obsługa magicznej metody __call w kontrolerach Kohana 3 została zarzucona. W zamian za to Twórcy frameworka zaimplementowali obsługę tzw „catch all-a”. Definicje ścieżek routingu definiuje się w pliku application/bootstrap.php

U mnie „catch all” wygląda mniej więcej tak.

Route::set('catch_all', '<path>', array('path' => '.+'))
    ->defaults(array(
        'controller' => 'error',
        'action' => '404',
));

Czyli jak łatwo się domyślić w przypadku nie istnienia danego kontrolera i akcji zwracana jest strona 404.

Jednak zamiast kontrolera error można by w tym miejscu wskazać dowolny inny kontroler a w miejsce 404 dowolną inną metodę.

Route::set('catch_all', '<path>', array('path' => '.+'))
    ->defaults(array(
        'controller' => 'page',
        'action' => 'index',
));

Dzięki takiemu podejściu z url-a można wyeliminować zarówno nazwę kontrolera jak i metody i ograniczyć się jedynie do unikalnego identyfikatora zasobu.

Wadą tego rozwiązania jest to, że wariant ten jest rozpatrywany dopiero na samym końcu, a poza tym wywołanie adresu na kształt http://domena.com/page/nie_istniejaca_akcja spowoduje rzucenie wyjątku

exception 'ReflectionException' with message 'Method action_nie_istniejaca_akcja does not exist'...

Wyjątek ten można oczywiście obsłużyć w bootstrapie tym bardziej, że parametr ‘status’ instancji klasy Request jest ustawiany na 404.

$request = Request::instance();
try {
    $request->execute();
    echo $request
        ->send_headers()
        ->response;
} catch (Exception $e) {
    if ($request->status == '404') {
        Request::factory('error/404')->execute();
    }
}

Co jednak kiedy chcemy mieć możliwość ograniczenia mechanizmu „catch all” do konkretnego kontrolera albo użyć go w więcej niż jednym kontrolerze? Jedynym rozwiązaniem wydaje mi się modyfikacja klasy Request. Nie namawiam oczywiście do grzebania w katalogu „system”. Kaskadowy układ plików Kohany pozwala na utworzenie pliku request.php w katalogu application/classes i zdefiniowanie klasy „Request” dziedziczącej po „Kohana_Request”. Modyfikacji wymaga w zasadzie jedna linia w metodzie „execute”.

Zamiast

$class->getMethod('action_'.$action)->invokeArgs($controller, $this->_params);

należy najpierw sprawdzić czy dana metoda istnieje, a podjąć próbę wywołania magicznej metody __call.

if($class->hasMethod('action_'.$action) === true)
{
    $class->getMethod('action_'.$action)->invokeArgs($controller, $this->_params);
}
else
{
    $class->getMethod('__call')->invokeArgs($controller, array('action_'.$action, $this->_params));
}
 
No Comments

Posted in PHP

 

Jak wydobyć kod authinfo domeny od rejestratora.

10 sie

Dawno nie pisałem. Przyczyn tego stanu rzeczy oraz dłuższej niedostępności mojego bloga jest kilka, ale pra przyczyną było to, że kilka lat temu zaufałem firmie.

„Info-Cal” Roman Trziszka
ul. Chludowska 387 lok. 9
62-003 Biedrusko

NIP: 778-100-71-00, Regon: 630002043

Nie jestem zbyt wymagającym klientem – nie narzucam się zbytnio, nie robię awantury kiedy serwer nie działa przez kilka godzin, nie żądam zakręconych rozwiązań i 24 godzinnego suportu. Jedyne czego nienawidzę to kiedy robi się ze mnie idiotę, a ponieważ obsłudze serwera cal.pl zdarzyło się kilka razy próbować wmówić mi, że wina niedziałającej aplikacji leży po mojej stronie podczas gdy były to najczęściej niedopatrzenia administratorów grzebiących w konfiguracji serwera postanowiłem rozstać się z hostingiem na cal.pl.

Ale jak tu się uwolnić od niechcianej miłości? Cały proces przypominał mi próbę przeprowadzenia rozwodu z żoną, która na pożegnanie ima się wszelkich sposobów aby wyrwać dla siebie co się jeszcze da.

Z backupami baz danych i oskryptowania stron nie było problemu. Schody zaczęły się kiedy poprosiłem o wydanie kodów authinfo do moich domen w tym tej, pod którą można znaleźć niniejszy blog.

Wydawać by się mogło, że przeniesienie domeny od jednego rejestratora do innego to tylko formalność. Wystarczy poprosić o kod authinfo podmiot, który dotychczas rejestrował domeny w naszym imieniu, a następnie przesłać je – zazwyczaj za pośrednictwem formularza na stronie internetowej – nowemu rejestratorowi, a on już za nas załatwi wszystkie sprawy formalne i techniczne. Tymczasem chęć przeniesienia domen w inne miejsce kłóci się z interesem naszego dotychczasowego rejestratora, który tak jak w moim przypadku robi wszystko aby cały proces wydłużyć, a najlepiej zniechęcić klienta do całej operacji.

Pan Roman Trziszka poszedł w formalizm. Wydanie kodu authinfo w firmie INFO-CAL wymaga wysłania stosownego wniosku wraz z kserokopiami odpowiednich dokumentów przy czym nie są one szczegółowo wymienione. Można było skorzystać z faksa, ale ja nauczony doświadczeniem wolałem wysłać list za potwierdzeniem odbioru. Po czterech dniach otrzymałem lakoniczną odpowiedź, że moje wnioski zostały odrzucone a powodem był „brak wymaganych dokumentów jako załączników”. Po wymianie kilku maili dowiedziałem się, że abonentem domen jest osoba fizyczna, a nie osoba prawna, a poza tym nie zgadza się adres i że powinienem wysłać kserokopie dowodu osobistego, a nie dokumenty firmy. Ja się pytam od kiedy jednoosobowa działalność gospodarcza ma osobowość prawną? Adres tzw. „zakładu głównego” rzeczywiście nie pokrywał się z moim poprzednim adresem zamieszkania jednak w przesłanym „Zaświadczeniu o dokonaniu wpisu do ewidencji działalności gospodarczej” można było łatwo odnaleźć drugi adres wykonywania działalności gospodarczej zgodny z tym zarejestrowanym w bazie WHOIS. Moja argumentacja została podsumowana krótkim stwierdzeniem, że wnioski zaraz po odrzuceniu są niszczone i nawet gdyby pracownicy firmy INFO-CAL chcieli to fizycznie nie mogą tego zweryfikować dlatego proszony jestem o wysłanie kolejnych wniosków wraz z kserokopią dowodu osobistego.

Muszę powiedzieć, że krew mnie wtedy zalała. W międzyczasie ożeniłem się, przeprowadziłem i wymieniłem dowód osobisty – zawsze twierdziłem, że małżeństwo jest źródłem wszelkich kłopotów – jednak nie straciłem głowy do końca bo się nie przemeldowałem i w moim dowodzie osobistym był wciąż ten sam adres co kiedyś. Mogłem wysłać kolejne wnioski, ale wyczytałem na stronie NASK informację zgodnie z którą:

Na żądanie Abonenta Partner obsługujący nazwę domeny zobowiązany jest niezwłocznie wydać kod authinfo, który umożliwi przeniesienie obsługi. Partner nie może uzależniać wydania kodu od uprzedniego spełnienia dodatkowych warunków.

Zażądałem więc wydania kodów authinfo i zagroziłem, że w przypadku nie spełnienia mojej prośby popełnię stosowny dokument do Rzecznika Praw Konsumenta oraz do NASK. Oczywiście Pan Roman Trziszka się nie przejął stwierdzając jedynie, że on wyda kody tylko, że musi mieć pewność co do tożsamości abonenta domen. Jakoś potwierdzenie mojej tożsamości nie było wymagane przy corocznym przedłużaniu ważności domeny, przy zmianie danych osobowych co uczyniłem przy okazji założenia działalności gospodarczej przy okazji pobierania ze strony cal.pl dokumentów finansowych (faktur). Potwierdzenie mojej tożsamości nie było wymagane w całym procesie zarządzania kontem www, aż do tego momentu.

Jako programista jestem osobą leniwą, a do tego z natury ugodową. Wysłałem w końcu ponownie wnioski (oczywiście za potwierdzeniem odbioru). Kiedy po 7 dniach od otrzymania potwierdzenia odbioru nie otrzymałem odpowiedzi na moje wnioski napisałem upominającego maila. Przedstawiciel firmy INFO-CAL – dodam na marginesie, że pod żadnym z mail otrzymanych od obsługi cal.pl nigdy nie znalazł się jakikolwiek imienny podpis – udzielił mi odpowiedzi, że jeszcze wniosków nie otrzymali, a jeśli nawet to na odpowiedź na wnioski pisemne mają 20 dni, i że jeżeli zależy mi na czasie to powinienem wysłać wnioski faksem.

Pokornie wysłałem wnioski faksem. Nikt nie zgadnie jaką otrzymałem odpowiedź? „Wnioski odrzucone z powodu nieczytelności.” Ręce mi opadły i na prawdę byłem już gotów napisać tego cholernego paszkwila do Rzecznika Praw Konsumenta oraz do NASK. Przestałem wymieniać korespondencję z INFO-CAL bo mijało się to z celem. Powiedziałem sobie w duchu, że nie dam zarobić już ani grosza tej „cholernej hienie”. Wykupiłem opcje na swoje własne domeny i czekałem cierpliwie kiedy wygasną.

Jakiś tydzień przed wygaśnięciem pierwszej z domen otrzymałem wiadomość od firmy Pana Romana Trziszki, że kody authinfo do domen zostaną wydane, za wyjątkiem tej najbliższej wygaśnięciu, do której kod zostanie wydany ale pod warunkiem jej wcześniejszego przedłużenia, zgodnie z pkt. 6.12 regulaminu.

6.12. Administrator może odmówić wydania kodu autoryzacyjnego domeny ( kod AuthInfo ) w przypadku domen wygasających w przeciągu 30 dni od daty otrzymania wniosku oraz do 30 dni po wykonaniu odnowienia domeny.

Wydanie kodów tym razem nie wymagało wysyłania żadnych szczególnych wniosków ani załączników, a jedynie wysłania maila z prośbą na adres domeny@consultingservice.pl??? Wysłałem i otrzymałem na drugi dzień kody do wszystkich moich domen.

Nie opiszę co czułem, kiedy się dowiedziałem, że faktycznym rejestratorem moich domen nie jest „Info-Cal” Roman Trziszka.

Później znalazłem w sieci wpis Damiana Nowaka pt. Prawa właściciela domeny – transfer i wydanie kodu authinfo, który może przydać się kiedyś każdemu posiadaczowi choćby jednej domeny. Żałuję, że nie natrafiłem na niego wcześniej – zaoszczędziłbym sobie wiele nerwów.

Firma „Info-Cal” Pana Romana Trziszki świadomie wprowadziła mnie w błąd w celu uzyskania korzyści materialnych, nie przestrzega regulaminów NASK, których jest już na chwilę obecną pełnoprawnym partnerem, a na dodatek dopuściła się naruszenia ustawy o ochronie danych osobowych. Nie będę się już rozwodził na temat niekompetencji administratorów serwerów cal.pl. To temat na kolejny wpis. Wszystkim obecnym i potencjalnym klientom wyżej wspomnianej firmy szczerze odradzam korzystania z jej usług.

 
19 Comments

Posted in Priv