Jak stworzyć element “wystający” z komórki tabeli

Wyobraźmy sobie, że mamy potrzebę wstawienia do komórki tabeli pewnej treści, która:

1) ma się znaleźć dokładnie w miejscu, gdzie znajduje się interesując nas komórka tabeli
2) nie powinna rozciągać komórki w której się znajduje
3) jednocześnie powinna być widoczna w całości bez zawijania tekstu.

Potrzeba ta nie została wymyślona “z kosmosu”, ale wynikła w faktycznej realizacji pewnego zadania. Jednym z rozwiązań mogłoby być pozycjonowanie za pomocą Javascript elementu <div>, tak aby znalazł się w wskazanym miejscu – ale to dosyć karkołomne. Da się to zrobić za pomocą styli, i to całkiem prosto jak się okazuje:

<table cellspacing="0">
<tr><th>ID</th><th>Nazwa</th><th>Ilość</th><th>Status</th></tr>
<tr>
	<td>1</td>
	<td>Lorem ipsum<br><br>
	<div style="position:absolute;"><div style="position:relative;top:-1em;"><span>Edytuj | Usuń | Ukryj | Przelicz | Rezerwuj | Zwolnij</span>
	</div></div>
	</td>
	<td>50 sztuk</td><td>Dostępny</td>
</tr>
</table>

table

Przedstawiony powyżej efekt można by oczywiście osiągnąć za pomocą dodatkowego wiersza tabeli i rowspan/colspan – jednak, w wspomnianym przypadku, nie było to zalecane, ze względu użycie funkcjonalności drag&drop na wierszach tabeli. :)

Dla porządku cały kod źródłowy przykładowej strony:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
        "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-2" />
<style type="text/css">
	table {
		width:350px;
		border:1px solid #000000;
		font-size:12px;
	}
 
	th {
		padding:5px;
		background:#777;
		color:#ffffff;
		text-align:left;
	}
 
	td {
		vertical-align:top;
		border-bottom:1px solid #000;
		padding:5px;
	}
 
	span {
		font-size:10px;
		color:#0033bb;
	}
</style>
</head>
<body>
 
<table cellspacing="0">
<tr><th>ID</th><th>Nazwa</th><th>Ilość</th><th>Status</th></tr>
<tr>
	<td>1</td>
	<td>Lorem ipsum<br><br>
	<div style="position:absolute;"><div style="position:relative;top:-1em;"><span>Edytuj | Usuń | Ukryj | Przelicz | Rezerwuj | Zwolnij</span>
	</div></div>
	</td>
	<td>50 sztuk</td><td>Dostępny</td>
</tr>
<tr>
	<td>2</td>
	<td>Doloret Sit Amet<br><br>
	<div style="position:absolute;"><div style="position:relative;top:-1em;"><span>Edytuj | Usuń | Ukryj | Przelicz | Rezerwuj | Zwolnij</span>
	</td>
	<td>25 sztuk</td>
	<td>Zarezerwowany</td>
</tr>
</table>
 
</body>
</html>

Regexp: Wyszukiwanie i linkowanie adresów email

Jak w prosty sposób wyszukać w tekście i automatycznie podlinkować wszystkie adres email? Można to zrobić np. w ten sposób:

	$string = 'Wyszukaj wszystkie qifl@ufydsg "prawdziwie" adresy mail@zumba.pl email imejl.kobza@lolo.bolo.com i stwórz z nich HTMLowe linki.';
 
	$reg = '/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}/i';
 
	print preg_replace($reg, '<a href="mailto:${0}">${0}</a>', $string);

Użyteczność stron organizacji pomocy społecznej

Do napisania tego postu skłoniło mnie smutne doświadczenie:

Stwierdziłem, że posiadam sporo nieużywanych ubrań, których jedyną funkcją jest zajmowanie miejsca w szafie, a które z powodzeniem mogłyby być wykorzystane przez inne osoby. Żadna to wielka dobroczynność ani poświęcenie – zamiast jednak wyrzucać ubrania na śmietnik, pomyślałem żeby przekazać je którejś z wielu organizacji niosącej pomoc ubogim, jakie działają w moim mieście.

Zacząłem więc poszukiwania takiej organizacji poprzez internet. I doszedłem do kilku wniosków. :)

