Pokazywanie postów oznaczonych etykietą facebook. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą facebook. Pokaż wszystkie posty

poniedziałek, 27 czerwca 2011

Tworzenie aplikacji na Facebooka w PHP wykorzystując Graph API w praktyce.

Cel ćwiczenia: zapoznanie się z zasadą pobierania danych użytkownika za pomocą Graph API.

Opis: Zaprojektować system autoryzujący użytkownika, wyświetlający jego zdjęcie, imię, nazwisko, miejsce zamieszkania oraz polubione rzeczy.

Efekt finalny przedstawiono na zrzucie ekranu.


Cały proces podzielimy na trzy części. W pierwszej zaprojektujemy na lokalnym komputerze szkic witryny z wykorzystaniem elementów CSS3, a w drugiej rozbudujemy autoryzujący skrypt utworzony w tym poście, aby spełniał założenia zadania.W ostatniej scalimy ze sobą dwie pierwsze uzyskując ostateczną aplikację.

1. Projekt HTML

1.1 Wizualny schemat strony.
Przed przystąpieniem do zadania warto naszkicować sobie na kartce papieru schemat witryny i oznaczyć poszczególne divy. Ja zrobiłem to w ten sposób:
Sekcja #frame zawiera w sobie wszystkie elementy. Jej szerokość jest 10px mniejsza niż szerokość jaką daje nam okno iFrame Facebooka (760px). Wewnątrz mamy dwa większe kontenery - #topPanel i #mainArea. Pierwszy zawiera zdjęcie użytkownika, jego imię i nazwisko oraz miasto w którym aktualnie się znajduje. Drugi składa się ze stałego tekstu oraz pola #userLikes sformatowanego tak, by wyświetlał dane w trzech kolumnach.

1.2 Szkic w kodzie HTML
Kod HTML, zawierający te wszystkie elementy może wyglądać np tak:
<div id="frame">
 <div id="topPanel">
   <div id="photoDiv">
   </div>
   
   <div id="nameDiv">
   </div>
   
   <div id="cityDiv">
   </div>
 </div>
 <div id="mainArea">
  <p class="goText">Wiem co lubisz? Co powiesz na:</p>
  <hr />
  
  <div id="userLikes">
  </div>
 </div>
</div>

1.3 Wprowadzenie przykładowych danych.
Na razie nie mamy żadnego połączenia z Facebookiem, więc na potrzeby stylizacji wprowadzimy przykładowe dane. Kod może wyglądać tak:

<div id="frame">
 <div id="topPanel">
   <div id="photoDiv">
    <img id="userPhoto" src="http://graph.facebook.com/100000027824838/picture?type=normal" />
   </div>
   
   <div id="nameDiv">
    <p>Norbert</p>
    <p>Kozlowski</p>
   </div>
   
   <div id="cityDiv">
    <p>Aktualne miasto:</p>
    <p class="cityName">Wroclaw</p>
   </div>
 </div>
 <div id="mainArea">
  <p class="goText">Wiem co lubisz? Co powiesz na:</p>
  <hr />
  
  <div id="userLikes">
   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
 Nulla at turpis eget nibh ultricies dignissim. Duis luctus
 euismod turpis. Mauris augue. Aliquam facilisis semper elit.
 Pellentesque semper hendrerit arcu. Phasellus eleifend
 commodo justo. Aliquam orci urna, imperdiet sit amet,
 posuere in, lobortis et, risus. Integer interdum nonummy
 erat. Nullam tellus. Sed accumsan. Vestibulum orci ipsum,
 eleifend vitae, mollis vel, mollis sed, purus. Suspendisse
 mollis elit eu magna. Morbi egestas. Nunc leo ipsum, blandit
 ac, viverra quis, porttitor quis, dui. Vestibulum ante ipsum
 primis in faucibus orci luctus et ultrices posuere cubilia
 Curae; Vivamus scelerisque ipsum ut justo. Pellentesque et
 ligula eu massa sagittis rutrum. In urna nibh, eleifend vel,
 suscipit ut, sagittis id, nunc.
  </div>
 </div>
</div>

W kodzie użyto zdjęcia pobranego z Facebooka. Nazwę miasta wprowadzono w klasie .cityName by ułatwić proces stylizacji który odbędzie się w następnym kroku. Sekcję #userLikes wypełniłem przykładowym tekstem. Niżej aktualny podgląd strony w przeglądarce.

1.4 Dołączenie styli
W zasadzie moglibyśmy pominąć ten krok, bo nie jest on konieczny do osiągnięcia celu. Miło jest popatrzeć na coś ładnie się prezentującego, dlatego poświęcimy trochę czasu wklepując kod. Wykorzystałem tu dwa elementy standardu CSS3 - cienie napisów oraz formatowanie tekstu w kolumnach. Internet Explorer nie obsługuje poprawnie tych możliwości.

p {
  padding:0;
  margin:0;
 }
 
 #frame {
  width:750px;
 }
 
 #topPanel {
  height:140px;
  background-color: #4aa0e8;
  padding-left:10px;
  padding-right:5px;
  border:1px solid #3e33ff;
 }
 
 #photoDiv {
  float:left;
  width:102px;
  padding-top:10px;
 }
 
 #nameDiv p {
  font-family: "Blackadder ITC";
  font-size:47px;
  
  text-shadow: 3px 3px 2px #808080;
  -moz-text-shadow: 3px 3px 2px #808080;
  -webkit-text-shadow: 3px 3px 2px #808080;
 }
 
 #nameDiv {
  float: left;
  padding-top:10px;
  padding-left:5px;
 }
 
 #cityDiv {
  float:right;
  text-align:right;
  line-height: 15px;
  margin-top:10px;
  margin-right:10px;
  font-family:"Calisto MT";
 }
 
 #cityDiv .cityName {
  text-transform: uppercase;
  font-weight:bold;
  margin-top:5px;
 }
 
 #userPhoto {
  border:1px solid white;
  height:80%;
 }
 
 #mainArea {
  margin-top:3px;
  background-color:#ff8040;
  border:1px solid #804000;
  padding:0 5px 5px 5px;
  text-align:justify;
 }
 
 #userLikes {
  font-family:Tahoma;
  font-size:12px;
  line-height:18px;
  
  -moz-column-count: 3;
  -moz-column-gap: 20px;
  
  -webkit-column-count: 3;
  -webkit-column-gap: 20px;
 }
 
 #mainArea .goText {
  margin-top:3px;
  margin-bottom:3px;
  width:100%;
  font-family:Tahoma;
  text-align:center;
 }

Opakujmy ten styl pomiędzy znacznik <style> i umieśćmy przed głównym kodem HTML. Jak prezentuje się strona w różnych przeglądarkach?

Firefox 5.0

Safari 5.0.5

Internet Explorer 9.0 64bit

