Usunięcie nieużywanych tagów z bazy WordPress’a

Czasami zachodzi potrzeba „ręcznego” kasowania postów z WordPress’a, jako że system ten działa na MySQL MyISAM to nie wspiera kaskadowego kasowania zależnych danych.

Skasowanie postów jest stosunkowo proste:

DELETE FROM wp_posts WHERE conditions;

Trudniejsza sprawa jest ze skasowaniem tagów. Na necie znalazłem poniższe zapytanie, działa wyśmielicie:

DELETE a,b,c
FROM
	wp_terms AS a
	LEFT JOIN wp_term_taxonomy AS c ON a.term_id = c.term_id
	LEFT JOIN wp_term_relationships AS b ON b.term_taxonomy_id = c.term_taxonomy_id
WHERE (
	c.taxonomy = 'post_tag' AND
	c.count = 0
	);

Czasami jednak pole „count” w relacji „wp_term_taxonomy” zawiera niepoprawne dane (liczbę większą od 0), wtedy nieużywane tagi musimy zidentyfikować poprzez LEFT JOINa z warunkiem ID IS NULL. Poniżej zapytanie wyświetlające nieużywane tagi.

SELECT DISTINCT wp_terms.slug FROM wp_terms wt
	INNER JOIN wp_term_taxonomy wtt ON wt.term_id=wtt.term_id
	INNER JOIN wp_term_relationships wtr ON wtr.term_taxonomy_id=wtt.term_taxonomy_id
	LEFT JOIN wp_posts wp ON wp.ID=wtr.object_id
WHERE
	taxonomy='post_tag'
AND 
	ID IS NULL
AND 
	NOT EXISTS(SELECT * FROM wp_terms wt2
                INNER JOIN wp_term_taxonomy wtt2 ON wt2.term_id=wtt2.term_id WHERE wtt2.parent=wt.term_id)
ORDER BY name

Code Spaces – hosting SVN i nie tylko

Przy tworzeniu swoich projektów od jakiegoś czasu korzystam z usług Code Spaces. Jest to serwis, który udostępnia hosting repozytoriów SVN + podstawowe narzędzia do zarządzania projektem.

Przy pracy nad większym projektem, w którym zaangażowanych jest więcej niż jedna osoba praca z repozytorium SVN jest nieodzowna a zarządzanie projektem oraz przepływem informacji jest równie ważna. Właśnie to ułatwia Code Spaces.

W Code Space umożliwia:

  • hostowanie repozytorium SVN,
  • przeglądanie repozytorium SVN online,
  • zarządzanie projektami, użytkownikami i ich prawami do projektów,
  • zarządzanie zadaniami, błędami (powiadomienia email),
  • zarządzanie dokumentami projektowymi,
  • zarządzanie mini 'project wiki’,
  • komunikowanie się w zespole na forum.

W system został wyposażony w przyjemny dla użytkownika interfejs, dostępne są statystyki commit’ów, najnowsze zadania, odliczany czas do kolejnego 'kamienia milowego’…

Ceny w Code Spaces nie są odstraszające, pakiet 'Micro ISV’ powinien starczyć na większość dzisiejszych startup’ów. Usługi Code Spaces można za darmo testować przez 45 dni.

Baza danych miejscowości w Polsce z podziałem na województwa w formacie CSV

Sam szukałem w necie… żadnej konkretnej listy nie znalazłem wiec zostałem zmuszony samemu ją wygenerować z danych dostępnych na stronie GUS.

Może komuś jeszcze się przyda:

Miejscowości to łącznie miasta (897) i wsie. Powyższa lista obejmuje wszystkie miasta i większe wsie na które dotarł GUS. Lista wszystkich wsi to podobno około 65 000 więc jeżeli ktoś potrzebuje dane wszystkich wioch to będzie musiał je samemu zebrać. Imho szkoda sobie nimi baze i interface serwisu zapychać.

Kodowanie plików UTF-8 (unicode). Stan danych na 2009 rok.

Skróty klawiaturowe w Eclipse PDT

Poniżej lista mniej znanych a równie przdatnych co Crtl+C skrótów klawiaturowych. Dzięki używaniu ich przy pracy można znacznie przyspieszających prace w Eclipse PDT.
Poniższą listę postaram się na bieżąco aktualizować.

Ctrl + Shift + L – Lista dostępnych skrótów klawiaturowych.

Ctrl + M – Minimalizowanie lewej kolumny (outline, explorer etc…).

