[PHP] Wyłączanie komunikatów notice

Problem stary jak świat. Po odpaleniu aplikacji (często na innym serwerze) wyskakuje szereg komunikatów typu notice (podkreślmy od razu bardzo ważną rzecz: notice to nie błąd). W tym wypadku przedstawiane są dwie szkoły: jedna mówi, że aplikacja nie powinna generować żadnych komunikatów, a druga uznaje, że skoro notice błędem nie jest, to może wystąpić, bo to tylko informacja dla programisty. Niezależnie od tego, którą z nich wyznajemy, ukrycie notice (jak i  błędów) jest konieczne w środowisku produkcyjnym, ze względów bezpieczeństwa.

Wykonanie tego zadania sprowadza się do dodania jednej linijki gdzieś na początku pliku PHP.

<?php
error_reporting(E_ALL ^ E_NOTICE);

To sposób jednorazowy/doraźny. Jeżeli chcemy załatwić sprawę na stałe i mamy taką możliwość to po prostu edytujemy plik php.ini i tak jak wyżej zmieniamy wartość dyrektywy error_reporting na E_ALL ^ E_NOTICE.

PS: Wiem, ze problem jest oklepany, ale będzie gdzie odsyłać kolejnych ludzi mówiących o "błędach notice".

IronCMS2 beta3 i informacje

Zgodnie z ostatnimi zapowiedziami projekt CMS-a (wbrew pozorom) nie został porzucony. Poza wydaniem kolejnej bety, która naprawia poprzednio znalezione błędy i dodaje trochę nowych funkcjonalności chciałem wyjaśnić kilka kwestii natury organizacyjnej.

Po pierwsze, coś co może Was lekko zasmucić (choć, serio, nie powinno :D). Z powodu moich niezbyt wyrafinowanych umiejętności i pewnych specyficznych wymagań jakie postawiłem przed systemem, a także uwarunkowań licencyjnych związanych z szablonem, CMS nie zostanie na razie wydany publicznie. Po drugie: ponownie nadszedł czas na podziękowanie grupie świetnych testerów i kolegów, którzy pomagają mi w realizacji całego tego przedsięwzięcia. Podziękowania lecą , alfabetycznie, do:

Jeżeli o kimś zapomniałem to proszę bić :D W tej wersji ponownie poprawiono ponad 50 zgłoszeń odnośnie błędów i funkcjonalności. Jeżeli ktoś z Was chciałby dołączyć do grona testerów, to proszę o skorzystanie z zakładki "Kontakt".

Mam nadzieję, że kolejna wersja będzie już stabilna, wtedy na pewno przedstawię więcej informacji.

Pozdrawiam :)

[Wordpress] Usuwanie zbędnego kodu HTML (WLW, RSD, generator)

Każdy, kto korzystał z WordPressa i ma w zwyczaju patrzeć jak jego strona wygląda "pod maską", z pewnością zauważył, że tytułowy CMS nie generuje najlepszego jakościowo kodu. Wynika to głównie z jego rozszerzalności i uniwersalności, ale nie oznacza to, że w prosty sposób nie możemy pozbyć się części nadmiarowego kodu HTML i CSS.

Usuwanie odwołania do RSD (Really Simple Discovery)
Mowa o następującym kodzie w nagłówku strony:

<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://example.com/xmlrpc.php?rsd" />

Jest on używany przez klienty protokołu XML-RPC, np. Windows Live Writer. Jeżeli nie używasz go lub wspomniane nazwy po prostu nic Ci nie mówią, prawdopodobnie możesz bez obaw usunąć ten kod ze swojej sekcji head.

Jak to zrobić? To bardzo proste, dzięki wbudowanemu w WordPressa systemowi akcji, który pozwala także na ich usuwanie. Najlepszym sposobem będzie dodanie następującego kodu na koniec pliku functions.php w katalogu Twojego szablonu (wp-content/themes/Twoj_Theme/functions.php). Jeżeli plik o tej nazwie nie istnieje we wspomnianej lokalizacji, należy go utworzyć i wstawić poniższy kod w jego treść:

<?php
remove_action('wp_head', 'rsd_link');

Usuwanie odwołania do WLW (Windows Live Writer)
Odpowiada za niego następujący kod HTML w nagłówku bloga:

<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://example.com/wp-includes/wlwmanifest.xml" />

Jego przeznaczenie jest takie jak powyższego, co widać dokładnie po nazwie :) Jeśli chcesz go usunąć, to analogicznie do functions.php w katalogu Twojego szablonu (więcej wyżej) dodaj kod:

remove_action('wp_head', 'wlwmanifest_link');

Usuwanie wersji WordPressa (meta generator) Mówię oczywiście o tym:

<meta name="generator" content="WordPress X.X.X" />

Ten kod jest najprawdopodobniej zbędny. Skorzystać z niego będą prawdopodobnie tylko roboty przeszukujące sieć pod kątem starych wersji WordPressa, które będą podatne na ataki. Nic nie stoi więc na przeszkodzie, aby i jego usunąć. Na koniec wspomnianego pliku functions.php dodajemy kod:

remove_action('wp_head', 'wp_generator');

Linki relacyjne i nawigacyjne do wpisów
Wiele blogów generuje kod na kształt poniższego:

<link rel='index' title='Strona główna' href='http://example.com' />
<link rel='start' title='Pierwszy wpis' href='http://example.com/pierwszy-wpis/' />
<link rel='prev' title='Post poprzedni' href='http://example.com/post-poprzedni/' />
<link rel='next' title='Post następny' href='http://example.com/post-nastepny/' />

Na początku warto zaznaczyć, że ten kod może być często przydatny, korzystają z niego niektóre przeglądarki, aby ułatwić nawigację pomiędzy kolejnymi artykułami na stronie (np. Opera). Jeśli jednak uważasz, że takie rozwiązanie jest Ci niepotrzebne, możesz je usunąć dodając do functions.php:

remove_action('wp_head', 'start_post_rel_link'); // Odniesienie do pierwszego wpisu
remove_action('wp_head', 'index_rel_link'); // Odniesienie do strony głównej
remove_action('wp_head', 'adjacent_posts_rel_link'); // Odniesienia do postów sąsiednich (poprzedni, następny)

Cały plik functions.php
Czyli opcja dla leniwych

<?php
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'start_post_rel_link');
remove_action('wp_head', 'index_rel_link');
remove_action('wp_head', 'adjacent_posts_rel_link');

Wyjaśnienie na koniec
Możesz też sobie zadać pytanie: dlaczego nie usunąć podanych fragmentów wprost z kodu WordPressa zamiast używać funkcji remove_action()? Odpowiedź jest bardzo prosta: przedstawione wyżej rozwiązanie nie narusza w żaden sposób rdzenia skryptu, w związku z czym zmiany nie zostaną utracone np. przy aktualizacji. Umożliwia też ono łatwe cofnięcie zmian - wystarczy usunąć odpowiednie wywołanie funkcji remove_action()

Mam nadzieję, że przedstawiona krótka porada przyda się Wam. Zawsze to zaoszczędzone kilka bajtów transferu :)

IronCMS 2 beta2

To znowu ja, powracający po chwilowej ciszy jaka zapadła na moim blogu. Można odczuć wrażenie, że ostatnie wpisy robią się lekko monotematyczne i w sumie jest to zgodne z prawdą. Bowiem dziś, równo po dwóch tygodniach od wydania na świat pożarcie testerom pierwszej bety IronCMS 2… pojawia się druga beta.

Wersja ta niesie ze sobą ponad 50 poprawek i nowych możliwości w odniesieniu do poprzedniej. Jestem bardzo zadowolony z pierwszych betatestów - pozwoliły mi wyłapać dużo błędów i zasugerować sporo zmian na lepsze. Nie wszystko zostało jeszcze ukończone, ale widzę duży progress względem poprzedniej wersji.

Właśnie dlatego chciałem znów złożyć małe podziękowania, należą się one (w kolejności ilości zgłoszonych błędów/propozycji tym razem): Kadetowi, Volixowi, Rapidwolfowi, Soanvigowi i Rodkanowi.

Nie ma co przeciągać. Osoby chętne do testowania zapraszam do kontaktu (zakładka w menu lub komentarz pod tym wpisem), odezwę się na podany adres email i pociągniemy ustalenia dalej.

Do usłyszenia! :)

Beta IronCMS 2!