Internet Explorer nie obsługuje cienia rzucanego przez imię i nazwisko, nie dzieli tekstu na trzy kolumny, oraz ma problemy z interpretacją źródła do obrazka. Trzeba to uwzględnić przy projektowaniu większych aplikacji. Nie jest naszym celem stworzenie teraz aplikacji, która będzie poprawnie obsługiwana przez wszystkie przeglądarki, ale pobieranie danych poprzez Facebook Graph API i na tym się skupimy.

Mając gotową stronę, możemy przejść do drugiego kroku, czyli do tworzenia ...

2. Skrypt PHP

1.1  Skrypt autoryzujący pobierający dodatkowe informacje

Korzystając z informacji w tym wpisie zalążkiem naszego skryptu będzie właśnie to co napisaliśmy. Zacznijmy od następującego kodu stopniowo go modyfikując.
<?php
 // Nr ID aplikacji
 $app_id = '179524192103712';
 // Adres canvas aplikacji
 $canvas_page = 'http://apps.facebook.com/khozzyaps/';
 // Tworzenie adresu url zgodnego ze standardem OAuth
 // Pobieramy dodatkowo takie informacje jak lubiane rzeczy oraz lokalizacje uzytkownika
 $auth_url = 'http://facebook.com/dialog/oauth?client_id=' . $app_id . '&redirect_uri=' . urlencode($canvas_page) 
 . '&scope=user_likes,user_location';
 // Odebranie danych i zapisanie jej do zmiennej
 $signed_request = $_REQUEST["signed_request"];
 // Dzielimy odebrane dane wzgledem kropki na dwie czesci
 list($encoded_sig,$payload) = explode('.',$signed_request,2);
 // Dekodujemy druga czecs odebranych danych i zapisujemy je do zmiennej
 $data = json_decode(base64_decode(strtr($payload,'_-',' /')), true);
 
 // Jezeli nie zautoryzowano uzytkownika, przenies go z powrotem na strone autoryzacji
 if (empty($data["user_id"])) {
  echo("<script> top.location.href='" . $auth_url . "'</script>");
 }
 // Jezli wszystko odbylo sie poprawnie mozemy kontynuowac
 else {
  
  // TU WLASCIWY KOD
 }
?>
Jak już wiadomo, odbywa się tu procedura autoryzacji użytkownika, w której prosimy także o udostępnienie informacji o jego lokalizacji i lubianych rzeczach. Jeśli uzyskano zgodę (tym samym zwrócony został obiekt signed_request zawierający wymagany access_token) możemy przejść do właściwego fragmentu kodu.

1.2 Tworzenie zapytań

Mając obiekt signed_request zapisany w zmiennej $data, wyłuskajmy z niego i zapiszmy ID użytkownika oraz wygenerowany klucz.
$user_id = $data['user_id'];
$access_token = $data['oauth_token'];
Ułatwi to nam konstruowanie zapytań do serwera Graph.

Aby pobrać informacje o imieniu, nazwisku i miejscu zamieszkania a następnie zapisać je, musimy stworzyć zapytanie w odpowiednim formacie. Wszystko opisałem w tym wpisie.
$graph_url = "https://graph.facebook.com/" . $user_id .
"?access_token=" . $access_token;
$response = file_get_contents($graph_url);
$user_info_response = json_decode($response,true);
Na początku tworzone jest ogólne zapytanie w którym argumentami są ID użytkownika i access_token zwrócony przez niego (abyśmy mogli dostać się do prywatnych zasobów). $graph_url jest adresem przechowującym obiekt tekstowy JSON. Pobierzmy te dane i zapiszmy do kolejnej zmiennej. Możemy użyć funkcji curl() (która w tym przypadku jest odrobinę zbyt skomplikowana) lub file_get_contents(). Używając tej drugiej kopiujemy zawartość pliku wskazanego przez $graph_url do zmiennej $response. Nadal jest to tekstowy obiekt JSON. Aby dekodować dane wykonujemy funkcję json_decode(). Drugi parametr true() powoduje, że  $user_info_response jest tablicą asocjacyjną (przechowującą informacje w formacie nazwa-wartość) przechowującą informacje o użytkowniku. Nie podanie tego parametru spowodowałoby utworzenie obiektu stdClass, a co za tym idzie  inny sposób odwoływania się do jej składowych.

Pobierzmy w ten sposób informacje o rzeczach lubianych przez użytkownika.
$likes_url = "https://graph.facebook.com/" . $user_id . 
"/likes?access_token=" . $access_token;
$likesResponse = file_get_contents($likes_url);
$likes_info_response = json_decode($likesResponse);
Dodajmy kod diagnostyczny, aby sprawdzić czy wszystko wygląda na pewno tak jak oczekujemy.
echo '<pre>';
print_r($user_info_response);
print_r($likes_info_response);
echo '</pre>';
Wynik działania przedstawiono na zrzucie ekranu.

Tak jak zakładaliśmy, zaznaczony fragment jest tablicą asocjacyjną przechowującą ogólne informacje o użytkowniku, a kod niżej jest klasą stdClass zawierającą tablicę data z jego ulubionymi rzeczami.

1.3 Wyłuskanie danych

Aby pobrać i zapisać imię, nazwisko i miasto, które są zapisane w tablicy wpisujemy po prostu:
$userName = $user_info_response['first_name'];
$userLastName = $user_info_response['last_name'];
$userCity = $user_info_response['location']['name'];
Jeśli dane te byłyby zapisane jako obiekt, odwołuje się do nich w analogiczny sposób:
$userName = $user_info_response->first_name;
$userLastName = $user_info_response->last_name;
$userCity = $user_info_response->location->name;
Tablica, zawierająca lubiane przedmioty:
$likesArray = $likes_info_response->data;
Którą możemy także wylistować:
for ($i = 0; $i < sizeof($likesArray); $i++) {
 echo $likesArray[$i]->name;
}
Pozostaje nam zapisanie adresu zdjęcia profilowego użytkownika:
$userPhotoAddress = "http://graph.facebook.com/ " . 
$user_id . "/picture?type=normal";

3. HTML + PHP

3.1 Dodanie szablonu strony do kodu PHP