Wniosek pierwszy – wiele stowarzyszeń nie posiada swojej strony, a ta mniejszość, która je prowadzi, ma je bardzo słabo wypromowane. Szkoda, bo nie jest to grupa, w której trwa wojna SEO pozycjonerów, a więc bardzo łatwo byłoby zwiększyć widoczność takich stron w wyszukiwarkach. Wystarczyłby najprostszy zabieg, czyli prawidłowe opisanie strony w metatagach. Wiele z tych stron w tytule ma podaną jedynie nazwę organizacji lub nazwę domeny(!), a opis strony całkowicie pominięty lub powtórzony jest w nim tekst z nazwą stowarzyszenia. O słowach kluczowych już nie wspominając.

Wniosek drugi – nieprawidłowa hierarchia ważności informacji. Wyeksponowane miejsca zajmują informacje o statusie, prowadzonej działalności od strony prawnej, informacje statystyczne i ubiegłe osiągnięcia, kwestie administracyjne i kadrowe. Zakres pomocy i do kogo jest ona kierowana często schodzi na drugi plan. Najtrudniej dostępnym elementem, często całkowicie nieobecnym, jest informacja o tym w jaki sposób można wspomóc stowarzyszenie. Wygląda to tak, jakby te instytucje działały same dla siebie, niezainteresowane wsparciem z zewnątrz, ani nie poszukujące choćby takich altruistów jak ja. ;) To spostrzeżenie było punktem wyjściowym do napisania niniejszego tekstu – prawda jest taka, że do tej pory nie wiem komu mam przekazać ubrania, aby zostały one z pożytkiem wykorzystane.

Wniosek trzeci – technologia wykonania, estetyka i przejrzystość prezentowanych informacji, woła o pomstę do nieba. O ile sprawy estetyki można by prawie całkowicie pominąć, technologię wykonania również zlekceważyć – o ile tylko nie przeszkadza ona w wyświetlaniu strony w różnych przeglądarkach (a niestety tak nie jest, część z nich wyświetla się prawidłowo jedynie w przeglądarce IE6, w pozostałych prezentując mniejsze lub większe defekty) – to kwestie przejrzystości należy zdecydowanie napiętnować. Główny zarzut, to kiepska prezentacja tekstów: długaśne tasiemce, bez akapitów, bez wyróżnień, pisane małą czcionką (czasami baaardzo małą) lub dziwnymi kolorami.

Wniosek czwarty – przeterminowane aktualności – w 90% przypadków najświeższe informacje podawane w dziale z newsami, pochodziły sprzed 2 lat. To ponownie budzi pytanie, czy taka organizacja jeszcze istnieje, czy też może dawno zawiesiła swoją działalność, więc próby skontaktowania się z nią będą jedynie stratą czasu.

Wniosek piąty – dostępność stron dla osób niepełnosprawnych nie jest w ogóle brana pod uwagę. Na szczęście wiele z tych stron broni się poprzez banalnie prostą konstrukcję i nierozbudowaną zawartość. Z drugiej jednak strony część z nich, poprzez próbę bycia “nowoczesną” strona, staje się zupełnie nieprzyjazna dla osób z wadami wzroku czy upośledzeniami ruchowymi. Kiepskim żartem jest strona lokalnego oddziału Polskiego Związku Niewidomych, której całą zawartość stanowi aplikacja napisana we flashu.

Wniosek szósty – na tym nieciekawym tle, wyróżniają się strony miejskie (np. Miejskiego ośrodka pomocy społecznej) i stron organizacji blisko związanych z gminą. Mają one również swoje wad, ale nie odstają od średniej stron dostępnych w polskim internecie.

Zupełnym kuriozum okazała się strona stowarzyszenia, szeroko reklamowana na innych stronach tematycznych, na której po wejściu ukazał się jedynie napis “W związku z reorganizacją prawną oficjalna strona jest w przebudowie.”. Czy reorganizacja prawna spowodowała zawieszenie działalności? Nie, gdyż stowarzyszenie prowadzi aktualnie akcję związaną z świętami. Cóż zatem przeszkadza im publikować swoją stronę – oprócz kwestii, nazwijmy to, polityczno-decyzyjnych?

Zdaję sobie sprawę, że tego typu stowarzyszenia prowadzą swoją działalność minimalnym nakładem środków, a ich strony internetowe są tworzone przez wolontariuszy, którzy często nie dysponują szeroką wiedzą w temacie technologii internetowych. Sedno sprawy w tym, że za pomocą prostych działań można by zdecydowanie podnieść ich efektywność – w czym zresztą zamierzam im pomóc. :) Jutro prześlę do tych działających na terenie mojego miasta, klarownie opisane wskazówki, których wdrożenie nie powinno nastręczyć większych kłopotów. Do tego samego zachęcam i Was, taki drobny dobry uczynek, na te, przemijające już, święta Bożego Narodzenia.