Wszelkie wiadomości związane z nową wersją IronCMS (seria 2.x) na moim blogu ostatnio przycichły, można było się domyślać, że jest to kolejny projekt, z jakim rozmyśliłem się po kilku tygodniach pracy (na moim blogu można znaleźć takich kilka ;)). W tym momencie mogę jednak (z nieskrywanym zadowoleniem) ogłosić, że właśnie ukończyłem pierwszą betę IronCMS 2.

Ciężko mi nawet określić ile czasu nad nią pracowałem. Pamiętam, że minimum 2 razy porzucałem cały dotychczas napisany kod, aby rozpocząć od nowa (raz na dalekim etapie). Nie potrafię też powiedzieć czy zabieg się opłacił - widzę jednak efekt. Można powiedzieć, że skrypt jako tako działa. Na pewno jest w nim dużo błędów, ponadto trzeba wziąć pod uwagę, że w tej wersji umyślnie wielu rzeczy nie skończyłem - po prostu. Bo mogę :) Będę je dorabiał stopniowo, jak na razie jestem po prostu zadowolony, że udało mi się skończyć jakiś wyraźny etap.

Cyferki

Teraz nadszedł czas na troszkę nudnych liczb. Poniżej przedstawiam statystykę linii kodu źródłowego - z podziałem na moją własną robotę oraz pełny skrypt wraz z bibliotekami zewnętrznymi (mój skrypt korzysta m.in. z parsera BBCode od Wookieba oraz GeSHi do kolorowania składni).

Kod PHP (całość)
13 353 linijki kodu (dodatkowo 1472 puste linie i 2542 linie komentarzy) - łącznie 17 367 linii.

Kod PHP (własny)
4772 linie kodu (dodatkowo 724 puste linie i 134 linie komentarzy) - łącznie 5 360 linii.

Duża rozbieżność pomiędzy ilością kodu własnego i tego ze źródeł zewnętrznych wynika ze stopnia rozbudowania i elastyczności bibliotek - mówiąc inaczej: duża ilość kodu nie jest wykorzystywana obecnie przez CMS - przykład: kolorowania składni dla wielu języków zawartych w GeSHi).

Chciałem pokusić się też o statystykę dla kodu CSS/JavaScript, ale do stworzenia szablonu strony został użyty Twitter Bootstrap, więc sytuacja wygląda jak wyżej, po pierwsze: ogromna ilość kodu nie jest jeszcze (albo nigdy nie będzie) używana, a po drugie, ciężko było by mi rozdzielić ile z tego kodu to moje dodatki/modyfikacje, a ile jest dziełem autorów Bootstrapa/szablonu na nim opartego. Mogę tylko powiedzieć, że skrypt został uzbrojony w 8141 linii czystego kodu CSS, bez rozdzielania na autorów.

Testy

Zgodnie ze wcześniejszymi zapowiedziami, aktualnie kod źródłowy skryptu nie będzie dostępny publicznie. Nie chcę po prostu Was straszyć moimi wymysłami, dam sobie jeszcze trochę czasu na wprowadzenie wielu istotnych poprawek. Właśnie dlatego, w tej chwili rozpoczną się pierwsze betatesty IronCMS-a. Wszystkich zainteresowanych testowaniem zapraszam do kontaktu.

Screeny?

Czyli jak pokazać coś, tak, aby nie wyjawić nic :) (Kliknij na obrazek, aby przejść do pełnych wymiarów).

Strona główna w IronCMS 2 Panel administracyjny w IronCMS 2

Co my tu mamy? Na pierwszym screenie jawi nam się duże zdjęcie w topie (nie moje) i kawałek podstrony (moje), na drugim znajduje się panel administracyjny pokazany poprzednio, a więc jak najbardziej nic nowego.

Zakończenie?

Co na koniec? Jak zwykle podziękowania. Z tego miejsca podziękowania wędrują do (w porządku alfabetycznym, żeby mnie nie pogryziono): CapaciousCore, m4tx'a, Marcina, Rhino, Rodkana, Thelleo, Volixa i wszystkich innych, którzy pomagali mi na rozmaite sposoby w osiągnięciu celu.

IronCMS 2 - raport taktyczny

Zgodnie z zapowiedzią, dzisiejszy wpis będzie nakreślał aktualny stan prac nad IronCMS-em 2. Nie chciałbym, aby projekt sprawiał wrażenie porzuconego, bo tak zdecydowanie nie jest.