Ctrl + Shift + R – Wyszukiwarka plików w projekcie, uwzględnia wyrażenia regularne.
Ctrl + O – Wyszukiwarka danych w typie (listing metod, atrybutów klasy).
Ctrl + E – Wyszukiwarka otwartych plików

Alt + Shift + R na zaznaczonym w browserze/explorerze plików – Możliwość edycji nazwy pliku.

Alt + strzałka Left / Right – Przeskoczenie pomiędzy ostatnio edytowanymi plikami do lokalizacji gdzie wykonywana była ostatia zmiana. Strzałka w lewo cofa się w historii, strzałka w prawo idzie do przodu.

Ctrl + Shift + X – Przekształca zaznaczony tekst na wielkie litery
Ctrl + Shift + Y – Przekształca zaznaczony tekst na małe litery

Ctrl + Shift + P – Przejście do klamry zamykającej blok.

Ctrl + Shift + / – Zakomentowanie kodu poprzez /* */
Ctrl + / – Zakomentowanie/odkomentowanie kodu poprzez //

Ctrl + L – Przejście do lini XX

Alt – Up / Down – Przesunięcie bieżącej lini w górę / w dół
Ctrl + Alt + Up / Down – Skopiowanie bieżącej linii

Tab + zaznaczone kilka linii tekstu – przesunięcie tekstu o tabulator w prawo
Shift + Tab + zaznaczone kilka linii tekstu – przesunięcie tekstu o tabulator w lewo

Ctrl + K + zaznaczenie zmiennej – przskoczenie do następnego wystąpienia zmiennej w pliku
Ctrl + Shift + K + zaznaczenie zmiennej – przskoczenie do poprzedniego wystąpienia zmiennej w pliku

Zrzut danych z bazy do pliku w MySQL

Metoda I

Zapytanie z poziomu bazy danych.

mysql> SELECT *
    -> INTO OUTFILE 'c:/data.csv'
    -> FIELDS TERMINATED BY ','
    -> ENCLOSED BY '"'
    -> ESCAPED BY '\\'
    -> LINES TERMINATED BY '\r\n'
    -> FROM table_to_export;
Query OK, 20 rows affected (0.02 sec)

Metoda II

Zapytanie z poziomu konsoli bez logowania się do bazy danych.

mysql -u user -h host -p --quick -e 'SELECT * FROM table_to_export' baza > data.txt

Warto pamiętać, że w metodzie I plik zapisywany jest po stronie serwera mySQL a w metodzie II po stronie klienta mySQL. Zatem gdy łączymy się z bazą danych z innego serwera niż wykonujemy zapytanie to należy użyć metody II.

Edycja danych ‘inline’ dzięki Jeditable i jQuery

Jeditable to bardzo fajny plugin do jQuery dzięki któremu można stworzyć mega szybką i prostą edycję danych bez przeładowania. Działa to tak, że np. blok tekstowy objęty odpowiednim <div> po kliknięciu zamienia się w <textarea>, następnie tekst w nim zawarty można edytować i zapisać.

Instalacja i korzystanie z Jeditable jest banalne. Trzeba zaincludować bibliotekę jQuery, plugina Jeditable oraz nałożyć odpowiednią klasę na edytowalny tekst i to wszystko. Aby zmienione dane zapisywały się oczywiście trzeba odpowiednio zaprogramować back-end.

Przejdź na stronę Jeditable oraz zobacz podstawowe przykłady i zaawansowane przykłady.

Wyrażenia regularne unicode

Problem

Mamy formularz z polami, które musimy zwalidować pod kątem poprawności danych. Pole może zawierać jedynie litery (duże, małe – wszystko jedno).

Jakim wyrażeniem regualarnym realizujemy sprawdzanie? Pierwsza myśl to [a-zA-Z]… niestety walidacja nie zadziała poprawnie, gdyż w zakres [a-z] uwzględnia jedynie 26 liter alfabetu łacińskiego natomiast nie uwzględnia znaków diakrytycznych czyli litery [ą, ć, ę, ł, ń, ó, ś, ź, ż] nie spełnią kryterium walidacji.

Zatem aby walidacja działała poprawnie trzeba by dodać do zbioru [a-zA-Z] litery ze znakami diakrytycznymi. Otrzymujemy zatem wyrażenie: [a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]. Wyrażenie to zadziała poprawnie, jednak jest narażone na potencjalne błędy, łatwo zapomieć o którejś literze lub zrobić literówkę. W przypadku gdy tworzymy aplikacje multijęzykową czynność tą będziemy musieli powtórzyć dla każdego z obsługiwanych języków.

