Bezprzewodowe odtwarzanie dźwięku na Raspberry Pi

Jak bezprzewodowo odtwarzać dźwięk z komputera, tabletu, lub telefonu? Artykuł opisuje możliwość bezprzewodowego odtwarzania dźwięku z urządzeń Windows/Android/Apple z wykorzystaniem Raspberry Pi jako pośrednika.

Źródło dźwięku na Raspberry Pi

Zacznijmy od zwykłego odtwarzania dźwięku na Raspberry Pi. Raspberry Pi posiada wyjście audio 3,5mm. Jakość dźwięku z tego źródła jest jednak niezadowalająca. Dlatego dużo lepszym rozwiązaniem jest podpięcie zewnętrznego przetwornika audio (USB audio DAC). Sam posiadam urządzenie bazujące na układzie PCM2704 (koszt 50-100zł) do kupienia na Allegro, Aliexpress, itd. Przetwornik najlepiej podłączyć za pośrednictwem aktywnego huba USB (z własnym zasilaniem) .

Po podpięciu przetwornika audio należy ustawić go jako domyślne urządzenie wyjściowe dla dźwięku. Wchodzimy do edycji pliku /etc/modprobe.d/alsa-base.conf

sudo nano /etc/modprobe.d/alsa-base.conf

Odnajdujemy linijki zaczynającą się od options snd-usb-audio index i options snd_bcm2835 i zmieniamy je tak aby snd-usb-audio miał priorytet:

options snd-usb-audio index=0
options snd_bcm2835 index=1

alsa-base.conf

Kolejny plik do zmiany to /etc/asound.conf

sudo nano /etc/asound.conf

Podmieniamy jego zawartość na:

pcm.!default {
        type hw
        card 0
        device 0
        channels 2
}

asound.conf

Zapisujemy oba pliki i rebootujemy Raspberry Pi.

sudo reboot

Po ponownym uruchomieniu wchodzimy do alsa-mixer i powinniśmy zobaczyć nasz USB Audio DAC jako domyślne urządzenie do odtwarzania:

alsamixer

Teraz za pomocą aplikacji mpg123 możemy spróbować odtworzyć plik mp3.

mpg123 /mnt/hdd/dowolny.mp3

Przygotowanie odbioru strumienia na Raspberry Pi

Shairport Sync pozwala na bezprzewodowe odtwarzanie dźwięku na Raspberry Pi z dowolnego nadajnika wspierającego AirPlay. Instrukcja do instalacji znajduje się na stronie GitHub projektu. Zestaw poleceń, który zadziałał dla mnie to:

sudo apt-get update
sudo apt-get install build-essential git autoconf automake libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev libao-dev libssl-dev git avahi-utils libwww-perl libcrypt-openssl-rsa-perl libio-socket-inet6-perl  libmodule-build-perl avahi-daemon libavahi-client-dev libssl-dev
git clone https://github.com/mikebrady/shairport-sync.git
cd shairport-sync/
autoreconf -i -f
./configure --with-alsa --with-avahi --with-ssl=openssl --with-metadata --with-systemv
make
sudo make install
sudo update-rc.d shairport-sync defaults 90 10

Konfiguracja aplikacji znajduje się w pliku /etc/shairport-sync.conf. Raspberry Pi rebootujemy i zabieramy się za konfigurację nadajnika. W przypadku urządzeń Apple nie trzeba nic konfigurować, powinniśmy już widzieć Raspberry Pi jako odbiornik AirPlay.

Windows jako źródło dźwięku

Do strumienowania dźwięku z Windows użyłem TuneBlade. Aplikacja w pełni darmowa do opisywanego scenariusza. Instalacja i konfiguracja jest banalnie prosta. Często się jednak zdarza, że domyślny sposób przechwytywania dźwięku przez TuneBlade nie działa. W moim przypadku konieczne było zainstalowanie i skonfigurowanie wirtualnego urządzenia dźwiękowego. Procedura jest opisana na stronie pomocy. Twórcy TuneBlade proponują darmowy VB-Audio Virtual Cable. Instalujemy sterowniki i restartujemy komputer. Na liście urządzeń wejścia/wyjścia dźwięku powinniśmy ujrzeć nowe wirtualne urządzenia.

VB Audio urzadzenia

Przechodzimy do ustawień TuneBlade i zmieniamy źródło strumieniowania z ‚Direct loopback’ na ‚Virtual Device Loopback’ wybierając wirtualne urządzenia w dwóch pozycjach ‚Line-In Device’ i ‚Output Device’.

