Archiwalia w kategorii 'code'

Wego: Wtyczki

Wczoraj pisałem o dodaniu do Wego CMS wtyczek dla systemów płatności online. Cypherq poprosił o szczegóły dotyczące implementacji. Noszę się z zamiarem przygotowania kilkuetapowego howto, kiedy tylko pojawi się darmowa wersja Wego (tak, to bardzo możliwe, po szczegóły odsyłam do nbw). Póki co, o samym sposobie ładowania wtyczek.

Moduły — wtyczki z automatyczną rejestracją

Wego ma dwa rodzaje pluginów. Pierwszy rodzaj — wtyczki z automatyczną rejestracją — używany jest przez moduły. Moduł to zamknięta część systemu, widoczna jako osobna pozycja w panelu. Przykładem niech będą Artykuły lub Kosz.

Automatyczna rejestracja oznacza tyle, że plik z wtyczką zawiera klasę o nazwie takiej samej, jak plik i nie musi właściwie robić nic więcej:

<?

/* $Id: foo.module.php 1 2007-04-27 12:00:00Z patrys $ */

require_once(THE_ROOT . '/class/genericModule.class.php');

/**
 * Foo module
 */

class fooModule extends genericModule
{
	public function __construct(& $modSys)
	{
		parent::__construct($modSys);
		$this->module = 'foo';
	}

	/**
	 * Check if module is usable (dependencies, etc.)
	 */
	protected function __public_init($data)
	{
		return array('success' => true);
	}

	/**
	 * Get module info
	 */
	public function getInfo()
	{
		return array
		(
			'title' => _('Foo'),
			'author' => 'patrys [ patrys at icenter pl ]',
			'version' => '1.0',
			'description' => _('Hello world example'),
			'modified' => filemtime(__FILE__)
		);
	}
}

?>

Do tego dochodzi klasa dla panelu administracyjnego (dla oszczędzenia pamięci, ładowana tylko w przypadku, gdy jest to aktywny moduł w panelu):

<?

/* $Id: foo.panel.module.php 1 2007-04-27 12:00:00Z patrys $ */

class fooPanelModule extends fooModule
{
	protected function __internal_list()
	{
		return 'Hello world!';
	}
}

?>

Jest tu kilka magicznych metod — wszystkie __public_* dostępne są automatycznie z poziomu inych modułów i z poziomu szablonów Smarty; __internal_* za to służą za obsługę ekranów panelu administracyjnego, wymagana jest przynajmniej obsługa metody list, która jest domyślnym widokiem.

Inne wtyczki — ręczna rejestracja

W przypadku wtyczek pól dodatkowych i systemów płatności okazało się, że automatyczna rejestracja nie jest najlepszym wyjściem — jedna klasa może realizować kilka funkcji, a sztuczne po niej dziedziczenie wprowadza tylko chaos w plikach. Zdecydowaliśmy się, że moduły te same muszą zgłosić obsługiwane przez siebie funkcje:

<?

/* $Id: platnosciPayment.class.php 7234 2007-04-25 10:55:39Z patrys $ */

class platnosciPaymentType extends genericPaymentType
{
	public static $paymentList = array
	(
		'platnosci_test' => array
		(
			'code' => 't',
			'name' => 'Platnosci.pl (płatność testowa)',
		),
		'platnosci_mtransfer' => array
		(
			'code' => 'm',
			'name' => 'Platnosci.pl - mBank mTransfer',
		),
		// [...]
		'platnosci_credit' => array
		(
			'code' => 'c',
			'name' => 'Platnosci.pl - Karta kredytowa',
		),
	);

	public function getConfigData()
	{
		// [...]
	}

	public function process()
	{
		// [...]
	}
}

$parent = paymentSystem::getInstance();
foreach (platnosciPaymentType::$paymentList as $key => $val)
	$parent->register($key, 'platnosciPaymentType', $val['name']);
$parent->registerSettings('platnosci', 'platnosciPaymentType', _('Platnosci.pl'));

?>

Dzięki temu możliwa jest obsługa wszystkich sposobów zapłaty za pomocą jednej klasy (konstruktor klasy genericPaymentType otrzymuje i zapisuje wybraną formę zapłaty).

Ładowanie wtyczek

Tutaj poszliśmy na łatwiznę:

public function loadPlugins()
{
	$modDir = dir(THE_ROOT . '/payment/');

	while (($file = $modDir->read()) !== false)
	{
		$fullPath = THE_ROOT . '/payment/' . $file;

		if (!is_dir($fullPath))
		{
			include($fullPath);
		}
	}
}