jQuery, validate plugin a weryfikacja numeru NIP i PESEL

Napisałem dwa dodatkowe walidatory do pluginu jquery-plugin-validation, które sprawdzają poprawność numeru NIP oraz PESEL.

Najpierw metoda dodająca walidację numeru NIP:

jQuery.validator.addMethod("nip", function(value, element) {
var verificator_nip = new Array(6,5,7,2,3,4,5,6,7); var nip = value.replace(/[\ \-]/gi, ''); 
if (nip.length != 10)  { return false; } else  {
var n = 0;
for (var i=0; i<9; i++) {	n += nip[i] * verificator_nip[i]; }
n %= 11;
if (n != nip[9]) { return false; }
}
return true;	
}, "Proszę o podanie prawidłowego numeru NIP");

Wystarczy zrobić capy&paste powyższego kodu, do swojej aplikacji. A teraz PESEL:

jQuery.validator.addMethod("pesel", function(value, element) {
var pesel = value.replace(/[\ \-]/gi, ''); 
if (pesel.length != 11) { return false; } else {
var steps = new Array(1, 3, 7, 9, 1, 3, 7, 9, 1, 3); 
var sum_nb = 0;
for (var x = 0; x < 10; x++) { sum_nb += steps[x] * pesel[x];}
sum_m = 10 - sum_nb % 10;
if (sum_m == 10) { sum_c = 0; } else { sum_c = sum_m;}
if (sum_c != pesel[10]) {	return false;}
}
return true;	
}, "Proszę o podanie prawidłowego numeru PESEL");

Tak się zastanawiam, gdzie do diaska się podział plugin odpowiedzialny za kolorowanie składni? :) (odnalazł już się)

Proszę jednak mieć na uwadze, że żyją w Polsce ludzie posiadający nieprawidłowy, z punktu widzenia algorytmu, numer PESEL. Taki urok naszej administracji :) Dlatego warto błąd weryfikacji tego numeru, stosować do ostrzeżenia użytkownika, ale niekoniecznie do całkowitego zablokowania akcji.

I jeszcze przykład wykorzystania walidatora:

$("#orderform").validate({
    rules: {
       field_nip: {
           nip: {
               depends: "#field_nip:filled"
           },			    
           required: "#field_companyname:filled"
        }
    }
});

Powyższy przykład dokona walidacji formularza #orderform, i sprawdzi numer NIP wg następujących reguł:

1) Pole z numerem NIP (field_nip) musi być wypełnione w przypadku gdy podana zostanie nazwa firmy (field_companyname)

2) Jeżeli zostanie podany numer NIP, to sprawdź jego poprawność

Flash + Interner Explorer + SSL = #2032: Stream Error

Problem nie nowy, ale wciąż spotykany. Otóż próba komunikacji aplikacji flashowej z serwerem w bezpiecznym połączeniu HTTPS, w przeglądarce Internet Explorer 6 oraz 7 ma spore szanse zakończyć się niepowodzeniem:

#2032: Stream Error. URL: https://www.serwer.pl/internal_api.php

Tego typu błąd otrzymamy w aplikacji napisanej w ActionScript3. W starszej wersji 2 języka problem może być jeszcze bardziej irytujący, gdyż nie otrzymamy żadnego widocznego komunikatu o błędzie, ale nasz program też nie będzie działać.

Błąd występuje niezależnie od wersji flash playera. Odporny na ten przypadek jest Internet Explorer dopiero w wersji 8. Lekarstwem na bolączkę jest wysłanie w zasadzie dowolnej nieprawidłowej wartości dla nagłówka Pragma, np.:

header (”Pragma: internet-explorer-is-my-life”);

Co ciekawe, wysłanie nagłówka “Pragma: no-cache”, nie dosyć że powoduje opisany wyżej problem, to wcale nie zapobiega cachowaniu zawartości podczas połączenia bez szyfrowania SSL.

Zobacz także:

Nota techniczna Adobe
Powiązany błąd na stronie Microsoftu

Formalnie żyję

Ten wpis to raczej tylko taka desperacka próba dawania znaku życia. Wczoraj w nocy powstało kolejna strona “o niczym”, czyli bojkot sylwestrowej spinki! Czy ty też walisz sylwestrową spinkę? Mniejsza z tym :D, chciałem powiedziedzieć, że fenomenem facebookowego API, które użyłem po raz kolejny, jest jego nieustanna zmienność, nieaktulność i nieprzewidywalność. Zwłaszcza biblioteki dla ActionScript3.