Tuneblade VB-Audio

Tuneblade powinien już widzieć nasz odbiornik ShairPort – klikamy connect na nasz odbiornik i od tej pory dźwięk będzie strumieniowany do RaspberryPi i powinien być słyszany z głośników. Po aktualizacji do Windows 10 TuneBlade przestał widzieć mój odbiornik. Jeżeli TuneBlade nie widzi naszego odbiornika, a mamy system Windows 10 – warto wyłączyć Protokół internetowy w wersji 6 (TCP/IPv6) w opcjach połączenia sieciowego (Panel sterowania\Sieć i Internet\Centrum sieci i udostępniania -> Zmień ustawienia karty sieciowej -> właściwości wybranej karty sieciowej). Informacja zaczerpnięta z Google grupy Tuneblade.

ipv6

TuneBlade posiada mnóstwo opcji, które są dość proste i nie będę ich opisywał. Korzystanie z VB-Audio uniemożliwia równoczesne strumieniowanie i odtwarzanie dźwięku z głośników komputera. TuneBlade zarządza urządzenia dźwiękowymi tak, że po jego włączeniu zapisuje domyślne urządzenia wejścia/wyjścia i przełącza je na te wybrane w Audio Capture, a po zakończeniu strumieniowania przełącza z powrotem na te uprzednio zapamiętane.

TuneBlade

Dźwięk z Android

Możliwe jest również strumieniowanie z urządzeń Android. Znalazłem aplikację AirAudio teoretycznie darmowa, w praktyce konieczny jest jej zakup (w wersji darmowej po pierwszych 5-ciu minutach nasz dźwięk jest zakłócany ‚piknięciami’). Aplikacja wymaga root’a.

Źródła:

[1] http://raspberrypi.stackexchange.com/questions/19705/usb-card-as-my-default-audio-device

[2] https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=35698

[3] http://www.chickensinenvelopes.net/2016/01/raspberry-pi-airplay-receiver/

[4] http://lifehacker.com/5978594/turn-a-raspberry-pi-into-an-airplay-receiver-for-streaming-music-in-your-living-room

[5] http://drewlustro.com/hi-fi-audio-via-airplay-on-raspberry-pi/

Git Deployment: Używanie Git do publikowania stron internetowych

Jednym z największych problemów występujących przy pracy nad stronami internetowymi jest aktualizacja strony, która jest aktywnie odwiedzana przez użytkowników. Od dłuższego czasu próbowałem różnych rozwiązań, ale zawsze występowały jakieś problemu, lub był element, który mi się nie podobał. Wydaje mi się, że udało mi się w końcu osiągnąć pożądany cel za pomocą systemu kontroli wersji Git.

Zacznę od końca, czyli jak obecnie wygląda proces aktualizowania strony. Posiadam dwie kopie bazy danych (produkcyjną i deweloperską), oraz dwie uruchomione kopie strony(produkcyjną i deweloperską, które korzystają z odpowiadających im baz danych). Testując nowe funkcjonalności, są one od razu widoczne na stronie deweloperskiej (dostępnej tylko dla mnie). Za każdym razem jak zapisuję plik, jest on wysyłany na serwer deweloperski przez FTP. Po przetestowaniu danej funkcjonalności, dodaję odpowiednie pliki do repozytorium, a następnie zmiany są wysyłane do strony produkcyjnej. Istotny jest fakt, że wysyłane są tylko zmiany. Zatem jeśli na serwerze produkcyjnym istnieją pliki (np. obrazki dodane przez użytkowników), które nie występują ani w repozytorium, ani na serwerze deweloperskim, nie zostaną one nadpisane/usunięte. Rozwiązanie niezwykle bezpieczne, a zarazem wygodne.

Do funkcjonowania takiego środowiska potrzebujemy Git zarówno na serwerze, jak i na komputerze lokalnym. Na Windowsa polecam Github, który zainstaluje nam msysGit, Windows PowerShell, oraz aplikację do obsługi repozytoria lokalnych i tych zamieszczonych na Github. Jeżeli używamy Git po raz pierwszy, polecam zapoznać się z biblią Git.

Ustawienia serwera