Ładowanie wtyczek z automatyczną rejestracją wymaga nieco więcej zachodu (po załadowaniu pliku, trzeba jeszcze sprawdzić, czy istnieje klasa o odpowiedniej nazwie i czy dziedziczy po odpowiedniej klasie bazowej), ale samodzielna implementacja nie powinna zająć więcej niż 5 minut.

Na koniec

Po notce o zmianie firmy pojawiły się niejasne spekulacje, jakobyśmy mieli zastąpić nbw butelką wody mineralnej. Spieszę z dowodem, że młodzieniec ów żyje i ma się dobrze (a jeśli już mielibyśmy kogoś zamienić na butelkę wody, to byłby to prędzej sit0).

The Crew
Ekipa, od lewej:
patrys, sit0, nbw, emes
jarv

Django: nie róbcie tego w domu

Wczoraj byłem zmuszony szybko dodać dodatkową funkcjonalność do jednej z rozwijanych przez nas aplikacji. Widok, o który chodziło opiera się na automatycznym, scaffoldowanym widoku administratora. Brzmi prosto?

Problem polegał na tym, że dostosowywanie logiki administratora jest dopiero w fazie planów (gałąź admin-newforms). W związku z tym, rozbudowanie go o własną logikę jest teoretycznie na tym etapie niewykonalne.

Przekopałem się więc przez większość kodu modułu django.contrib.admin i okazało się, że można Django nieco oszukać. Widoki są funkcjami, z czego korzysta się często przy generic views. Nie można po nich dziedziczyć, ale można wołać jeden z drugiego.

Brzydki hack

Do pliku urls.py na samym początku (przed sekcją admin) dopisujemy:

(r'^admin/aplikacja/model/add/', 'serwis.aplikacja.admin_views.add_stage'),

Następnie zakładamy plik serwis/aplikacja/admin_views.py:

from django.contrib.admin.views import main

def add_stage(request, *args, **kwargs):
	post = request.POST.copy()
	if request.method == 'POST':
		post['user'] = request.user.id
	request.POST = post
	return main.add_stage(request, 'aplikacja', 'Model', *args, **kwargs)

Voila!

Parę słów wytłumaczenia

Pierwsza część jest chyba oczywista, w tym przypadku nadpisujemy widok dodawania dla konkretnego modelu. Ponieważ Django przetwarza mapę rutingu w takiej kolejności, w jakiej są podane regułki, nasza linijka przesłoni znajdujące się niżej automatyczne reguły wbudowanego panelu administratora.

Druga część to utworzenie osobnego pliku dla widoków administracyjnych (nie musi to być osobny plik, ale to dobra praktyka). W pliku tym umieszczamy definicję własnego widoku add_stage.

W powyższym przykładzie dodawany jest automatycznie właściciel dla tworzonej instancji modelu. Nie bardzo można zrobić to w samym modelu, bo model jest bezstanowy i nie ma dostępu do stanu aplikacji. Poza tym, chcemy, żeby pole to było ustawiane tylko w tym przypadku.

Obiekt request.POST jest domyślnie immutable, więc tworzymy jego kopię i właśnie tę kopię modyfikujemy, a następnie podmieniamy oryginalny obiekt.

Ostatnia linijka powoduje wywołanie oryginalnego, magicznego widoku administratora, dostarczonego przez Django. Z tą różnicą, że przekazujemy do niego nazwę aplikacji i modelu. W przypadku wywołania bezpośredniego, pochodzą one z wyrażenia regularnego w konfiguracji urls.py, tutaj musimy podać je jawnie.

PHP bezpieczniej

W ramach eksternistycznego zaliczenia kursu PHP na studiach, prowadzący zaoferował mi przygotowanie krótkiej prezentacji o języku. Prezentacje nie są tym, co programiści lubią najbardziej. Zamiast prezentacji wyszedł krótki tekst, a zamiast możliwości języka są błędy ludzi.

Całość zamieszczam i tutaj, może się komuś na coś przyda. O ile wierzę w swoją wiedzę, to zastrzegam z góry, że praca nie była jeszcze oceniana. Przepraszam też za wielkość, ale to AbiWord taki duży plik wygenerował :)

Pobierz PHP i aplikacje WWW (290 kB, PDF)

Zanim zaczniesz tworzyć webaplikacje