Poprawne rozwiązanie

W programowaniu jak już coś sie robi to należy zrobić to porządnie, tak aby swojego kodu nie trzeba było poprawiać, modyfikować etc…

Z pomocą przychodzi nam kodowanie UNICODE, w które umożliwia napisać reguły na litery posiadające znaki diakrytyczne. Wyrażenie \p{L} oznacza jakąkolwiek literę w jakimkolwiek języku, może by być: Ł, ź, ü, ß czy cookolwiek co jest literą (także nie łacińską). Zatem nasze wyrażenie walidujące pole zawierające włącznie litery będzie wyglądać następująco:

preg_match('#^\pL+$#', 'MałyŁoß');

Gwoli wyjaśnienia \pL to skrót od \p{L} a \p{L} oznacza znak (code point) unicode \p należący do kategorii liter {L}.

Klasy/kategorie znaków w unicode:

\p{L} – wszystkie litery
\p{M} – wszystkie znaki, które samotnie nie występują, musi być połączony z innym znakiem i składa się na np. akcent, umlaut, etc…
\p{Z} – wszystkie znaki niewidoczne
\p{S} – wszystkie symbole, znaki walut, znaki matematyczne
\p{N} – wszystkie znaki numeryczne
\p{P} – wszystkie znaki interpunkcyjne: przecinki, średniki etc…
\p{C} – wszystkie niewidoczne znaki kontrolne i niewykorzystywane znaki code point

Przeczytaj więcej o właściwościach wyrażeń regualrnych w unicode.

Aktualny czas a transakcja w PostgreSQL

Ostatnio miałem ciekawy case’ik – musiałem w obrębie jednej transakcji zmienić dane rekordów. Wszystko odbywało się w kilku funkcjach plpgsql, funkcje mogły się wywoływać rekurencyjnie w triggerach a zmiany dotyczyły między innymi pól typu TIMESTAMP. Wartości pól TIMESTAMP rekordów były zmieniane i ich zmiany automatycznie wpływały na w działanie skryptu.

Jakież było moje zdziwienie gdy skrypt nie chciał działać, a wszystkie rekordy miały taką zamą wartość pola typu TIMESTAMP. Otóż okazało się, że po rozpoczęciu transakcji funkcje zwracające aktualny czas:

SELECT CURRENT_TIME;
SELECT CURRENT_TIMESTAMP;
SELECT NOW();

przestały zwracać aktualny czas a zamiast tego czas rozpoczęcia transakcji.

I o dziwo jest to rzeczywiście poprawne działanie. Na stronie http://www.postgresql.org/docs/8.3/static/functions-datetime.html można przeczytać:

Since these functions return the start time of the current transaction, their values do not change during the transaction. This is considered a feature: the intent is to allow a single transaction to have a consistent notion of the „current” time, so that multiple modifications within the same transaction bear the same time stamp.

Zatem wyjściem z tego problemu jest użycie postgres’owej funkcji timeofday() i zrzutowanie jej wyniku na TIMESTAMP, realizyje to zapytanie:

SELECT cast( timeofday() AS timestamp);

Edit: zamiast timeofday() można uzyć funkcji clock_timestamp(), która zwraca date w postaci TIMESTAMP’a

SELECT clock_timestamp();

Dla ciekawości sprawdziłem jak zachowują się inne dialekty SQL i okazało się, że zarówno MySQL jak i MSSQL realizują tą sytuację odmniennie niż PostgreSQL, zatem:

  • MySQL – w transakcji zwracany jest bieżący czas (nie czas rozpoczęcia transakcji)
  • MSSQL – w transakcji zwracany jest bieżący czas (nie czas rozpoczęcia transakcji)
  • PgSQL – w transakcji zwracany jest czas rozpoczęcia transakcji

Sztuczki w wordpress’ie: przeciążanie standardowych funkcji dostępnych w szablonie

Gdy programujemy w szablonie skórki WordPress’a możemy korzystać z całej masy funkcji dostępnych w engine WordPress’a. Niektóre z nich zwracają wartości, inne od razu drukują na ekran, zwykle są parametryzowane i możliwość konfiguracji jest wystarczająco duża.

Problem