Na serwerze musimy utworzyć aż dwa repozytoria. Wynika to ze specyfiki działania Git, który nie pozwala na wysyłanie zmian do repozytorium w którym posiadamy „dirty working directory”, lub po polsku brudny katalog roboczy, tzn. zmiany nie dodane do repozytorium. Rozwiązaniem tego problemu jest utworzenie dwóch repozytoriów. Pierwsze repozytorium, nazwijmy je hub, jest repozytorium bare (tzn. bez katalogu roboczego) do którego wysyłamy zmiany z komputera lokalnego. Tworzymy je w lokalizacji niedostępnej dla serwera www, np. w katalogu domowym.

cd ~
mkdir projekt_x.git
cd projekt_x.git
git init --bare

Jeżeli używamy Git na serwerze po raz pierwszy, zostaniemy poproszeni o ustawienie nazwy i adresu email użytkownika, następnie możemy wykonać powyższe instrukcje. Drugie repozytorium (live) tworzymy w katalogu dostępnym dla serwera www, będzie to nasza wersja produkcyjna strony. Jest to już pełnoprawne repozytorium z katalogiem roboczym. Dodatkowo tworzymy pierwszy commit z pustym plikiem.

cd ~/domains/tomaszmazur.eu/public_html/
git init
touch empty
git add empty
git commit -m "Initial commit"

Musimy ustawić repozytorium hub jako zdalne repozytorium naszego nowo utworzonego repozytorium:

git remote add hub ~/projekt_x.git
git push hub +master:refs/heads/master

Już teraz każda zmiana dostępna w repozytorium hub, może zostać ściągnięta do repozytorium live. Chcemy jednak ten proces zautomatyzować, tak aby każda zmiana wysłana do hub była automatycznie pobierana przez live. W tym celu, w repozytorium hub tworzymy „hook” (skrypt) w pliku ~/projekt_x.git/hooks/post-update

#!/bin/sh
 
echo
echo "**** Pulling changes into Live [Hub's post-update hook]"
echo
 
cd ~/domains/tomaszmazur.eu/public_html || exit
unset GIT_DIR
git pull hub master
 
exec git-update-server-info

Skrypt ten będzie wykonany po każdej zmianie wysłanej do repozytorium hub. Skrypt przechodzi do repozytorium live, i pobiera zmiany z repozytorium hub. W sprytny sposób omijając niemożność wysłania zmian. 😉 Nie zapomnijmy nadać praw wykonywania:

chmod +x ~/projekt_x.git/hooks/post-update

Na serwerze wszystko jest już ustawione, pozostaje tylko skonfigurować Git na komputerze lokalnym, tak abyśmy mogli wysyłać zmiany do repozytorium hub.

Ustawienia lokalne

Przechodzimy do repozytorium na komputerze lokalnym za pomocą Windows PowerShell. Jeżeli nie mamy repozytorium to je tworzymy analogicznie jak wcześniej (dodajemy wszystkie niezbędne pliki do repozytorium i tworzymy pierwszy commit). Wystarczy skonfigurować repozytorium zdalne:

git remote add deploy ssh://login@serwer/sciezka/do/projekt_x.git
git push deploy +master:refs/heads/master

Całość naszego repozytorium lokalnego powinna zostać wysłana do naszego repozytorium hub, a stamtąd do repozytorium live. Żeby sprawdzić, czy wszystko przebiegło pomyślnie przechodzimy do repozytorium hub i sprawdzamy za pomocą komendy git log, historię commitów. Podobnie czynimy w repozytorium live, dodatkowo sprawdzamy czy pliki w tym repozytorium rzeczywiście istnieją.

Aktualizowanie strony live

Od tej chwili nanoszenie zmian (commitów) na wersję produkcyjną strony sprowadza się do wykonania komendy:

git push

Prawidłowo wykonana aktualizacja zwraca listę naniesionych zmian:git_push_success

Submodules – Sub moduły

Jeżeli nasz projekt posiada sub moduły, musimy pamiętać o tym, że nie zostaną one automatycznie zaktualizowane, ani zainicjowane w repozytorium live. Teoretycznie możemy odpowiednie komendy dodać do skryptu post-update, ale musimy się liczyć z konsekwencjami tego czynu (zawsze najbardziej aktualna wersja submodułów, niezależnie od wersji posiadanej na komputerze lokalnym). Z tego względu nie zdecydowałem się na automatyzację tej czynności. Przechodzimy do repozytorium live i uruchamiamy komendy:

git submodule init
git submodule update

Submoduły powinny zostać zaciągnięte z repozytoriów zdalnych.

Zabezpieczenie katalogu .git