Bez spełnienia poniższych punktów, nawet nie zabieraj sie za pisanie niczego, do czego mają mieć dostęp ludzie. W szczególności nie zabieraj się też za pisanie CV do nas ;)

  • potrafisz rozwinąć skrót RDBMS i własnymi słowami powiedzieć, jaką rolę pełni
  • w odpowiedzi na powyższe nie użyłeś sformułowania w który mogę rzucać dowolnymi śmieciami, a potem je wyjąć
  • potrafisz określić, czym jest MVC albo potrafisz powiedzieć, czemu nie należy umieszczać kodu logiki biznesowej w kodzie prezentacji
  • nie musiałeś przed chwilą sprawdzić w Google, czym się różni logika biznesowa od logiki bazodanowej i logiki prezentacji
  • trzecia postać normalna nie kojarzy ci się ze Wstępem do psychoanalizy Freuda
  • zapytany o indeksy, nie zastanawiasz się, czy dziekan już podpisał
  • wiesz, że klucze obce nie służą do otwierania mieszkań co ładniejszych koleżanek
  • zapytany o HTML nie rozwijasz skrótu jako How to meet ladies, a przynajmniej nie na rozmowie kwalifikacyjnej
  • zdajesz sobie sprawę, że baza danych jest środowiskiem wielodostępowym, rozumiesz działanie transakcji i pojęcia typu race condition (trudne słowo)
  • na dźwięk skrótu JS nie piszesz odruchowo <SCRIPT LANG="JAVASCRIPT"><!-- document.write("Hello") --></SCRIPT>
  • potrafisz powiedzieć, czym jest OOP, kiedy go używać i kiedy jest to overkill (trudne słowo)
  • znasz przynajmniej jeden używany jeszcze dialekt SQL i potrafisz cytować przekazywane do niego parametry w taki sposób, aby zapytania mogli pisać tylko deweloperzy, a nie odwiedzający serwis

Internet Explorer: System error -1072896658

Właśnie z sit0 zabieraliśmy się za spędzenie drugiego dnia nad kolejnym dziwadełkiem, które występowało w naszej ukochanej przeglądarce. Spora część ajaksowej funkcjonalności naszych zabawek zaprzestała pełnienia swych funkcji w IE.

Zaczęliśmy tradycyjnie, od zabawy z Fiddlerem i upewnienia się, że zapytanie trafia do serwera prawidłowo i że serwer generuje prawidłową odpowiedź. Pudło, trzeba pobawić się z JS. Debugowanie tego ostatniego w IE jest zabawą tyle niewdzięczną, co czasami graniczącą z absurdem. Po kilku godzinach zabawy z wstawianiem kodu kontrolnego w różne etapy przetwarzania odpowiedzi (rozoraliśmy całą bibliotekę script.aculo.us), zostaliśmy dalej z niczym. Mieliśmy tylko jeden wyjątek, który nie miał kompletnie sensu. Cała jego zawartość to System error -1072896658.

Zbawienie przyszło z Google: ktoś miał już identyczny problem i okazało się, że rozwiązanie stosuje się także w naszym przypadku. Jeśli więc IE zachowuje się dziwnie i nie działają w nim wywołania AJAX, sprawdź zawsze nagłówki. IE jest bardzo wybredny i bardzo małomówny.

Technorati Tags: , , ,

Internet Explorer: Pomocy!

Kolejny dzień spędzamy na poszukiwaniu przyczyny chorego zachowania IE6.

Efekt jest taki, że losowo przerywa przetwarzanie dokumentu, a efektem jest jedynie fragment drzewa DOM. Co ciekawe, przy każdym odświeżeniu strony, utyka gdzie indziej. Próbowałem to rozdłubać nawet Fiddlerem. Ten ostatni pokazuje też, że IE pyta serwera, czy grafiki się zmieniły, otrzymuje odpowiedź 304, po czym dziarsko uznaje, że grafiki nie da się załadować.

W każdej innej przeglądarce, z IE7 włącznie, serwis działa poprawnie.

W życiu takiego cyrku nie widziałem. Pomoże nam ktoś?

Update: chłopaki z Janmedia zdiagnozowali problem — winnym okazał się serwis Google Video i wtyczka do Flasha. Otóż po załadowaniu odtwarzacza googlowego, IE przestawał ładować cokolwiek więcej. Czekamy jeszcze na potwierdzenie, czy to jedyna przyczyna.

Encje w HTML

Na prośbę Roya:

(X)HTML w skrócie definiuje dokument jako zestaw elementów. Każdy element to albo textNode, albo kontener — typowy element, jak nagłówek, paragraf, czy span, który zawiera kolejne elementy.

Elementy są czasem błędnie nazywane tagami, podczas gdy tagi to sposób tekstowego zapisu elementów — składają się wtedy z tagów otwierającego i zamykającego (albo jednego samozamykającego w przypadku elementów, które nie zawierają żadnych dzieci).

Wszytkie elementy, poza tekstowymi, posiadają różne atrybuty. Te ostatnie zapisuje się jako listę par nazwa="wartość" wewnątrz tagu otwierającego element. Teraz ważne: ponieważ HTML jest aplikacją SGML (trudne słowo), a XHTML jest aplikacją XML (jeszcze trudniejsze słowo), wartości atrybutów w zapisie trzeba cytować za pomocą encji.

Ponieważ HTML definiuje encji masę, a XHTML dziedziczy ich po XML tylko pięć, to cytować powinno się tylko te ostatnie (inaczej nie można przetwarzać wyniku jako dokumentu XML).

Oto więc lista kandydatów: &amp;, &lt;, &gt;, &quot;, &apos;.

Ponieważ w PHP funkcja html_entities jest mocno nadgorliwa, poniżej prosta implementacja poprawnej alternatywy:

function xmlentities($string)
{
	return str_replace(array
		(
			'&', ''', '"', '<', '>'
		), array
		(
			'&amp;', '&#039;', '&quot;', '&lt;', '&gt;'
		), $string);
}

Apostrof jest traktowany specjalnie, bo w HTML go… nie przewidzieli.

Technorati Tags: , , , ,

Dlaczego systemy szablonowe są ważne

Masa ludzi zadaje mi ciągle to samo pytanie — skoro PHP został stworzony jako język prezentacyjny, to jaki jest sens implementowania w nim kolejnego języka prezentacyjnego — systemu szablonów?

To prawda, że PHP powstał właśnie jako język do osadzania wewnątrz kodu HTML. Prawdą jest, że do dzisiaj mnóstwo ludzi w ten właśnie sposób go używa. Nic dziwnego, podczas wczorajszej wycieczki do Empiku, natknęliśmy się z Qwiatem na stustronicową książeczkę o wdzięcznym tytule PHP — to proste.

Już nie tylko liczniki

Sam język jednak przeszedł od czasów wersji pierwszej ogromne zmiany, największe z nich miały miejsce pomiędzy trzecim i piątym wcieleniem. Skupiają się one na umożliwieniu łatwego budowania dużych aplikacji. Aplikacji, gdzie mieszanie warstwy widoku i kontrolera jest co najmniej wysoce niewskazane, ze względu na czytelność i łatwość zarządzania kodem.

Minęły czasy, kiedy języków server-side używało się do budowania liczników. Często całość treści jest generowana właśnie przez kod. Tyczy się to tak samo PHP, jak każdego innego języka, który może działać po stronie serwera. Zmieniły się oczekiwania, wzrosła skala projektów, logika wewnątrz strony przestała wystarczać.

Ktoś powie zaraz, że przecież można oddzielić warstwę widoku i nadal korzystać tam z PHP, tak jak robi to CakePHP. Można, czasem jednak nie warto. Cake powstał jako klon uznanego frameworku Ruby on Rails. Oba celują w projekty rozwijane — przynajmniej w pierwszych stadiach życia — metodą programowania ekstremalnego. Koncentrują się na jak łatwiejszym zbudowaniu proof-of-concept i dalszym jego rozwoju, już po odkręceniu dodatkowych kółeczek od roweru.

Serwisy budują projektanci, a nie programiści

Oczywiście, są aplikacje, przy budowie których pracują prawie wyłącznie programiści. Niech będzie to Google Mail, YouTube, czy Wrocek. Prawda jest jednak taka, że większość komercyjnych serwisów internetowych jest budowana dla konkretnego klienta przez agencje interaktywne.

Ich ceny oscylują w przedziale od kilku do kilkunastu/kilkudziestu tysięcy i zyski z tego typu działalności ciężko przeznaczyć na zatrudnienie połowy Microsoftu. Dlatego też do zadań programisty często należy tylko przygotowanie silnika, zaś resztę pracy wykonują projektanci — webmasterzy lub graficy, w których gestii jest przygotowanie odpowiedniego kodu HTML, arkuszy CSS i połączenie tego w całość — produkt, który można oddać klientowi.

Co z tego wynika? Ano wynika tyle, że przy zatrudnianiu ważniejsza jest doskonała znajomość obu wyżej wymienionych technologii, niż zdolności programistyczne. Nie każdy webmaster zna PHP (Javę, Pythona, platformę .NET). Smarty można opanować w ciągu jednej doby, to zdecydowanie za mało na poznanie choćby części filozofii projektowania zorientowanego obiektowo.

This sit3 haxx0rd by scr1pt kiddi3z

Wyżej wspomniałem, że nie każdy jest programistą. Praktyka uczy też, że nie każdy powinien. Miałem w życiu wiele sytuacji, w których zmuszony byłem współpracować z ludźmi, którzy zdawali się całą swoją wiedzę pozyskać ze wspomnianej empikowej książeczki. Dość powiedzieć, że podatność na SQL injection to najlżejsze z popełnianych przewinień.

Popularne grzechy to ślepe include‘owanie pliku, którego nazwa trafiła przez wywołania GET lub POST (najczęstsza, poza korzystaniem z register_globals, przyczyna ataków XSS), trzymanie ważnych danych w ciasteczkach przeglądarki, rekordzista umieścił tam nawet całe zapytania SQL. Z pewnością wystarczyłoby na wypełnienie kalendarza Daily WTF na najbliższy rok.

Silniki szablonów mają tę ogromną zaletę, że zapewniają piaskownicę (sandbox), w której można bezpiecznie wywołać kod nieznanego pochodzenia (czytaj: cudzy kod). Nazwa pochodzi od terminu, jakim saperzy nazywają specjalne pojemniki i pomieszczenia do badania i rozbrajania znalezionych przedmiotów, gdzie ewentualna eksplozja nie naraża niczyjego życia.

W tym przypadku niczyje życie nie jest bezpośrednio zagrożone, ale ciężko wypracować sobie reputację, kiedy co drugie wdrożenie twojego silnika kończy się włamaniem na serwer. Korzystanie w przypadku prezentacji z prostego języka, zbudowanego specjalnie do tego celu, daje pewność, że nawet najmniej kompetentny człowiek nie będzie w stanie nic zepsuć.

W zasadzie tyle chciałem napisać, ale może ktoś ma zupełnie inny pogląd na te sprawy? Z góry zapowiadam, że nie interesują mnie argumenty na poziomie Smarty to nieakceptowalny narzut rzędu dziesiątej części sekundy na każdą wyświetlaną stronę.

The Daily WTF, part 5

Jeden z klientów zapytał nas, czy możemy zbudować dla niego system, który pozwoli wybrać poszczególne sekcje z broszury informacyjnej, a następnie wygeneruje dla użytkownika jeden plik PDF z całą wybraną treścią.

Jako przykład podał nam brytyjskiego usługodawcę, który podobne systemiki sprzedaje ich anglojęzycznym kolegom. A strona jego wprawiła nas w zachwyt niemały, a radości pomniejszym i większym końca nie było. Zwłaszcza, kiedy oglądaliśmy źródła ichnich skryptów ASP.

Wystarczyło bowiem zajrzeć w kod HTML, żeby znaleźć taki oto fragment:

<input type=button value="  DOWNLOAD NOW  "
onclick="javascript:test('C:/webroot/LocalUser/br4589/Website/files/34/userfiles/brochure_197866219.pdf');" >

Piękne, zwłaszcza, kiedy nieco niżej, bo u dołu dokumentu znaleźliśmy to cudeńko:

function test(atch){
	window.location.href="download.asp?file="+atch+"";
	}

Ciekawskim od razu odpowiadam, że tak, można w ten sposób pobrać boot.ini, pliki z katalogu z Windowsem oraz kod całego serwisu. Pogratulować pomysłowości autorom.

Technorati Tags: , , ,

MS Phishing Explorer

Taka drobna ciekawostka dla fanów Domyślnej Przeglądarki. Znaleziona w raporcie dotyczącym błędu z 2005. roku. Okazuje się, że to, co pojawia się w pasku stanu IE, nie ma nic wspólnego z tym, dokąd trafisz po kliknięciu odnośnika.

Prosty przykład:

<form action="http://7thguard.net/" method="get">
	<a href="http://google.com/"><input type="image" src="http://www.google.com/intl/en/images/logo.gif" alt="google.com" /></a>
</form>

Do pobawienia się przykład phishingu w IE. Tak, działa z każdym Service Packiem.

Technorati Tags: , , ,