spinka

Baza miast, gmin i powiatów Polski

Do kolejnego projektu potrzebna mi była pełna baza miejscowości w Polsce, łącznie ze wsiami, koloniami, przysiółkami, osadami i innymi folwarkami (tak, takie określenie również funkcjonuje :) ). Zdobycie takiej bazy okazało się całkiem proste, gdyż GUS udostępnia dane z TERYTu, czyli Rejestru Podziału Terytorialnego Kraju. Udostępniona baza w formacie XML zawiera pełną listę miejscowości wraz z ich przypisaniem do województwa, powiatu i gminy. Bazę można pobrać pod adresem http://www.stat.gov.pl/broker/access/prefile/listPreFiles.jspa.

Jest jednak mały problem z tą bazą – ze względu na jej wielkość (~40MB, ponad 1 200 000 linii) zaimportowanie jej w PHP z wykorzystaniem parsera XML potrafi zająć nawet kilka godzin. Ponieważ ja to już mam za sobą, więc zapraszam do pobrania pliku w formacie CSV, którego import do bazy danych trwa ledwie kilka sekund. W pliku tym nie ma wszystkich informacji dostępnych w oryginalnym XML – ze względu na moje potrzeby została okrojona do nazwy miejscowości, powiatu i województwa. Zapraszam do pobrania: plik zip (1MB)

Jak użytkownicy zapisują datę? Wyniki badania

William Hudson kilka miesięcy temu przeprowadził badanie preferowanych przez użytkowników formatów zapisu daty.  Z wynikami jego pracy można zapoznać się w artykule Dates in Interaction Design – Actual Research – generalnie wnioski sprowadzają się do tego, że nie istnieje jeden najlepszy format, a użytkownicy datę zapisują na różne sposoby. Jednak, jego badanie zostało przeprowadzone na międzynarodowej próbie, a mnie interesuje jak to wygląda wśród polskich użytkowników internetu. Ponieważ nikt jeszcze nie wykonał takiej analizy dla Polski, lub nie potrafię do niej dotrzeć, więc postanowiłem wykonać ją samodzielnie. Hudson zebrał 998 próbek, mi się udało do tej pory uzyskać 230 wyników. Ilość mniejsza, ale daje już pewien obraz sytuacji. Należy również zaznaczyć, że przyjąłem trochę inną formę pytania – w mojej ankiecie pytanie brzmiało “Podaje datę urodzenia”, co mogło mieć pewien wpływ na wyniki. Założyłem również, że w Polsce nikt nie podaje daty w kolejności miesiąc/dzień/rok, wobec czego wszystkie niejednoznaczne odpowiedzi (tzn. takie w których dzień był mniejszy niż 13) kwalifikowałem jako zapisane w kolejności dzień/miesiąc/rok. Dodatkowo pytałem się także o numer telefonu, aby sprawdzić w jaki sposób użytkownicy go zapisują. Rezultat badania prezentuję poniżej.

Wyniki - format zapisu daty

Jak widać w Polsce zdecydowanie najpopularniejszym formatem jest dd.mm.rrrr – 57% badanych użytkowników zapisało datę w takiej postaci. Co ciekawe, format skrócony stosuje tylko 17%, a zapis z dywizami jedynie 5% badanych. Przed wykonaniem badania uważałem, że najczęściej używanym formatem będzie zapis skrócony z kropkami dd.mm.rr.  Żadna z badanych osób nie zapisała daty w formie skróconej z dywizami dd-mm-rr, jak również w formie skróconej z ukośnikami dd/mm/rr. Za to aż 10% z nich użyło ukośników jako seperatorów wraz z pełnym zapisem roku dd/mm/rrrr.

Dodatkowo należy zaznaczyć, iż część osób pomijała zera wiodące w liczbach mniejszych od 10 – ale jedynie 3% dat z wszystkich, które miały liczby mniejsze od 10. Co ciekawsze, w badaniu Hudsona ponad 76%  respondentów nie stosowało zer wiodących. Czyżby polscy użytkownicy byli mniej leniwi? ;)