Może się okazać, że nasz katalog .git w repozytorium live jest dostępny z poziomu przeglądarki internetowej. Blokujemy dostęp, tworząc lub modyfikując plik .htaccess:

RewriteEngine On
RewriteRule \.git - [F,L]

Uwagi końcowe

Należy pamiętać, że tak ustawiony system nie obsługuje aktualizacja/zmian bazy danych. Jeżeli wprowadzimy zmianę w strukturze bazy danych deweloperskiej, musimy pamiętać o naniesieniu tych zmian na bazę danych produkcyjną (z chwilą naniesienia odpowiednich zmian w kodzie). Mogą również wystąpić pewne problemy przy nanoszeniu zmian na repozytorium live, np. dodaliśmy do repozytorium nowy plik, który już istniał w katalogu roboczym repozytorium. Należy wtedy uruchomić Git w obrębie repozytorium live i ręcznie rozwiązać wszelkie konflikty.

Źródła:

  1. Using Git for Deployment – Dan Barber
  2. Using Git to manage a web site
  3. A web-focused Git workflow | Joe Maller

CKEditor: Sprawdzanie pisowni przez przeglądarkę

Sprawdzanie pisowni w CKEditor nie działa najlepiej, dodatkowo język polski nie jest domyślnie dostępny. Najprostszym rozwiązaniem tego problemu jest umożliwienie sprawdzania pisowni przez przeglądarkę. W pliku konfiguracyjnym CKEditor dodajemy ustawienia:

CKEDITOR.editorConfig = function( config )
{
    config.disableNativeSpellChecker = false;
    config.removePlugins = 'scayt,contextmenu';
};

W pierwszej linijce pozwalamy na sprawdzanie pisowni przez przeglądarkę. Kolejna linijka wyłącza scayt, czyli sprawdzanie pisowni wbudowane w CKEditor, oraz wyłącza własne menu kontekstowe w obrębie CKEditor. Ostatnią czynność wykonujemy po to, żeby po kliknięciu prawym przyciskiem myszy na podświetlony wyraz, można było zobaczyć podpowiedzi (przynajmniej tak to działa pod Firefox).

ckeditor_sprawdzanie_pisowni

 

SSH i klucze DSA, czyli logowanie bez wpisywania hasła

Dzisiaj opiszę bardzo przydatną i praktyczną poradę, dotyczącą logowania się na serwer za pomocą SSH. W codziennej pracy możemy się logować na dany serwer nawet po kilkadziesiąt razy, a za każdym razem musimy wpisać hasło. Jest to oczywiście mało wygodne i czasochłonne. Rozwiązaniem tego problemu jest skorzystanie z pary kluczy DSA.

Zakładam, że korzystamy z Windowsa i Putty do logowania się na serwer z Linuksem. Ze strony PuTTY należy ściągnąć PuTTY, PuTTYgen i Pageant. W pierwszym kroku należy wygenerować klucze DSA na serwerze, do którego chcemy się wygodnie logować. Uruchamiamy Putty i logujemy się na serwer tak jak zwykle (cieszymy się faktem, że ostatni raz wpisujemy hasło :)). W katalogu domowym tworzymy folder .ssh z prawami dostępu 700.

mkdir .ssh
chmod 700 .ssh

Następnie uruchamiamy polecenie odpowiedzialne za wygenerowanie klucza:

ssh-keygen -t dsa

Zostaniemy poproszeni o podanie nazwy pliku w którym zostanie zapisany klucz(zostawiamy domyślne), oraz passphrase(hasło zabezpieczające klucz). Passphrase powinno być różne od hasła dostępu do konta. Jeżeli ktoś wszedłby w posiadanie tego klucza, jedyne co dzieli go od zalogowania się na serwer jest właśnie podany passphrase. Można również ustawić pusty passphrase, co jest dość niebezpieczne, ale wygodne. Szczegółowe wyjaśnienia znajdą się pod koniec artykułu. Dopisujemy klucz publiczny id_rsa.pub do pliku authorized_keys.

cat id_dsa.pub >> authorized_keys

Możemy teraz ustawić odpowiednie prawa dla wszystkich plików w katalogu:

chmod 600 *

To by było na tyle jeśli chodzi o pracę na serwerze. Ściągamy plik id_dsa na komputer lokalny. Otwieramy PuTTYgen, z menu wybieramy Actions->load. Wybieramy nasz plik id_dsa i wpisujemy odpowiedni passphrase. Zapiszemy teraz klucz w formacie zrozumiałym dla Putty. Klikamy save public key i zapisujemy pod nazwą moja_nazwa.pub, analogicznie save private key pod nazwą moja_nazwa.ppk. Pliki umieszamy dowolnym katalogu, np. w katalogu z Putty, lub naszym katalogu domym w podfolderze .ssh.