Niestety z czasem zawsze przychodzi pewne 'ale’… chcielibyśmy aby standardowa funkcja wordpressa działała minimalnie inaczej. Zatem musimy zmodyfikować jej kod… ale stop nie możemy tak bezczelnie nadpisywać funkcji wordpress.Takie hamskie haki są niedopuszczalne z kilku powodów:

  • Utrudniamy przyszłe aktualizacje, po każdej aktualizacji będziemy musieli pamiętać aby w pliku XXX w linii YYY wstawić swojego hacka.
  • Ta sama funkcja może być wywoływana w innym pliku szablonu, może to powodować błędne/niechciane działanie. Pozatym gdy zmienimy skórkę na inną to hack pozostanie dalej mimo, że już go nie będziemy potrzebować
  • Hack’owanie kodu jakichkolwiek bibliotek/frameworków czy takich gotowych kombajnów jak wordpress jest sprzeczne z zasadami programowania.

Zatem jak sobie z tym problemem poradzić?

Wszelkie gotowe rozwiązania powinno się rozszerzać nie modyfikując przy tym źródeł samej głównej aplikacji. Jeżeli takie rozszerzenie jest nie możliwe to oznacza, że aplikacja została źle napisana i należy poszukać innej rozszerzalnej.

Czy rozszeżać funkcje w WordPress?

Oczywiście. Posłużymy się w tym celu trochę zapomnianym plikiem szablonów: functions.php. Jeżeli plik functions.php nie istnieje w katalogu głównym naszej skórki to go tworzymy. Plik ten jest automatycznie ładowany wraz z uruchomieniem WordPress’a, można w nim umieszczać swoje dedykowane dla skórki funkcje oraz jak w naszym przypadku funkcje speudo przeciążające.

Zwykle w szablonach nie ma tego pliku, także świadomość programistów wordpress’owych na temat jego jest niewielka. Zatem radze sobie zapamiętać o takiej możliwości!

Dobra, a więc załóżmy, że chcemy zmodyfikować działanie funkcji the_content_rss. Tworzymy zatem w pliku functions.php funkcję my_the_content_rss.

function my_the_content_rss($more_link_text='(more...)', $stripteaser=0, $more_file='', $cut = 0, $encode_html = 0) {
	global $id;

	ob_start();
	the_content_rss('', $stripteaser, $more_file, $cut, $encode_html);
	$bufferContent = ob_get_contents();
	ob_end_clean();

	$bufferContent = preg_replace( '/\.\.\.\s*$/', '', $bufferContent );
	$bufferContent .=  '&lt;span class="read-on"&gt;&lt;a href="'. get_permalink() . "#more-$id\" class=\"more-link\"&gt;$more_link_text&lt;/a&gt;&lt;/span&gt;";
	
	print $bufferContent;
}

Funkcja my_the_content_rss musi przyjmować co najmniej takie parametry parametry jak oryginalna funkcja the_content_rss. Wywołuje oryginalną funkcję, jako że the_content_rss nie zwraca wartości tylko drukuję ją na ekran musimy zatem poprzez ob_start() włączyć buforowanie, od tej pory wszystkie dane, które miały być drukowane są zapisywane w buforze, następnie poprzez ob_get_contents() odczytujemy ten bufor i kończymy buforowanie ob_end_clean().

Teraz mając w zmiennej $bufferContent wynik działania oryginalnej funkcji możemygo dostosować do naszych indywidualnych wymagań w przykładzie jest to wycięcie „…” oraz wstawienie linka do szczegółów posta.

Tyle. Następnie w szablonie w miejscach, w których potrzebujemy niestandardowy wygląd/dane szablonu wywołujemy zamiast the_content_rss nowo stworzoną funkcję my_the_content_rss.

Postgres 8.3 a zgodność typów danych

Około rok temu została wypuszczona wersja 8.3 systemu bazodanowego postgreSQL, teraz po roku nadszedł wreszcie czas kiedy to firmy hostingowe zaczynają upgrate’ować bazy do tej wersji.

W wersji 8.3 zostało prowadzonych wiele przydatnych funkcjonalności jak chociażby wyszukiwanie pełnotekstowe TSearch2, jednak nie o tym miałem pisać…

Oprócz dodatkowych funkcjonalności w postgres 8.3 została wprowadzona kontrola zgodności typów danych. Po przejściu providera hostingu z pg 8.x.x na pg 8.3.x istnieje realne zagrożenie błędnego działania naszych aplikacji w przypadku gdy nie zachowywaliśmy zgodości typów danych.