Wypływające z badania wnioski można ująć krótko: nie zmuszajmy internautów do wpisywania daty w jednym ściśle określonym formacie, ponieważ prawie połowa z nich stosuje inny format, niż dd.mm.rrrr. Aby jednak nie pozostawiać ich z wątpliwościami, czy prawidłowo wypełnili formularz, powinniśmy w locie parsować podaną datę i wyświetlać ją przetworzoną do klarownej postaci, np.: 20 września 1990. W przypadku błędnego odgadnięcia intencji użytkownika, będzie on miał możliwość reakcji i poprawienia daty. Oprócz tego warto stosować kalendarzyki pozwalające wybrać datę za pomocą myszki, jednak nie ograniczajmy użytkownika tylko do tej jednej możliwości. Wypełnianie z klawiatury jest często szybsze i wygodniejsze.

O wynikach badania numerów telefonów napiszę wkrótce.

Kto nie szyfruje ten trąba

Jak wiele serwisów, w których założyłeś konto, nie szyfruje twoich haseł?

Niewiele? Wiele? A może bardzo wiele? Wydawać by się mogło, że serwisy www przechowujące w swoich bazach jawnie zapisane hasła, to jedynie margines internetu. Bo któż, kto nie ma niecnych zamiarów, naraża swoich użytkowników na poważne niebezpieczeństwo?

Jednak po ostatnich aferach związanych z wyciekiem haseł na Wykopie, akcji “Bezpieczne konto” na Allegro czy sprawie związanej z dostępnością haseł na platformie Istore (pisał o tym E-Komers) okazuje się, że nawet poważni gracze pozwalają sobie na niepoważne zachowanie. Co więcej, okazuje się, że nie tylko ci najwięksi: podczas przeprowadzanego niedawno audytu średniej wielkości serwisu, natrafiłem w nim na podobną przypadłość. Serwis napisany stosunkowo poprawnie, zabezpieczony przed typowymi atakami, ale obsługa haseł zrealizowana wybitnie nieprawidłowo. I nie był to pierwszy raz, gdy stwierdziłem tego typu zaniedbanie. Tak więc poniżej krótkie przypomnienie dla projektantów, project managerów i inszych programistów. Ludzie, to są podstawy!

Zagrożenia jakie niesie za sobą przechowywanie haseł w formie jawnej:

1. Administrator serwisu, administrator serwera oraz prawie każdy programista zajmujący się utrzymaniem czy rozbudową serwisu otrzymują nieskrępowany wgląd w hasła użytkowników.

2. Istnieje możliwość wycieku haseł na wskutek włamania czy kradzieży bazy danych.

3. Istnieje możliwość wycieku haseł w wyniku błędu oprogramowania.

4. Istnieje możliwość wycieku haseł w wyniku błędnej konfiguracji serwera, np. gdy inny użytkownik bazy danych otrzyma uprawnienia do przeglądania cudzych zasobów.

5. Istnieje możliwość przechwycenia hasła wysyłanego w mailu – efekt nieprawidłowo zrealizowanej funkcji “Przypomnij hasło”.

Czy szyfrowanie haseł algorytmem dwustronnym zwiększa bezpieczeństwo?

Tak, ale tyle co nic. :) Jest wielce prawdopodobne, że włamując się na serwer hacker zdobędzie również informacje potrzebne do odszyfrowania haseł. Powiedzmy zresztą sobie jasno – w 99.999% przypadków serwisów webowych nie ma takiej potrzeby, aby hasło użytkownika mogło być odczytane w oryginalnej postaci. Nie ma i basta.

Jak mantrę należy więc powtarzać podstawowe zasady:

1. Hasła szyfrujemy algorytmem jednostronnym (generujemy skrót MD5 czy SHA) i tylko w takiej postaci przechowujemy je w bazie.

2. W celu zapobieżenia wykorzystaniu przez włamywacza Rainbow Tables w łamaniu haseł metodą Brute Force, przed utworzeniem skrótu hasła dodajemy do niego tzw. salt, czyli przypadkowy ciąg znaków przechowywany gdzieś poza samą bazą danych.

3. Nie wysyłamy hasła w poczcie email, nawet zaraz po rejestracji. Użytkownicy nie kasują takich wiadomości, przez co ich hasła stają się podatne na zdobycie w kolejny sposób (atak na skrzynkę pocztową).

4. Funkcję “przypomnij hasło” najbezpieczniej jest zrealizować poprzez generowanie unikalnego linku (z trudnym do odgadnięcia identyfikatorem – np. z użyciem funkcji uniqid w PHP), którego ważność jest ograniczona czasowo do np. 3 godzin. Wygenerowany link wysyłamy na adres email zarejestrowany w serwisie. Po jego kliknięciu użytkownik uzyskuje możliwość wpisania nowego hasła do swojego konta.