Ostatnim krokiem jest ustawienie Putty tak, aby korzystał z wygenerowanych kluczy. Uruchamiamy Putty i przechodzimy do edycji sesji połączenia z naszym serwerem – klikamy na sesję, a następnie przycisk Load. W zakładce Connection->SSH->Auth w polu Private key file for authentication wybieramy przed chwilą zapisany plik moja_nazwa.ppk. Pamiętajmy, aby wrócić do zakładki Session i klikąć Save. Już teraz powinniśmy móc się zalogować na serwer korzystając z naszego klucza prywatnego. Zamiast hasła wystarczy podać passphrase.

putty_auth

Naszym celem było logowanie się bez podawania hasła, jednak dalej przy każdym logowaniu musimy podawać nieszczęsny passphrase. Aby tego uniknąć musimy skonfigurować Pageant. Jest to narzędzie które przechowuje klucze prywatne w postaci zdekodowanej w pamięci, tak abyśmy nie musieli za każdym logowaniem podawać passphrase do klucza. Jednym sposobem jest każdorazowo uruchamianie Pageant i dodanie klucza z poziomu menu dostępnego w programie. Lepszym sposobem jest utworzenie skrótu do Pageant, który automatycznie załaduje wybrany klucz. Tworzymy zwykły skrót do Pageant, edytujemy jego właściwości, w polu element docelowy po spacji dopisujemy lokalizację naszego klucza prywatnego. Wygląda to następująco:

C:\Programy\putty\pageant.exe C:\Programy\putty\moj_klucz.ppk

Uruchamiamy Pageant poprzez utworzony skrót, zostaniemy poproszeni o podanie passphrase. Od tej chwili klucz rezyduje w pamięci i nie ma potrzeby już więcej wpisywać passphrase. Taki skrót możemy umieścić w autouruchamianiu Windowsa, lub uruchamiać za każdym razem jak rozpoczynamy pracę na serwerze. Tak skonfigurowane Putty pozwala również na korzystanie z TortoiseSVN bez podawania hasła, o ile korzystamy z protokołu SSH.

Chciałbym jeszcze wrócić do kwestii pustego passphrase. Wiemy już kiedy musimy go podawać, w optymistycznym przypadku jest to raz na dzień (pracy). Ustawiając pusty passphrase nie musimy go podawać w ogóle. Rozwiązanie bardzo wygodne, ale musimy się liczyć z konsekwencjami. Każdy kto wejdzie w posiadanie naszego klucza prywatnego będzie w stanie się zalogować na serwer bez znajomości hasła/passphrase.

W przeszłości, na jednym z serwerów napotkałem problem z logowaniem się za pomocą kluczy DSA. Rozwiązaniem było zmodyfikowanie klucza publicznego po stronie serwera. Z końcówki pliku authorized_keys (zakładam, że plik posiada tylko jeden klucz, tj. jedną linijkę) musimy usunąć część nazwy domeny, tak aby końcówka pliku wyglądała tak jak nazwa użytkownika i domeny w tytule okienka z Putty.

putty_keys

Sublime Text – kompromis pomiędzy IDE a notatnikiem

Postanowiłem wrócić do pisania bloga, a także do zgłebiania tajemnic CakePHP. Od czasu ostatniego postu na temat CakePHP wiele się zmieniło. Pojawiła się całkiem nowa linia 2.0, a także kolejne wersje 2.1 i 2.2. Przez ten czas stworzyłem kilka stron/aplikacji, w tym również działające pod CakePHP 2.x, a także zaktualizowałem jedną stronę z CakePHP 1.3 -> 2.0. W tej chwili jestem w trakcie przepisywania tej strony na CakePHP w wersji 2.2.3. Niestety skrypty mające automatycznie przekonwertować aplikację z wersji 1.3 na 2.0 w moim przypadku nie zadziałały. Nie jest to jednak skomplikowana aplikacja i przepisanie jej na nowo może przynieść jej tylko korzyści – tym bardziej, że wiele rzeczy chciałbym zmienić.