W postgreSQL operatory (np. =, > etc..) i funkcje (np. substr(), regexp_replace() etc…) posiadaja zadeklarowane typy danych na których działają np. INT=INT, substr(TEXT, INT, INT).

Problem

W wersjach przed postgreSQL 8.3 jeżeli operator lub funkcja, która przyjmowała wartości tekstowe została wywołana z wartościami o typie nie tekstowym to wartość parametru był automatycznie przekształcania do typu tekstowego TEXT. Od wersji postgreSQL 8.3 automatyczna konwersja typów została zlikwidowana gdyż jej idea nie jest zgodna z poprawnymi zasadami programowania. To my programiści powinniśmy kontrolować przepływ, wartości i typy danych. Pozostawiona została jedynie automatyczna konwersja w obrębie typów tekstowych (CHAR(X), VARCHAR(X) są CAST’owane do TEXT).

Zatem w najnowszej wersji postgreSQL zapytanie:

SELECT substr(id, 1, 1 ) FROM ...;

gdzie id jest typu integer zwróci błąd „function does not exist” gdyż nie ma wbudowanej funkcji substr(INT, INT, INT)

Analogiczny błąd zwróci operator =

SELECT ... WHERE id = foo;

gdzie id jest typu INTEGER, a foo typu CHAR zwróci błąd „operator does not exist” gdyż nie ma wbudowanego operatora INT = CHAR.

Rozwiązanie

Możliwe są trzy rozwiązania:

  1. Poprawimy zły typ kolumn, które powodują błędy
  2. W każdym zapytaniu zrzutujemy zmienną na dobry typ
  3. Napiszemy własne funkcje, operatory obsługujące te niestandardowe operacje.

1. Poprawa typu kolumny

Możemy zmodyfikować typ kolumny np. z CHAR na INT i wtedy zapytanie wykorzystujące operator INT = INT zadziała poprawnie.

ALTER TABLE sample ALTER COLUMN foo TYPE INTEGER;

Jednak operacja ta nie zawsze może być możliwa. W przypadku źle zaaprojektowanej aplikacji, konwersja taka może okazać się niemożliwa. Co w wypadku gdy pole 'foo’ zawiera ciąg liczb zaczynających się od zera? Przed konwersją przykładowy ciąg może wyglądać '0012388′ po konwersji na typ liczbowy ayutomatycznie zmieni się na '12388′ gdyż INTEGER nie może posiadać zer na przedzie. Ta zmiana może spododować, że kolejne zapytania pomimo prawidłowej składni syntaktycznej zwrócą nieprawidłowe wyniki, pozatym możemy stracić w bazie pewne informacje (zera na przedzie).

2. Castowanie typów w zapytaniu

Gdy zmiana typu kolumny jest niemożliwa pozostaje nam dostosować naszą aplikację do wymagań postgresa. W zapytaniu możemy zrzutować wartość zmiennej na żądany typ

SELECT ... WHERE id = foo::integer;

lub odpowiednik

SELECT ... WHERE id = CAST(foo AS integer);

Minusem takiego rozwiązania jest to, że kolumne 'foo’ należy zrzutować w każdym zapytaniu.

3. Własna funkcje lub operator

Zamiast poprawiać wszystkie zapytania, możemy napisać funkcje/operator który obsłuży żądaną operację.

Tworzenie operatora składa się z dwóch etapów: stworzenie funkcji w języku proceduralnym pl/pgSQL, która obsługuje operację i definicję samego operatora.

Funkcja INTEGER = CHAR

CREATE OR REPLACE FUNCTION equal_integer_vs_char(integer, char) RETURNS bool AS $$
DECLARE
    left_arg ALIAS FOR $1;
    right_arg ALIAS FOR $2;
BEGIN
    IF left_arg = CAST( right_arg AS INTEGER ) THEN
        RETURN true;
    ELSE
        RETURN false;
    END IF;
END;
$$ LANGUAGE plpgsql;

Definicja operatora INTEGER = CHAR, przeczytaj także w manualu.

CREATE OPERATOR = (
    LEFTARG = integer ,
    RIGHTARG = char,
    PROCEDURE = equal_integer_vs_char
);

W przypadku potrzeby posiadania funkcji substr(INT, INT, INT) musimy zdefiniować funkcję przyjmującą odpowiednie parametry.

CREATE OR REPLACE FUNCTION substr(integer, integer, integer) RETURNS text AS $$
DECLARE
       ...
BEGIN
         ...

        RETURN foo;
END;
$$ LANGUAGE plpgsql;