Wykorzystując funkcję echo() wkleimy do skryptu PHP cały kod HTML który napisaliśmy w punkcie pierwszym.
else {
  $access_token = $data['oauth_token'];
  $user_id = $data['user_id'];
  
  // Pobranie info o uzytkowniku
  $graph_url = "https://graph.facebook.com/" . $user_id .
 "?access_token=" . $access_token;
  $response = file_get_contents($graph_url);
    $user_info_response = json_decode($response,true);
  
  // Pobranie info o lubionych przedmiotach
  $likes_url = "https://graph.facebook.com/" . $user_id . 
"/likes?access_token=" . $access_token;
  $likesResponse = file_get_contents($likes_url);
  $likes_info_response = json_decode($likesResponse);
  
  $userName = $user_info_response['first_name'];
  $userLastName = $user_info_response['last_name'];
  $userCity = $user_info_response['location']['name'];
  
  // Tablica z lubianymi przedmiotami
  $likesArray = $likes_info_response->data;
  
  // Adres ze zdjeciem profilowym
  $userPhotoAddress = "http://graph.facebook.com/ " 
. $user_id . "/picture?type=normal";
  
  echo '
   TU KOD HTML WITYRYNY
  ';

  //for ($i = 0; $i < sizeof($likesArray); $i  ) {
  // echo '<b>=> </b>' . $likesArray[$i]->name;
  //}
 }
Gdzie napisany przez nas kod witryny to:

<style>
 p {
  padding:0;
  margin:0;
 }
 
 #frame {
  width:750px;
 }
 
 #topPanel {
  height:140px;
  background-color: #4aa0e8;
  padding-left:10px;
  padding-right:5px;
  border:1px solid #3e33ff;
 }
 
 #photoDiv {
  float:left;
  width:102px;
  padding-top:10px;
 }
 
 #nameDiv p {
  font-family: "Blackadder ITC";
  font-size:47px;
  
  text-shadow: 3px 3px 2px #808080;
  -moz-text-shadow: 3px 3px 2px #808080;
  -webkit-text-shadow: 3px 3px 2px #808080;
 }
 
 #nameDiv {
  float: left;
  padding-top:10px;
  padding-left:5px;
 }
 
 #cityDiv {
  float:right;
  text-align:right;
  line-height: 15px;
  margin-top:10px;
  margin-right:10px;
  font-family:"Calisto MT";
 }
 
 #cityDiv .cityName {
  text-transform: uppercase;
  font-weight:bold;
  margin-top:5px;
 }
 
 #userPhoto {
  border:1px solid white;
  height:80%;
 }
 
 #mainArea {
  margin-top:3px;
  background-color:#ff8040;
  border:1px solid #804000;
  padding:0 5px 5px 5px;
  text-align:justify;
 }
 
 #userLikes {
  font-family:Tahoma;
  font-size:12px;
  line-height:18px;
  
  -moz-column-count: 3;
  -moz-column-gap: 20px;
  
  -webkit-column-count: 3;
  -webkit-column-gap: 20px;
 }
 
 #mainArea .goText {
  margin-top:3px;
  margin-bottom:3px;
  width:100%;
  font-family:Tahoma;
  text-align:center;
 }
</style>
<div id="frame">
 <div id="topPanel">
   <div id="photoDiv">
    <img id="userPhoto" src="http://graph.facebook.com/100000027824838/picture?type=normal" />
   </div>
   
   <div id="nameDiv">
    <p>Norbert</p>
    <p>Kozlowski</p>
   </div>
   
   <div id="cityDiv">
    <p>Aktualne miasto:</p>
    <p class="cityName">Wroclaw</p>
   </div>
 </div>
 <div id="mainArea">
  <p class="goText">Wiem co lubisz? Co powiesz na:</p>
  <hr />
  
  <div id="userLikes">
   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
 Nulla at turpis eget nibh ultricies dignissim. Duis luctus
 euismod turpis. Mauris augue. Aliquam facilisis semper elit.
 Pellentesque semper hendrerit arcu. Phasellus eleifend
 commodo justo. Aliquam orci urna, imperdiet sit amet,
 posuere in, lobortis et, risus. Integer interdum nonummy
 erat. Nullam tellus. Sed accumsan. Vestibulum orci ipsum,
 eleifend vitae, mollis vel, mollis sed, purus. Suspendisse
 mollis elit eu magna. Morbi egestas. Nunc leo ipsum, blandit
 ac, viverra quis, porttitor quis, dui. Vestibulum ante ipsum
 primis in faucibus orci luctus et ultrices posuere cubilia
 Curae; Vivamus scelerisque ipsum ut justo. Pellentesque et
 ligula eu massa sagittis rutrum. In urna nibh, eleifend vel,
 suscipit ut, sagittis id, nunc.
  </div>
 </div>
</div>
Należy zamienić takie miejsca jak:
  • #photoDiv
  • #nameDiv
  • #cityDiv
  • #userLikes
Aby poszczególne sekcje pobierały dane ze zmiennych, należy podmienić wpisane przykładowe wartości nazwami zmiennych. Więc:
<div id="photoDiv">
 <img id="userPhoto" src="' . $userPhotoAddress . '" />
</div>
Zamiana imienia i nazwiska:
<div id="nameDiv">
 <p>' . $userName . '</p>
 <p>' . $userLastName . '</p>
</div>
miasta:
<div id="cityDiv">
 <p>Aktualne miasto:</p>
 <p class="cityName">' . $userCity . '</p>
</div>
Wypisanie lubianych przedmiotów
<div id="userLikes">';

 for ($i = 0; $i < sizeof($likesArray); $i  ) {
  echo '<b>=> </b>' . $likesArray[$i]->name;
 }
  
echo ' 
</div>
Użyłem apostrofów, aby wyjść funkcji echo, wypisać całą zawartość tablicy, a potem znowu ją wywołać, aby pozamykać pozostałe sekcje. Warto też dodać znacznik nowej linii po każdym przedmiocie (tutaj był pomijany). Całość widać na zrzucie ekranu

Wnioski
Przykładowe screeny:

Napotkane problemy wymagające rozwiązania: kompatybilność z przeglądarkami (poprawne wyświetlanie zdjęcia), kodowanie polskich znaków.
Założony cel osiągnięty.

niedziela, 26 czerwca 2011

Czym jest Graph API?

Graph API jest filarem na którym opiera się działanie Facebooka jako portalu społecznościowego. Dokładnie jest to interfejs programistyczny wiążący wszystkie elementy występujące na stronie w spójną całość. Jest to możliwe, ponieważ każde zdjęcie, osoba, wydarzenie czy komentarz reprezentowana jest jako obiekt o określonych właściwościach. W zależności od ustawień prywatności i od tego czy uzyskamy pozwolenie możemy ich widzieć więcej lub mniej.

Aby możliwe było utworzenie powiązań (np. przypisanie albumu ze zdjęciami konkretnemu użytkownikowi) każdy obiekt posiada swój unikalny numer ID.

Aby pobrać informacje o takim obiekcie musimy zmodyfikować specjalny adres URL serwera, który je przechowuje. Zobaczmy kilka przykładów.

http://graph.facebook.com/ID
http://graph.facebook.com/username