Od dłuższego czasu pracowałem z użyciem środowiska Netbeans. Środowisko to jednak staje się co raz bardziej powolne, nieresponsywne, pojawiają się różne kłopoty wymagające restart środowiska. Postanowiłem więc wypróbować coś nowego. Zaczęłem poszukiwania lekkiego środowiska, które w jakiś sposób wspiera CakePHP. Dość szybko natrafiłem na najnowszy post na moim ulubionym blogu o CakePHP zatytułowany IDE’s are a thing of the past, and ST is here to save the day. Pierwsza myśl – otwieram zainstalowany Notepad++ i zastanawiam się co mi w nim brakuje do pełni szczęścia: synchronizacji przez FTP, widoku całego projektu i uzupełniania kodu. No i oczywiście wsparcia dla CakePHP w postaci np. możliwości korzystania z autouzpełniania metod danego modelu przywołanego w kontrolerze. Prawdopodobnie wszystkie te braki można uzupełnić za pomocą różnych pluginów. Zdecydowałem się jednak wypróbować Sublime Text.

Pierwsze chwile z Sublime Text przerosły moje najśmielsze oczekiwania. Aplikacja (bo środowiskiem nie można nazwać) uruchamia się natychmiast. Nie trzeba jak w przypadku Netbeans czekać… i czekać. ST posiada bardzo elegancki, minimalistyczny interfejs. Okazuje się, że posiada jednak wszystko co potrzebuję. Jedną z najlepszych funkcji to „Goto anything”. Działa dużo lepiej niż się można spodziewać! Po zapoznaniu się ze składnią można w dosłownie sekundę przejść do konkretnego pliku w konkretnym miejscu – czy to funkcja czy linia. Nie spotkałem się z taką funkcjonalnością w żadnym IDE ani edytorze tekstowym. Naprawdę rewelacja! Pierwsza rzecz którą robię to tworzę nowy projekt w katalogu z kodem mojej strony. W zasadzie cały ekran jest zajęty przez kod, a jedyna rzecz widoczna poza nim to sidebar, który zawiera tylko drzewo z widokiem projektu.

Pierwszym krokiem na drodze do skonfigurowania ST do moich potrzeb było zainstalowanie Sublime Package Control. Jest to dodatek (package) służacy do zarządzania innymi dodatkami – instalowaniem nowych, aktualizowanie oraz usuwanie zainstalowanych. Warto wspomnieć, że dodatek automatycznie aktualizuje zainstalowane dodatki. Wszystkim zarządza się z poziomu menu Preferences->Package Control. Drugim dodatkiem, który zainstalowałem był Sublime SFTP. Dodatek robi to co się od niego oczekiwałem, po jego skonfigurowaniu ST automatycznie wgrywa zmieniony plik na wskazany w konfiguracji adres. Konfiguracja dodatku to oczywiście plik tekstowy zawierający JSON. W zasadzie wszystkie ustawienia są przechowywane w plikach z JSON. Ta koncepcja bardzo mi się podoba, łatwo jest do wszystkiego dotrzeć, nic nie umknie naszej uwadze.

Inne dodatki warte wypróbowania to:

  • Search CakePHP API – pozwala przeszukiwać API CakePHP z poziomu Sublime Text. Posiada kilka możliwości wywołania np. zaznaczenie tekstu do wyszukania + wybranie opcji z menu kontekstowego, zaznaczenie tekstu i skrót klawiszowy, lub skrót klawiszowy i wpisanie tekstu do wyszukania. Bardzo przydatne, oszczędza kilka kliknięć.
  • Search CakePHP Book – analogicznie jak wyżej, z tym wyszukiwanie następuje w Cook Book. Dodatek (przynajmniej dla mnie) mniej przydatny, ale uznałem, że warto o nim wspomnieć.
  • SideBarEnchancements – dodaje możliwość tworzenia nowych plików, folderów z poziomu paska bocznego
  • BrowserRefresh – za pomocą jednego skrótu klawiszowego zapisuje aktualny plik, odświeża aktywną kartę w wybranej przez nas przeglądarce i przełącza przeglądarkę na pierwszy plan. Cudo!

Ogromną listę dodatków do Sublime Text można znaleźć tutaj. W najbliższym czasie na pewno wypróbuję kilka nowych dodatków, ale nie chciałbym przesadzić. Płynność z jaką chodzi ST jest jego najwięskzą zaletą, której nie chciałbym utracić. Polecam również zapoznać się z wideo tutorialem o ST, oraz dokumentacją. Istotna kwestia którą pominąłem to tzw Snippets. Nie zdążyłem się z nimi zbyt dobrze zapoznać, ale jest to bardzo przydatna funkcjonalność, pozwalająca zaoszczędzić mnóstwo czasu w trakcie pisania projektów – wkrótce na pewno pojawi się wpis na ten temat. Czas który chciałem przeznaczyć na poprawę funkcjonalności strony został przeznaczony na zabawę z Sublime Text, jednak na pewno nie jest to czas stracony.