W wyniku kilku dyskusji i przemyśleń postanowiłem zmienić drastycznie jeden z aspektów skryptu – aktualnie jestem na etapie wdrażania prostego systemu szablonów na potrzeby CMS-a. Przyjął on formę znaną z wielu frameworków webowych (nie tylko PHP) i obecnych tam widoków. Mówiąc krótko i zwięźle – pliki szablonów są pisane z użyciem PHP (i w przyszłości kilku rozsądnych helperów ułatwiających pracę). Powodów, dla których odrzuciłem koncepcję użycia rozbudowanego systemu szablonów z dedykowaną składnią (np. Smarty, Twig) jest kilka, ale to może przedstawię przy innej okazji.

Co jeszcze zmieniło się w CMS-ie od ostatniego wpisu o nim? Przede wszystkim zostały ukończone kolejne kluczowe elementy – system komentarzy jest gotowy niemalże w połowie, funkcja bloga na której chciałem się skupić została już prawie ukończona. Skończone zostało dodatkowe kilka podstron, widocznych zarówno dla odwiedzającego stronę jak i dla administratorów. Gotowy jest praktycznie cały panel administracyjny.

Panel administracyjny IronCMS

Powyższy screenshot pokazuje z grubsza jego wygląd i układ. Wspomniany wcześniej system szablonów obejmie także panel administracyjny, tak więc nic nie będzie stało na przeszkodzie, aby przygotować alternatywne wersje panelu administracyjnego – np. wersję lite czy mobilną.

Aktualnie kluczowymi elementami do ukończenia są: system komentarzy,poprawki do parsera BBCode oraz wspomniany system zmiany szablonów. Po wykonaniu tej pracy ukaże się wersja beta, przeznaczona do skatowania przez grono zażartych testerów. Dopiero po tym rozpocznie się faza wdrażania mojego potworka na sobak.pl :)

Korzystając z okazji chciałbym pozdrowić Thelleo, który intensywnie rozwija swój system zarządzania treścią - LunaCMS.

A może tak… IronCMS 2?

Ostatnio znów zrobiło się ciszej z nowymi wpisami - tym razem powodem jednak nie było lenistwo, a przynajmniej nie w czystej postaci ;) Od dłuższego czasu pracuję nad nowym skryptem - tak tak, tytuł to nie żart - ci z Was, którzy pamiętają mój projekt o nazwie [IronCMS][portfolio-ironcms] mogą być lekko zaskoczeni, ale to prawda - aktualnie tworzę drugą wersję tego skryptu o jakże odkrywczej nazwie - IronCMS 2.

Różnic w odniesieniu do poprzedniej gałęzi będzie sporo - całkowicie napisany od nowa kod źródłowy, ale także kilka zmian w podejściu. Pierwsza i zasadnicza - skrypt nie będzie dostępny publicznie… przynajmniej na razie :) Przed ewentualnym wydaniem zostanie na pewno dobrze przetestowany i poddany próbie bojowej (już wkrótce więcej o tym). Do ewentualnego wydania na światło dzienne kod zachowam dla siebie, jednak będę się starał Was informować o ważniejszych zmianach w nim - to będzie także sposób na motywację dla mnie.

Wstępna data premiery wersji 2.0 jest już wyznaczona, jednak nie chcę jej jeszcze podawać, aby nie czuć się zobligowany żadnymi terminami. Przed premierą na pewno odbędzie się minimum 10 dni zamkniętych betatestów, w których poprawię najważniejsz znalezione błędy. Po premierze zaś, zbiorę wnioski wynikające po używaniu CMS-a w praktyce i niedługo po niej prawdopodobnie zacznę przygotowywać kolejną wersję z adekwatnymi poprawkami.

Jeśli chodzi o zastosowane rozwiązania, to od wydania ostatniej wersji IronCMS-a minęło prawie półtora roku (27.06.2011 - [wersja 1.2.1][ironcms-121]). Sądzę, że przez ten czas zebrałem jednak trochę nowych doświadczeń, z których będę mógł skorzystać przy swojej pracy.

Myślę, że dziś to będzie na tyle - nie chcę przeciągać tego wpisu bez potrzeby. Przed premierą powinien się ukazać jeszcze minimum jeden wpis o IronCMS-ie w wersji 2.0  - prezentacja screenów czy coś - zwyczajna marketingowa papka :D