Graf profilu Marka Zuckerberga, założyciela portalu (o numerze ID 68310606562), dostępny jest pod adresem http://graph.facebook.com/68310606562 i wygląda następująco:
{
   "id": "68310606562",
   "name": "Mark Zuckerberg",
   "picture": "http://profile.ak.fbcdn.net/hprofile-ak-snc4/
50270_68310606562_2720435_s.jpg",
   "link": "http://www.facebook.com/markzuckerberg",
   "likes": 4538132,
   "category": "Public figure",
   "website": "www.facebook.com",
   "username": "markzuckerberg",
   "personal_interests": "openness, making things that help
people connect and share what's important to them, 
revolutions, information flow, minimalism\n\n\n"
}
Zamiast numeru ID możemy przekazać nazwę username obiektu (czyli do wspomnianego wyżej przykładu dostaniemy się poprzez adres http://graph.facebook.com/markzuckerberg). Dla przykładu - graf mojego fanpage-u zrzeszającego ludzi, którym podoba się idea Facebookowym pieczątek (https://graph.facebook.com/PieczatkiLubieTo) wygląda w taki sposób:
{
   "id": "129613800426235",
   "name": "Piecz\u0105tki \"Lubisz to\"",
   "picture": "http://profile.ak.fbcdn.net/hprofile-ak-snc4
/41607_129613800426235_5059275_s.jpg",
   "link": "http://www.facebook.com/PieczatkiLubieTo",
   "likes": 1401,
   "category": "Product/service",
   "website": "www.polubisz-to.pl\nhttp://allegro.pl/
show_user_auctions.php?uid=1761089",
   "username": "PieczatkiLubieTo",
   "company_overview": "Ty te\u017c jeste\u015b Facebook-o-
-holikiem tudzie\u017c Facebook-o-holiczk\u0105? \n\nTe\u017c
 czasami a\u017c chcesz oznaczy\u0107 co\u015b swoim \"Lubi\u0119
 to\" w realu?\n\nTeraz masz tak\u0105 mo\u017cliwo\u015b\u0107...\n\n
Piecz\u0105tk\u0105 \"Lubisz to\" mo\u017cesz oznaczy\u0107 dos\u0142ownie 
wszystko. Tylko od Twojej kreatywno\u015bci i wyobra\u017ani zale\u017cy, 
co to b\u0119dzie. \n\nCzekamy na Twoje pomys\u0142y, zdj\u0119cia, filmy 
- poka\u017c \u015bwiatu, co naprawd\u0119 lubisz!",
   "products": "Kieszonkowe stemple Pocket Stamp z Twoim imieniem i nazwiskiem 
lub pseudonimem oraz znaczkiem \"lubisz to\""
}
Odpowiedzi te są obiektami JSON. Możemy je wygenerować dla wszystkich obiektów w portalu (m.in. dla zdjęć, albumów, zdjęć profilowych, grup, aplikacji, statusów, stron...). Jak widać zawierają tylko podstawowe i ogólnie dostępne informacje do których dostęp ma nawet osoba nie posiadająca konta na Facebooku. Informacje te są powszechnie dostępne.

Obiekty są ze sobą w jakiś sposób powiązane. Dzieje się tak kiedy np. użytkownik lubi pewną stronę, uczestniczy w wydarzeniu, jest w związku z innym użytkownikiem, bądź po prostu ma go w znajomych. W omawianym "socjalnym grafie" relacja ta nazwana jest  połączeniem. Możemy badać takie "połączenia" elementu z numerem ID z innymi elementami jak np:
  • znajomymi
    https://graph.facebook.com/ID/friends?access_token=...
  • najnowszą aktywnością znajomych (news feed)
    https://graph.facebook.com/ID/home?access_token=...
  • wpisami na ścianie
    https://graph.facebook.com/ID/feed?access_token=...
  • lubianymi rzeczami
    https://graph.facebook.com/
    ID/likes?access_token=...
  • filmami
    https://graph.facebook.com/ID/videos/uploaded?access_token=...
  • muzyką
    https://graph.facebook.com/
    ID/music?access_token=...
  • książkami
    https://graph.facebook.com/ID/books?access_token=...
  • notatkami
    https://graph.facebook.com/
    ID/notes?access_token=...
  • znacznikami na zdjęciach
    https://graph.facebook.com/
    ID/photos?access_token=...
  • albumami
    https://graph.facebook.com/ID/albums?access_token=...
  • wydarzeniami
    https://graph.facebook.com/ID/events?access_token=...
  • grupami
    https://graph.facebook.com/ID/groups?access_token=...
  • .....
Informacje te są już jednak bardziej poufne i trzeba się nieco postarać aby uzyskać do nich dostęp. Sam użytkownik musi "pozwolić" nam je przeczytać. Robi to poprzez przekazanie nam parametru access_token, który podajemy jako argument w takim adresie URL. Jest on poufny, dlatego też połączenie musi odbywać się poprzez bezpieczny kanał (konieczny przedrostek https!). Aby uzyskać ten ciąg znaków należy przeprowadzić proces autoryzacji, który opisałem w tym wpisie. Jeśli użytkownik nie zgodzi się udostępnić nam swoich prywatnych danych, nie zostanie nam przydzielony access_token - nie uzyskamy dostępu do bardziej szczegółowych danych więc będziemy mogli korzystać tylko z zasobów, które zostały przez niego sklasyfikowane jako publiczne.

Zdjęcia

W łatwy sposób możemy także generować zdjęcia (np. profilowe, wydarzeń, grup, stron, aplikacji...). Wystarczy stworzyć zapytanie zgodne ze schematem

http://graph.facebook.com/ID/picture
http://graph.facebook.com/username/picure

Zdjęcia możemy sobie renderować według wymagań dzięki parametrowi type. Domyślnie jest to kwadrat (square) o boku 50px. Możemy wybierać między small (50px szerokość, wysokość zmienna), normal (100px szerokość, wysokość zmienna) oraz large (200px szerokość, wysokość zmienna).

Przykład:

Jeśli chcemy, aby zdjęcie było zwrócone bezpiecznym protokołem SSL, dodajemy argument return_ssl_resources.

Przykład:


Zapytania

Możemy tworzyć bardziej zaawansowane zapytania, które nie będą zwracały wszystkich informacji o obiekcie, tylko te o które sami poprosimy. Robi się to dodając argument fields z nazwami pól oddzielonymi przecinkami, które chcemy otrzymać.


Zwróci:
{
   "id": "68310606562",
   "name": "Mark Zuckerberg",
   "category": "Public figure"
}
Do jednego zapytania możemy dołączyć prośbę o inne obiekty. Służy do tego argument ids. Przykład niżej zwróci publiczne informacje o Marku Zuckerbergu i o fanpage-u 50 centa.


Warto wiedzieć także o identyfikatorze me, który zwraca informacje o aktywnym użytkowniku.


Wyszukiwanie i filtrowanie

Tworząc zapytania, możemy ograniczać liczbę zwracanych wyników. Służą do tego zmienne limit i offset

https://graph.facebook.com/me/likes?access_token=...&limit=3

Zapytanie to zwróci trzy ostatnio polubione przez nas rzeczy. Aby odnieść się do kolejnych trzech należy dodać offset czyli przesunięcie.

https://graph.facebook.com/me/likes?access_token=...&limit=3&offset=6
Bardzo ciekawą opcją jest wyszukiwarka. Zwraca ona wszystkie obiekty, które zawierają w sobie wyszukiwaną frazę. Wyszukiwanie ogranicza się przedziałami czasowymi (za pomocą argumentów since i until po którym podany jest czas zgodny z funkcją strtotime()) a szukaną frazę podaje się za argumentem q (ang. query).

http://graph.facebook.com/search?until=yesterday&q=gruby

To zapytanie wyświetli wszystko umieszczone przez użytkowników na portalu (np jako statusy, komentarze pod zdjęciami, nazwy filmów...) które zawiera w sobie słowo 'gruby' i zostało umieszczone nie wcześniej niż wczoraj.

Na każdej zwróconej przez wyszukiwarkę stronie umieszczone jest 25 obiektów, przeglądając je dochodzimy do linijek umożliwiających dalsze przeglądanie
"paging": {
      "previous": "https://graph.facebook.com/search?q=gruby&access_token=...
&paging=next&limit=25&since=1308956406",
      "next": "https://graph.facebook.com/search?q=gruby&access_token=...
&paging=next&limit=25&until=1308285693"
   }
Są to pola obiektu JSON więc implementacja w kodzie nie jest problemem.

Możemy także sprecyzować jakiego typu wyniki nas interesują. Służy do tego parametr type, który aktualnie obsługuje takie wyniki jak posty (post), użytkownicy (user), strony (page), wydarzenia (event), grupy (group), miejsca (place). Przykładowe zapytanie wyszukujące użytkownika nazywającego się Norbert

https://graph.facebook.com/search?q=norbert&type=user

sobota, 18 czerwca 2011

Jak przebiega autoryzacja aplikacji na Facebooku? Krok po kroku.

Aby uzyskać autoryzację dla aplikacji Facebookowej developerzy zalecają korzystanie z protokołu OAuth 2.0 Dialog (jest to standard pozwalający dzielić się swoimi prywatnymi materiałami z innymi). Do wykorzystania są także popularne biblioteki JS SDK lub PHP SDK ułatwiające pracę. Tu jednak poruszone zostanie klasyczne rozwiązanie.

Autoryzację OAuth w Facebooku wykonujemy przekierowując użytkownika na adres URL, stworzony według schematu: 
  
https://www.facebook.com/dialog/oauth?
client_id=YOUR_APP_ID
&redirect_uri=YOUR_CANVAS_PAGE


Gdzie YOUR_APP_ID jest identyfikatorem aplikacji , a YOUR_CANVAS_PAGE ścieżką URL do naszego  serwera który ją przechowuje. Domyślnie adres ten uzyskuje dostęp do podstawowych informacji o użytkowniku. Aby pobrać więcej danych, musimy wymienić żądane informacje, po parametrze scope. Poniższy przykład pokazuje jak uzyskać dostęp do adresu e-mail oraz pola news feed (czyli informacji o aktywności znajomych) 

https://www.facebook.com/dialog/oauth?
client_id=YOUR_APP_ID  
&redirect_uri=YOUR_CANVAS_PAGE 
&scope=email,user_status,user_location 

Pełna lista informacji jakie możemy pobrać dostępna jest na tej stronie. Istnieje pewna zależność pomiędzy ilością zbieranych danych, a liczbą użytkowników zezwalających na ich udostępnianie. Mianowicie, im więcej danych żądamy, mniej użytkowników będzie korzystało z naszej aplikacji.

W przypadku gdy użytkownik odmówi udostępnienia informacji, przeglądarka przekieruje go na adres wygenerowany na podstawie podanego wcześniej redirect_uri, zwracając błędy:

http://YOUR_CANVAS_PAGE?
error_reason=user_denied
&error=access_denied
&error_description=The+user+denied+your+request.

 
W przypadku, gdy użytkownik zezwala na udostępnianie informacji o które został poproszony, aplikacja przekierowuje go na adres podany w redirect_uri ustawiając parametr signed_request. Parametr ten jest połączeniem funkcji hashującej HMAC SHA-256, znaku ‘.’ oraz obiektu JSON zakodowanego algorytmem base64url. Może wyglądać np. tak (bez znaków nowej linii)
vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso.
eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0 
Sygnatura HMAC zapewnia, że przesyłane dane pochodzą z serwisu Facebook (dzięki kodowaniu przez tajny kod App Secret znany tylko twórcy aplikacji).

Dalej zakodowany jest obiekt JSON. Jest to format tekstowy używany przy wymianie danych przez skrypty. Często używa się go przy okazji wykorzystania technologii AJAX. Obsługa odebranego ciągu znaków ogranicza się do zastosowania na nim funkcji eval() (JS). Zamienia ona dane JSON na dane w formacie tablicy. Dostęp do nich uzyskujemy poprzez nazwę i indeksy pól. Na przykład odebranaDana.tytul lub odebranaDana.tresc.

W przypadku języka PHP, wystarczy na tablicy danych zastosować funkcję json_encode(), aby zamienić je na format JSON. Dekodowanie wykonujemy analogicznie wykorzystując funkcję json_decode().

Obiekt signed_request zawiera następujące pola:

·         user
Tablica JSON zawierająca takie informacje jak kraj z którego pochodzi użytkownik, zakres wieku w jakim się plasuje, locale (czyli ustawienia zmiennych środowiskowych).
·         algorithm
Ciąg znaków JSON zawierający nazwę algorytmu użytego do zakodowania żądania.
·         issued_at
Liczba JSON w formacie POSIX, informująca kiedy żądanie zostało zrealizowane.
·         user_id
Ciąg znaków JSON zawierający identyfikator bieżącego użytkownika (UID).
·         oauth_token
Ciąg znaków JSON wykorzystywany przy korzystaniu z Graph API.
·         expires
Liczba JSON zawierająca czas w formacie POSIX, którego data wyznacza okres wygaśnięcia oauth_token.

Poniższy kod PHP demonstruje zasadę działania procesu autoryzacji oraz wyświetla zawartość zmiennej signed_request w formacie przyjaznym użytkownikowi.
<?php
 # Nr ID aplikacji
 $app_id = '179524192103712';
 # Adres canvas aplikacji
 $canvas_page = 'http://apps.facebook.com/khozzyaps/';
 # Tworzenie adresu url zgodnego ze standardem OAuth
 # Pobieramy dodatkowo takie informacje jak email, status oraz lokalizacje uzytkownika
 $auth_url = 'http://facebook.com/dialog/oauth?client_id=' . $app_id . '&redirect_uri=' . urlencode($canvas_page) 
 . '&scope=email,user_status,user_location';
 # Odebranie danych i zapisanie jej do zmiennej
 $signed_request = $_REQUEST["signed_request"];
 # Dzielimy odebrane dane wzgledem kropki na dwie czesci
 list($encoded_sig,$payload) = explode('.',$signed_request,2);
 # Dekodujemy druga czecs odebranych danych i zapisujemy je do zmiennej
 $data = json_decode(base64_decode(strtr($payload,'_-',' /')), true);
 
 # Jezeli nie zautoryzowano uzytkownika, przenies go z powrotem na strone autoryzacji
 if (empty($data["user_id"])) {
  echo("<script> top.location.href='" . $auth_url . "'</script>");
 }
 # Jezli wszystko odbylo sie poprawnie wyswietl zawartosc zmiennej $data w formie przyjaznej uzytkownikowi
 else {
  echo "<pre>";
  echo print_r($data); 
  echo "</pre>";
 }
 
?>

Oraz zrzut ekranu prezentujący działanie aplikacji:


niedziela, 10 kwietnia 2011

6-7 kwietnia - Górny Śląsk - COLOP

"Każda podróż jest nową obietnicą".

Środa (6 kwietnia) zapowiadała się interesująco od samego początku. 
Tego dnia miałem drugi etap konkursu, gdzie główną nagrodą jest płatny staż w profesjonalnej firmie projektującej oprogramowanie - Tieto, potem labolatoria z projektowania algorytmów i sztucznej inteligencji,  a po południu wyjeżdżałem do Bytomia.

Obudziłem się oczywiście niewyspany, wieczorem poprzedniego dnia planowałem poranny trening, niestety nie podołałem. Po szybkim śniadaniu pojechałem na Uniwersytet Ekonomiczny, gdzie odbywał się konkurs. Było sporo osób. Można było pisać test z inżynierii oprogramowania (ja), audytu, finansów i rachunkowości oraz public relations. Sala była dość duża, konferencyjna, podzielona na dwie części. Dziewczyna, którą zagadałem (studentka IV roku z UEk) powiedziała mi, że strasznie jej nie lubi, bo nie można ściągać z powodu kamer. Życie.


Z półgodzinnym poślizgiem zaczęliśmy konkurs. Pytania układane przez firmę sponsorującą nagrody, do prostych nie należały. Bardziej niż AiR ("Akrobatyka i Robotyka") odpowiedni byłby profil informatyczny i to na wydziale IZ. Pytania były głównie z programowania systemów embedded, designu programistycznego, sprzętu. 50 zagadnień z różną ilością poprawnych odpowiedzi, 60 minut czasu. 
Poszło mi średnio, tym bardziej, że nie jest to moim konikiem, ani w jakikolwiek sposób się do niego nie przygotowywałem. Coś wiedziałem, ale nie było to mistrzostwo w moim wykonaniu.

Wyszedłem 15 min przed czasem i udałem się na tramwaj. Musiałem dotrzeć do domu po laptopa i od razu iść na zajęcia. Projektowanie algorytmów i metod sztucznej inteligencji. W praktyce - programowanie w C++ lub czymkolwiek co umożliwia OOP (Object-oriented programming). Prowadzący dał nam 3 tygodnie na przygotowanie własnej implementacji kopca oraz drzewa binarnego (czerwono-czarnego, lub AVL). Niestety, ale jako zabiegany człowiek, do zagadnienia usiadłem poprzedniego wieczora, z nadzieją doznania olśnienia. Prawie mi się udało. Ale jak wiadomo - "prawie robi wielką różnicę". Programy nie były do końca dopracowane.


Idąc na kurs, myślałem co powiedzieć, żeby móc oddać pracę w innym terminie. Myślałem, też do kogo zagadać, żeby zrobił to za mnie, bo szczerze nie chciało mi się w tym babrać. Wiem jak dużo czasu zabiera zrobienie tego dobrze. Wiem też jak ten czas mogę lepiej wykorzystać.

Na szczęście, około 60% mojej grupy zajęciowej miało ten sam problem - nie dokończyli swoich programów. Pozostałe 40% to ludzie, których mi trochę szkoda. Podałem jednemu rękę przed zajęciami, dawno takiego flaka nie trzymałem. Humor mi się poprawił, jak ten mały, śmieszny MacGyver opowiadał o swoich imprezach:

- No, wiesz, co. No byłem z kumplami w Alibi wczoraj, noo. (Alibi - klub studencki we Wrocławiu)
- No i?
- No i czaisz. 2 piwa wypiłem. Już nieźle się wstawiłem. Czaisz? No i wtedy poszliśmy na kebaba.
- No i?
- No i kumpel mi kupił tego kebaba, bo mi się język trochę już plątał. No i zjadłem połowę i film mi się urwał. Czaisz, kumple mnie odnosili do akademika. Noo
- ....

Nie wiem co robić. Z jednej strony mu współczuję, że urodził się cipką, z drugiej zazdroszczę, że kebab i 2 piwka doprowadzają go do takiego stanu. Na dłuższą metę mu jednak współczuję :)