CakePHP: JSON w widokach

Pomysł jak i klasa do realizacji pomysłu są zaczerpnięte z tego wpisu. Jeżeli mamy akcję, której zadaniem jest zwrócić wynik w JSON; zazwyczaj nie ma potrzeby korzystania z plików widoków i layoutów. Niektórzy pokusiliby się o wyłączenie autorenderowania i następnie wpisali echo json_encode($something);.Bardziej doświadczeni programiście wiedzą, że jest to niezgodne z architekturą MVC. Do tego celu stworzona została klasa JsonView. Poniższy kod ląduje w pliku app/views/json.php.

class JsonView extends View {
  var $content = null;
  function __construct(&$controller, $register = true) {
    if (is_object($controller) && isset($controller->viewVars['json'])) {
      $this->content = $controller->viewVars['json'];
    }
    if ($register) {
      ClassRegistry::addObject('view', $this);
    }
    Configure::write('debug', 0);
  }
 
  function render($action = null, $layout = null, $file = null) {
    if ($this->content === null) {
      $data = '';
    } else {
      $data = json_encode($this->content);
    }
    return $data;
  }
}

Teraz w naszej akcji, której zadaniem jest wypisać obiekt JSON wystarczy wpisać:

function my_action() {
    $this->view='json';
    $json = array('myobject'=>true,'mvc'=>'not broken');
    $this->set(compact($json));
}

W odpowiedzi akcji powinniśmy otrzymać:

{"myobject":true,"mvc":"not broken"}

Mechanizm bardzo prosty w implementacji, pozwalający znacząco przyspieszyć pisanie kodu z użyciem JSON.

CakePHP: Linki absolutne

Ostatnio napotkałem problem tworzenia linków w mailu wysyłanym z sekcji administratorskiej aplikacji napisanej w CakePHP. Linki tworzyłem za pomocą metody link w HtmlHelper.

echo $this->Html->link('Link'
    array(
        'controller'=>'contents'
        ,'action'=>'view'
        ,'slug'=>'my-custom-slug'
    )
);

Tak utworzony link tworzony jest z prefixem ‚admin’; jakoże został utworzony z poziomu sekcji admin, która jest uzyskana za pomocą takiego prefixu. Pozbywamy się prefixu podająć w tablicy drugiego parametru element o kluczu takim jak nazwa prefixu, a w wartości prefixu FALSE. Zaleca się aby w każdym tworzonym linku anuluwać nieużywane, a zdefiniowane prefixy. Dla przypomnienia prefixy te zdefiniowane są w pliku app/config/routes.php.

echo $this->Html->link('Link'
    array(
        'controller'=>'contents'
        ,'action'=>'view'
        ,'slug'=>'my-custom-slug'
        ,'admin'=>FALSE
    )
);

Teraz wystarczy tylko uzyskać link absolutny, czyli zaczynający się od http://tomaszmazur.eu/ zamiast standardowego /. W tablicy drugiego parametru tworzymy element o kluczu full_base i wartości TRUE.

echo $this->Html->link('Link'
    array(
        'controller'=>'contents'
        ,'action'=>'view'
        ,'slug'=>'my-custom-slug'
        ,'admin'=>FALSE
        ,'full_base'=>TRUE
    )
);

CakePHP: Jeden silnik dla wielu aplikacji

Jak posiadać jedną kopię framework’a dla wielu aplikacji? Standardowe podejście to jeden silnik dla jednej aplikacji. Zakładając, że na serwerze posiadamy 10 aplikacji, w chwili gdy zechcemy zaktualizować silnik, musimy wgrać nową wersję 10 razy. Dodatkowo posiadamy 10 identycznych kopii źródeł CakePHP. Rozwiązaniem tego problemu jest posiadanie jednego silnika CakePHP wykorzystywanego przez wszystkie 10 aplikacji.

Na moim serwerze struktura katalogów wygląda następująco:

~/domains/domena1.pl/public_html/
~/domains/domena2.com/public_html/

W każdym z tych katalogów posiadam 2 działające aplikacje CakePHP, zawartość tych katalogów to:

app/
cake/
plugins/
vendors/
.htaccess
index.php