Pozdrawiam.

[ironcms-121] [portfolio-ironcms]: https://sobak.pl/projects/iron-cms/

[PHP] Sprawdzanie typu (MIME) pliku

Witajcie po bardzo długiej przerwie. W wyniku kilku spraw nie mogłem znaleźć weny i czasu na naskrobanie czegoś na bloga. Nadszedł czas troszkę to nadrobić - zacznę, mam nadzieję, konkretnie.

Dziś na warsztat pójdzie sprawdzanie typu pliku na podstawie jego MIME. Jak dobrze wiadomo, sprawdzanie po rozszerzeniu jest niepewną i ryzykowną formą - rozszerzenie można zmienić, a nieraz (wskutek takiej czy innej konfiguracji serwera) można wykonać plik zamaskowany rozszerzeniem .jpg jako normalny plik PHP. Potencjalnych skutków chyba nie trzeba opisywać ;) Przechodząc do rzeczy:

1) Korzystając z klasy Fileinfo
To rozszerzenie PHP powstało właśnie do tego celu i jest aktualnie najlepszą metodą na osiągnięcie naszego celu. Poniżej krótki i treściwy przykład działania klasy.

<?php 
$file1 = 'image.png'; // Faktyczny obrazek w PNG
$file2 = 'kod.png'; // Plik PHP ze zmienionym rozszerzeniem

$finfo = new finfo(FILEINFO_MIME);

echo $finfo->file($file1, FILEINFO_MIME_TYPE); // Wyświetli: image/png
echo $finfo->file($file2, FILEINFO_MIME_TYPE); // Wyświetli: text/x-php

W tym miejscu już doskonale widać przewagę tego rozwiązania nad sprawdzaniem rozszerzenia pliku - oszust został zdemaskowany.

2)  Poprzez funkcję mime_content_type
Uwaga, ten sposób jest gorszy! Wspomniana funkcja została zdeprecjonowana, więc nie powinno się jej używać. Jednakże dosyć często zachodzi sytuacja, w której na serwerze nie jest dostępna pierwsza metoda (klasa fileinfo), a możemy użyć owej funkcji - dlatego o niej wspominam.

<?php
echo mime_content_type('php.gif'); // Wyświetli: image/gif

3) Sprawdzanie typu pliku po jego zawartości Każdy typ pliku zawiera na swoim początku zestaw charakterystycznych dla niego kilku bajtów. Sprawdzając je, możemy określić typ pliku - należy jednak wspomnieć, że to właśnie z tej metody korzystają powyższe rozwiązania, więc zabawę z ręczną jej implementacją możemy rozpocząć gdy jesteśmy zdesperowani i nie mamy żadnej z powyższych możliwości.

Poniżej krótki przykład, pobranie trzech pierwszych bajtów pliku, skonwertowanie ich do postaci heksadecymalnej i porównanie ich z bajtami charakterystycznymi dla pliku MP3

<?php
$file = fopen('plik.mp3', 'r');
$bytes = bin2hex(fread($file, 3));

if($bytes == '494433') {
    // Sprawdzany plik jest plkiem MP3
}
else {
    // Inny plik
}

fclose($file);

Dużą listę takich charakterystycznych bajtów możecie znaleźć tutaj, można też skorzystać z serwisu FileExt.com.

Mam nadzieję, że komuś przydał się ten wpis. Dziękuję za uwagę :)

WordPress Cleaner - kilka słów o skrypcie

Na bloga powrócił skrypt WordPress Cleaner. Część z Was może go jeszcze pamiętać ze starej wersji bloga. Po ostatnich zmianach postanowiłem go nie wrzucać, do momentu napisania lepszej wersji. I oto jest!

Skrypt służy do usuwania rewizji wpisów z bazy danych WordPressa. Rewizje to kopie zapisywane automatycznie przez edytor wpisów, są tworzone co jakiś czas, głównie po to, aby nie utracić wpisu po nieoczekiwanym zamknięciu strony edycji (np. poprzez brak zasilania i wyłączenie komputera lub po prostu przypadkowe wyłączenie przeglądarki).

W nowszej wersji uprościłem skrypt (wydaje mi się, że tak bardzo jak to możliwe) i poprawiłem kilka rzeczy.