Prowadzący (wygląda jakby kilka lat temu była zagorzałym fanem ciężkiego rocka), powiedział, że mamy tydzień na dokończenie swoich implementacji. To wcale nie jest, żadne ułatwienie, biorąc pod uwagę, że nie chce mi się tego robić. No ale mam jeszcze kilka dni - to coś wymyślę.

Wróciłem do domu, szybko zrobiłem trening. Zjadłem coś, umyłem się i zacząłem przygotowywać do wyjazdu do Bytomia. Pociąg z Wrocławia do Katowic odjeżdżał o 17:22. Miałem chwilę czasu, więc postanowiłem się jakoś ubrać. Problem - jadę na spotkanie w interesach, a wszystkie moje spodnie są w różnych miejscach porwane. Na udach, nogawkach, kroku. Nawet krótkie spodenki były porwane. Miałem ładny dres Pumy, ale on nie pasował do charakteru wizyty. Raz się żyje. Wziąłem te średnio porwane, dopasowałem do nich bluzę z kapturem i czarne buty treningowe. Pod spód założyłem fioletową koszulę. Taka kombinacja pozwalała mi w pół minuty zmienić image ze street na casual. To był dobry pomysł. 
Idę na pociąg.


Ładna maszyna. Nie wyglądała na drugą klasę, nie wyglądała też na pierwszą. Klasa półtora. Podróż minęła przyjemnie. Wziąłem sobie nawet dwie książki - "Podstawy automatyki" oraz poradnik o relacjach międzyludzkich. Zacząłem czytać pierwszą i zasnąłem. Obudziłem się pod Katowicami około 20:20.