W momencie, gdy będę chciał zastosować strategię ‚jeden silnik dla wielu aplikacji’, zawartość folderu ograniczy się do:

app/
.htaccess
index.php

Pozostałe foldery zostaną przeniesione do innej dowolnej lokalizacji. W moim przypadku przenoszę je do ~/cakephp_nr_wersji. Otrzymuję przykładowy efekt:

~/cakephp_1_3_10/cake
~/cakephp_1_3_10/plugins
~/cakephp_1_3_10/vendors/

Aby aplikacje wiedziały, gdy znajduje się silnik CakePHP, musimy dokonać modyfikacji pliku app/webroot/index.php
ustawiając stałą CAKE_CORE_INCLUDE_PATH na ścieżkę do katalogu zawierającego katalogi cake i vendors (u mnie jest to linia #53). W wyżej opisanym przykładzie wyglądałoby to następująco:

define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(ROOT))).DS.'cakephp1_3_10');

Zmiany musimy oczywiście dokonać w każdej aplikacji z osobna, lecz jest to działanie tylko jednorazowe. Możemy pójść krok dalej i wyrzucić zawartość katalogu app bezpośrednio do public_html, ale o tym w oddzielnym wpisie.

Linux: Kilka przydatnych komend dla programistów

Przedstawię kilka komend linuxowych, które przydają się w codziennej pracy programisty. Niekażdy programista zna linuxa na wylot, w większości przypadków wystarczy znajomość podstaw i kilku, specyficznych dla danej działalności komend. Polecenia są dość jednoznaczne, dlatego nie zamieszczam dokładnego opisu komend.

  1. Zrzut/kopia bazy danych
    mysqldump -h localhost -u username -p database > file.sql
    -p oznacza, że konieczne jest podanie hasła dla użytkownika.
  2. Wgranie/import bazy danych
    mysql --host localhost --user username -p database < file.sql
    -p oznacza, że konieczne jest podanie hasła dla użytkownika.
  3. Policzenie liczby plików/folderów w danym katalogu
    ls /my/dir/ -l | wc -l
  4. Wylistowanie plików/katalogów zawierających w nazwie określony ciąg znaków
    ls /my/dir | grep ".jpg"Komendy z punktów 3 i 4 można połączyć
    ls /my/dir -l | grep ".jpg" | wc -lPost będzie uzupełniany w miarę potrzeb.

SQL: Przechowywanie IP w bazie danych

Wpis dotyczy tematu przechowywania adresów IP(dla ścisłości IPv4) w bazie danych MySQL. Najczęściej spotykanym sposobem przechowywania takiego adresu IP jest pole VARCHAR. Jest to zdecydowanie niewłaściwe podejście, wymaga zastosowania pola co najmniej VARCHAR(15), jednak w praktyce można spotkać nawet VARCHAR(100). Polecanym rozwiązaniem jest skorzystanie z pola UNSIGNED INT(4). Aby jednak wcisnąć adres IP do tego pola, potrzebna jest jego pewna obróbka, na szczęście zarówno PHP jak i MySQL posiadają funkcje pozwalające na obustronna konwersje adresów. Przejdźmy do praktyki i utwórzmy nasze pole:

CREATE TABLE IF NOT EXISTS `ips` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `ip` int(4) unsigned NOT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Aby wstawić do bazy danych adres IP 127.0.0.1 możemy skorzystać z zapytania:

INSERT INTO `ips` (ip) VALUES(INET_ATON('127.0.0.1'))

W bazie danych pojawi się wpis, który w polu ip będzie zawierał adres IP:

2130706433

Co pozwoli nam na odczyt adresu IP w znanej nam formie. Jak wspomniałem, czynności te można również wykonać z poziomu PHP. Służą do tego odpowiednio funkcje ip2long i long2ip

$ip = '127.0.0.1';
$encoded = ip2long($ip);
echo $encoded; //wyświetli 2130706433
$ip_2 = long2ip($encoded);
echo $ip_2; //wyświetli 127.0.0.1

Jak widać korzystanie z tego sposobu przechowywania adresów IP w bazie danych nie jest takie straszne jak mogłoby się wydawać. Pozatym, że rozwiązanie to jest dużo wydajniesze od zwykłego pola VARCHAR. Należy wspomnieć że udostępnia dodatkową funkcjonalność jak np. wyszukiwanie adresów IP z danego zakresu adresów, sprawdzanie czy adres należy do danej puli itd.