Więcej informacji tutaj.

Blackout - podliczanie punktów i CRON

W pierwszym wpisie o Blackoucie – silniku gier strategiczno-ekonomicznych via WWW przedstawiłem zaledwie szkic moich notatek. Dlatego też, zgodnie z obietnicą postaram się przybliżyć bardziej jeden z aspektów.

Ostatnio wziąłem na warsztat kwestię w xNovie dość kluczową, podliczanie punktów graczy. Podliczanie w xNovie punktów jest czynnością wykonywaną z panelu administracyjnego, która w założeniu raz dziennie ma policzyć punkty dla każdego gracza i sojuszu w grze, aby potem zapisać je w bazie i przedstawiać np. w zakładce „Statystyki”. Pierwszą sprawą jaka powinna zaintrygować każdego jest pytanie o to, dlaczego taka sprawa nie jest wykonywana automatycznie za pomocą CRON-a. Szczerze mówiąc nie wiem :D Wiem za to, że w xNovie cały skrypt odpowiedzialny za to jest wepchnięty jako zakładka panelu administracyjnego, dlatego nie za bardzo jest możliwość podpięcia tego do CRON-a. Trzeba by to wydłubać do osobnego pliku.

Dlatego też w Blackoucie podliczanie punktów służących do tworzenia statystyk zostało po prostu umieszczone w funkcji BuildStats() (konwencja nazewnictwa zgodna z resztą funkcji w xNovie, żeby nie robić bałaganu większego niż już jest). W tym momencie miejsce wywołania tej operacji (panel administracyjny/CRON) nie ma żadnego znaczenia. Całe podliczenie statystyk sprowadza się do wywołania jednej funkcji.

Budowanie statystyk zostało w xNovie rozbite na dwa pliki: admin/statbuilder.php i admin/statfunctions.php. Cztery funkcje znajdujące się w drugim pliku uprościłem lekko (bo standardowo, jak to w xNovie, generowały trochę nigdzie nie używanych danych) i jako, że są używane tylko przy podliczaniu punktów, przerzuciłem je do pliku z funkcją BuildStats()

Jeśli chodzi o objętość kodu. W xNovie: plik statbuilder.php – 308 linijek, statfunctions.php – 87. Razem daje to 395 linii kodu. U mnie plik spełniający funkcje dwóch powyższych, czyli BuildStats.php zajmuje 232 linijki.

Jedną z kilku przyczyn takiej różnicy w długości kodu jest to, co znalazłem na końcu statbuilder.php – otóż na koniec pliku do podliczania punktów doklejono na chama (nie zgadza się nawet poziom wcięć, choć to w xNovie norma) kod do usuwania nieaktywnych użytkowników. Najlepsze jest to, że w xNovie istnieje już funkcja do usuwania użytkownika razem ze wszystkimi potrzebnymi do tego rzeczami (usuwanie planet, flot, sojuszy itd. itd.)

Wprowadziłem też kilka usprawnień w tabeli która przechowuje statystyki graczy i sojuszów. Zacząłem przede wszystkim od skasowania zbędnych pól - ich liczba zmniejszyła się 25 z do 19, spadł więc też rozmiar trzymanych danych. Najlepszym dowodem na brak myślenia jest fakt, że data podliczenia statystyk, która jest jednakowa w całej tabeli jest przechowywana przy każdym jej rekordzie. A ich może być sporo - po jednym na każdego gracza i na każdy sojusz w grze.

Jeśli chodzi o drugą część wpisu, czyli CRON-a. W oryginalnej xNovie było wiele czynności, które można by wykonywać z jego pomocą - na czele oczywiście podliczanie punktów. Wstępnie w Blackoucie zdecydowałem się aby skrypt odpalany CRON-em realizował następujące zadania: podliczanie punktów, usuwanie graczy, którym minął czas na aktywację konta, w przyszłości także podliczanie rekordów (będą cache'owane i generowane raz na dobę) i wykonywanie innych cyklicznych czynności związanych z przewidywanymi nowymi możliwościami w grze. Oczywiście każda czynność wydzielona do funkcji, tak aby problemem nie było na przykład wywołanie jej z panelu admina.

I to na tyle w tym wpisie, siadam do kodu :)