Dworzec w Katowicach zawsze budził we mnie mieszane uczucia. Z zewnątrz jest strasznie zaniedbany. Dopiero pierwszy raz w życiu schodząc niżej odkryłem, że jest całkiem ładnie odnowiony. Czyściutko, ładnie i schludnie. Piekielnie zimno. 

Miałem prawie 2 godziny wolnego czasu, więc postanowiłem się wybrać na wycieczkę po Katowicach - stolicy Górnego Śląska. Miasto ma około 2x mniej mieszkańców niż Wrocław co było odczuwalne. Spacerując nocami po uliczkach rynku, mijało mnie bardzo mało normalnych ludzi. Dużo częściej mijali mnie dresiarze i kibice. Dobrze, że wziąłem dużą bluzę z kapturem i czapkę z wojskowym krojem. Nikogo nie zaczepiałem ;)

Totalną porażką jest brak fast-foodów w centrum tak wielkiego miasta. Wszystko było pozamykane. Byłem głodny, chciałem MacDonalda albo KFC. Nie stać mnie było na jakąś restaurację. Dopiero po kilkudziesięciu minutach poszukiwań znalazłem Maca w jakiejś bramie.
Po WieśMacu chciałem też porobić kilka zdjęć. Ale widząc, wszędzie twarze w kapturach wpatrujące się we mnie jak w jakiegoś turystę, dałem sobie spokój. Bezpieczeństwo.


Miałem około 20 min na następny pociąg. Bez problemu znalazłem drogę powrotną na dworzec i wsiadłem do pociągu jadącego w kierunku Bytomia.


W Bytomiu miałem spędzić noc u dalszej ciotki. Z dworca miało mnie odebrać dwóch braci ciotecznych, których pamiętam jeszcze z dzieciństwa. Wysiadając, na peronach nie było żywej duszy. Dobrze, że zorientowałem się na mapie miasta wcześniej w domu i wiedziałem mniej więcej w którym kierunku iść.

Nie szedłem jednak długo, po kilku chwilach drogę zagrodziło mi dwóch gości. Jakże się ucieszyłem widząc znajome twarze. Jakby nie były znajome prawdopodobnie szukałbym wzrokiem sposobu do szybkiej ewakuacji.

Starszy, maturzysta, szkoła wojskowa, wytatuowany, irokez na głowie, dobrze zbudowany. Młodszy, 3 gimnazjum, łysy, umięśniony, z wielkim łańcuchem dyndającym na szyi.

Pogadaliśmy chwilę, po czym samochodem podrzucili mnie do miejsca gdzie miałem spać (ul. Wrocławska). Spotkanie miałem jutro o godzinie 10, Paweł powiedział, że podrzuci mnie rano, bo i tak jedzie do szkoły w tym samym kierunku. Super.

Na miejscu, w mieszkaniu przywitałem się z ciocią Gosią - nauczycielką języka angielskiego w szkole podstawowej. Porozmawialiśmy trochę, dostałem świetną kolację i wielkie łóżko z ogromnym TV nad głową. Mhm... :)

Następnego dnia musiałem wstać o 7 rano. Wszyscy zbierali się do pracy/szkoły/przedszkola. Mała Julka (córeczka Gosi, ok 3.5 lat) początkowo była nieufna wobec mnie. Szybko jednak znaleźliśmy wspólny język (język dinozaurów) i chyba się polubiliśmy. Znowu dostałem ogromną porcję jedzenia na śniadanie + kilka kanapek na drogę. Lubię to.
Chwilę później przyjechał Paweł i zabraliśmy się do centrum. Wysadził mnie na ulicy Jana Smolenia, gdzie mieściła się firma, oraz pokazał mi w którą stronę mam iść, żeby dotrzeć do stacji tramwajowej (plac Sikorskiego). Odjeżdzały z niej tramwaje m.in do Katowic. W okręgu Górnośląskim można przemieszczać się między miastami za pomocą tramwajów. Bilet ulgowy na 1 gminę kosztuje 1,3 zł, na 2 - 1,8 zł , a na 3 gminy 2 zł.  Pomijając stan tych tramwajów, pomysł jest genialny. 


Paweł pojechał do szkoły, była godzina 8:30, a spotkanie miałem zaplanowane na 10. Postanowiłem przejść się po okolicy, znaleźć jakąś ławkę, rzucić okiem na zaznaczone fragmenty książki Dale Carnage, aby przygotować się do rozmowy.

Bytom wywołał we mnie raczej pejoratywne skojarzenia. Kojarzy mi się z miastem biednym, zaniedbanym, szarym i smutnym. Na pewno trochę spowodowane było to pogodą - słońce chowało się za chmurami. Momentami padał deszcz. Ale największą rolę odegrał czynnik ludzki. Ludzie są strasznie agresywnie nastawieni. Panuje tu typowy nastrój z polskich hip-hopowych pieśni ludowych - pod szarą ścianą bloku, po zapadnięciu zmroku, razem z kolegami dostaliśmy ataku ślinotoku.. Efekt potęgował fakt, że bardzo ważna w tym rejonie jest piłka nożna. Każde miasto ma swoją drużynę. GKS Katowice, Polonia Bytom, Górnik Zabrze, Ruch Chorzów. Nie znając panujących stosunków między kibicami danych drużyn ryzykowałem.

Znalazłem jakąś ławeczkę, ale gdy tylko się do niej zbliżyłem w moim kierunku wyskoczyło dwóch kolesi z kapturami. Jeden zaczął machać rękami, drugi się na mnie patrzył. Ustąpiłem. Odchodząc próbowałem ich jeszcze zniszczyć wzrokiem, ale chyba mi się nie udało bo popatrzyli się na siebie i zaczęli iść za mną. Podejrzewam, że jednak się przestraszyli, bo po kilku minutach zawrócili :)

Zdecydowałem, że cały czas będę w ruchu. Muszę być czujny. Podczas tych przemyśleń moim niebieskim oczom ukazała się galeria handlowa Agora. O ile we Wrocławiu, w każdym większym centrum handlowym panuje zawsze duży ruch, tam ludzi można było zliczyć na palcach. Pokręciłem się chwilę, znalazłem łazienkę i wyszedłem, bo nie było sensu tam być.

Zostało mi 15 min do spotkania. Postanowiłem wpasować się w otoczenie niczym kameleon. Stanąłem więc w bramie i udawałem cwaniaka. Nie była to jednak moja rola i nie czułem się spełniony.

Wchodzę. I'm in! Kwintesencja podróży miała się spełnić w następnych chwilach.

Pani sekretarka chyba oczekiwała, kogoś innego pokroju. Może nie spodobałem jej się? Zaczęła mi zadawać dziwne pytania i chyba lekko ironizować. Ściągnąłem więc bluzę, zdjąłem czapkę. Wtedy ją miałem. Powiedziała, że prezes już idzie i żebym poczekał na niego w sali konferencyjnej. Siedziałem tam około 10 minut i z nudów zaczynałem już sam oglądać maszyny, które tam stały.

W końcu przyszedł właściciel. Przez telefon mówił, że także jest osobą młodą, pełną wigoru i pomysłów. Cieszę się, że ludzie tak o sobie myślą i mówią. Z wyglądu taki młody nie był.

Na początku opowiedział mi trochę o firmie, pokazał jak działają, zapoznał z kilkoma paniami (od marketingu, sekretarki, jakaś obsługa). Pokazał mi różne produkty, wyjaśnił z czym jest problem, czego ode mnie oczekuje. 

Potem ja miałem opowiedzieć skąd czerpię swoje pomysły, skąd mam siły, aby je realizować, jakie wnioski wyciągam z błędów, których sporo popełniłem. Opowiedziałem o także o swoich konkursach, kolejnych ideach, o których myślałem, ale brakowało mi funduszy.

Potem wybraliśmy się do firmowego sklepu, gdzie Pan Robert pokazał mi cały proces produkcyjny, kilka maszyn, omówił technikę działania każdej, poprosił panie z obsługi, aby zademonstrowały mi proces tworzenia pieczątki. 

Po powrocie obejrzeliśmy kilka katalogów, m.in. różne rodzaje tuszów (do drewna, na karton, UV, itp), omówiliśmy kilka rodzajów stempli (w tym fenomenalny suchy stempel - bardzo mi się spodobała idea. Myślę że można z niego coś ciekawego zrobić)



Pod koniec Pan prezes zamówił pizzę (kurczak + kukurydza) oraz zaoferował mi zrobienie pieczątki. W sumie dostałem świetny długopis z pieczątką "Lubię to.", oraz grafitowy model Stamp Mouse 20, który wykonaliśmy razem od podstaw.

Podsumowując:
Oczekiwania względem mnie:
- nowe pomysły na niekonwencjonalne wykorzystanie pieczątek
- równoległa promocja kampanii na Facebooku oraz zajmowanie się innymi pomysłami
- angażowanie swojego wolnego czasu
- progres w sprzedaży
- regularne spotkania (np co 3 miesiące), wspólne udziały w targach

Czego ja mogę oczekiwać?
- od każdej sprzedanej pieczątki moja marża -> im więcej sprzedam tym więcej zarobię
- pomoc od firmy
- finansowe wsparcie po przedstawieniu i uznaniu mojej koncepcji projektu

Spotkanie skończyło się po 3 godzinach. Oczywiście zapomniałem wziąć katalogi, które zostały mi dane (muszę napisać maila po wersję PDF). Prezes odwiózł mnie swoją piękną Audi A6 na plac Sikorskiego, skąd też zasugerował, abym zamiast do Katowic, pojechał do Zgorzelca. Why not? Przecież pociągi i tak muszą przejechać przez tą miejscowość.

Po dotarciu kupiłem sobie bilet za 2 złote na 3 gminy. Wydaje mi się, że mogłem kupić tańszy, ale wolałem nie ryzykować. Po chwili podjechał tramwaj nr 5, który kursował bezpośrednio tam gdzie chciałem. Wchodząc dodatkowo się upewniłem (koleś coś odburnkął - uznałem to za dobry znak).

Stan tramwaju był tragiczny, przypominał mi prawie komunikację miejską na Ukrainie, gdzie bus jeździł z dziurą w podłodze. Oprócz pojazdu tragicznie prezentowali się też ludzie, którzy nim jechali. Wszyscy stwarzali pozory alkoholików, przegranych, agresywnych ludzi o bardzo wąskich perspektywach. Metr ode mnie stało rumuńskie dziecko z matką, któremu się przyglądałem trzymając profilaktycznie wszystkie swoje rzeczy. Brudne, obdarte, zaniedbane. Dawno się z czymś takim nie spotkałem. Smutne.

Po dojechaniu do Zabrza, zapytałem się faceta, który siedział za mną, gdzie najlepiej wysiąść, żeby dostać się na dworzec PKP. Nie udało mi się nawiązać jakiejkolwiek rozmowy, ponieważ otrzymywałem szczątkowe i zdawkowe odpowiedzi - "Powiem ci kiedy." Powiedział. Wyszedłem we wskazanym miejscu. Przeszedłem się główną ulicą Zabrza. Szału nie doznałem.

Bez problemów też odnalazłem dworzec. Na tablicy odjazdów było napisane, że najbliższy, w kierunku Wrocławia mam za 14 min. Poczekam więc na peronie.

Kiedy tam doszedłem właśnie odjeżdżał  jakiś pociąg. Krzyknąłem do jakiejś kobiety przy drzwiach czy jedzie przez Wrocław. Ona mi powiedziała, że tak i jeśli chcę też jechać muszę się pospieszyć ;)

Chciałem tu wstawić filmik, który widziałem kiedyś na Joe Monster przedstawiający hindusów lub murzynów wskakujących do jadącego pociągu. Nie znalazłem, ale może go widzieliście. Przeżyłem to samo.

Biegnąc równolegle do torów kobieta wystawiła rękę, żebym ją złapał. Najpierw rzuciłem jej swój plecak, później rozpędzony skoczyłem do drzwi łapiąc się jedną ręką barierki, a drugą jej dłoni. Damn, lubię to :))

Po znalezieniu przedziału w którym się rozgościłem, porozmawiałem trochę ze starszą kobietą, która tam siedziała. Jechała z Katowic do Szczecina i opowiedziała mi, że na śląsku niebezpiecznie jest jeździć samemu w przedziale. Często organizowane są napady. Mówiła jak tydzień temu, obok jej przedziału, w jednej miejscowości, do siedzącej w przedziale samej osoby dosiadło się dwóch mężczyzn. Pobili ją i okradli. Na następnej stacji czekała już policja. Niestety minęli się. Oni weszli innym wejściem, a tamtych dwóch wyszło drugim.

Potem do samego Wrocławia spałem.

------------------------ ZAPAMIĘTAJ NORBERCIK ------------------------
- Rejestracje samochodowe (SK - Katowice, SY - Bytom, SZ - Zabrze)
- Drużyny piłkarskie - przed kolejnymi podróżami orientować się stosunkach lokalnych klubów piłkarskich z innymi
- Z chęcią wysłałbym wszystkie wrocławskie kaleki z dyskotek do Katowic. Myślę, że czegoś by się nauczyli. Kiedy tam byłem, nie widziałem, ani jednego kolesia z dłuższymi włosami, ani jednego kolesia w rurkach lub kogokolwiek ubranego jak pedał. To ogromny plus.