Kurs tworzenia wtyczek w WordPress

WordPress umożliwia zwiększenie funkcjonalności poprzez tworzenie pluginów / wtyczek. Aby robić to sprawnie musisz mieć dostęp do serwera i znać PHP. Przyda się również znajomość HTML i CSS. Do testowania wtyczek powinieneś mieć osobne środowisko testowe, aby użytkownicy odwiedzający Twoją witrynę nie musieli oglądać Twoich prób. Zapraszam do kursu tworzenia wtyczek w WordPress.

Opis

Wtyczka będzie liczyła znaki w tekście i wyświetlała tą informację pod tekstem lub w dowolnym miejscu tekstu.

Struktura plików

Struktura plików będzie wyglądała następująco:

/licznik-znakow
   licznik-znakow.php

Wtyczka jest na tyle prosta, że powyższa „struktura” (jeden plik) wystarczy. Możesz jednak skorzystać z innych – zalecanych – udostępnionych za pośrednictwem GitHub-a

Zanim zaczniesz, utwórz na serwerze katalog z nazwą wtyczki – u mnie w wp-content/plugins/licznik-znakow/. Tutaj będzie znajdował się cały kod.

Plik główny wtyczki – licznik-znakow

Technicznie cały kod mógłby się znaleźć tutaj. Jest to główny plik z którym będzie się komunikował WordPress. Zaczyna się od nagłówka w którym podajemy podstawowe informacje o wtyczce. Pisząc bardziej zaawansowane pluginy łatwiej Ci będzie rozbijać to na więcej plików.

<?php

/**
 * Plugin Name:       Licznik znaków
 * Plugin URI:        https://eskim.pl/wlasna-wtyczka-w-wordpress-na-przykladzie-licznika-znakow/
 * Description:       Liczy znaki w tekście.
 * Version:           1.0
 * Requires at least: 5.2
 * Requires PHP:      5.6
 * Author:            Maciej Włodarczak
 * Author URI:        https://eskim.pl
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       licznik-znakow
 * Domain Path:       /languages
 */
 
 ?>

Myślę, że powyższy kod jest zrozumiały w większości. Wyjaśnienia może wymagać jedynie text domain. Po tej nazwie WordPress rozpoznaje elementy tłumaczone. Powinna zawierać nazwę katalogu w którym znajduje się wtyczka.

W zasadzie już stworzyłeś własną wtyczkę. Kiedy wejdziesz do menu Wtyczki -> Zainstalowane wtyczki znajdziesz ją na liście. Włącz ją.

włączenie wtyczki

Hooki – akcje i filtry

Hooki służą do „podczepienia” kodu do WordPress-a i są używane przez niego samego. Nie jest to więc mechanizm dedykowany tylko developerom. Oznacza to, że możemy wczepić kod w różnych momentach uruchamiania CMS-a oraz zmienić sposoby jego działania oraz sposób działania innych wtyczek korzystających z hooków.

seohost

Akcje to jednorazowe zdarzenia wywoływane w konkretnych momentach uruchamiania WordPress-a. Pozwalają wpływać bezpośrednio na sposób jego działania. Akcją będzie zapisanie i usunięcie posta czy załadowanie szablonu. Możesz się podpiąć pod istniejącą akcję, albo utworzyć własną i następnie ją wywołać.

Do dodania zadania do istniejącej akcji (lub utworzenia nowej) służy add_action. W argumencie podajesz nazwę akcji (tutaj save_post – akcja wywoływana w momencie zapisywania wpisu), nazwę funkcji, priorytet (kiedy ma być wykonywana akcja – standardowy priorytet to 10, czyli 999 oznacza – pod koniec) oraz liczbę argumentów, które przekazujemy

function dodaj_cos_do_posta($postId) {
    // tutaj wywołujemy kod, który doda coś do posta
}
add_action('save_post', 'dodaj_cos_do_posta', 999, 1);

Jeżeli chcemy wywołać akcję używamy funkcji do_action(). Listę dostępnych hooków akcji znajdziesz się w dokumentacji: https://codex.wordpress.org/Plugin_API/Action_Reference

Filtry są wywoływane w trakcie działania strony i służą do modyfikowania danych (np. zwracanych przez inne funkcje). Po ich modyfikacji zwracają nowe dane. Filtrem będzie zatem zmiana treści lub stopki. Podobnie jak z akcjami – dodanie odbywa się za pomocą add_filter, a wywołanie – apply_filters (zwracana jest jednak wartość funkcji).

function dodaj_cos_do_posta($content) {
   return $content.' super';
}
add_filter('the_content', 'dodaj_cos_do_posta',999, 1);

Lista hooków WordPress znajduje się tutaj: https://developer.wordpress.org/reference/

Liczba znaków pod każdym tekstem

W zasadzie możesz już napisać funkcję, która pod każdym tekstem będzie wyświetlała ilość znaków z których tekst się składa. Wykorzystaj do tego filtr the_content. Z dokumentacji możesz się dowiedzieć, że filtr ten zawiera jeden parametr z tekstem posta. Użyj również funkcji wp_strip_all_tags, która zamieni kod na zwykły tekst.

function eskim_pl_count_post_chars($content) {

    $length = strlen(wp_strip_all_tags($content));
    $add_text = "<hr>Ilość znaków: $length<hr>";
    return $content.$add_text;
}
add_filter('the_content', 'eskim_pl_count_post_chars', 999);

Filtr pobrał tekst, zmienił go i zwrócił (nie zmienił go na stałe tylko wprowadził zmianę w trakcie generowania tekstu). W efekcie wywołania tego kodu, na końcu każdego artykułu dodała się informacja z ilością znaków.

ilość znaków

Widok tylko dla administratora

Tekst wyświetla się wszystkim użytkownikom serwisu co niekoniecznie jest wskazane. Jeżeli chcesz, by tylko administrator go widział to musisz użyć funkcji current_user_can, która sprawdza czy aktualny użytkownik ma stosowne uprawnienia (w tym przypadku uprawnienia do edycji).

if (current_user_can('edit_others_posts')) {
    add_filter('the_content', 'eskim_pl_count_post_chars', 999);
}

Niestety, ale po dodaniu kodu strona wyświetliła błąd o braku funkcji current_user_can. Jest to związane z tym, że plik z definicją nie został jeszcze załadowany. Możemy temu zaradzić na dwa sposoby – albo dodając kod do odpowiedniej akcji (wywoływanej po załadowaniu skryptu), albo dołączając brakujący plik. Spójrzmy na oba rozwiązania.

include_once (ABSPATH.'wp-includes/pluggable.php');
if (current_user_can('edit_others_posts')) {
    add_filter('the_content', 'eskim_pl_count_post_chars', 999);
}

W poniższym przykładzie użyłem anonimowej funkcji, aby nie tworzyć kolejnej oraz akcji init, która jest uruchamiana po załadowaniu wszystkich skryptów. Ten przykład jest moim zdaniem lepszy, gdyż nie odwołujemy się bezpośrednio do pliku, który w którejś wersji w przyszłości może zmienić swoje położenie.

add_action('init', function () {
    if (current_user_can('edit_others_posts')) {
        add_filter('the_content', 'eskim_pl_count_post_chars', 999);
	  } 
});

Skróty kodowe

Skróty kodowe (ang. shortcodes) są to wpisy w tekście otoczone znakami [ ], które umożliwiają wyświetlenie jakiegoś elementu. Przykładowo – wpisanie wyświetli w zadanym miejscu galerię o id 1. Skróty mogą mieć również format podobny do używanego w HTML-u (jeżeli zawierają więcej danych) – [shortcode] ... [/shortcode]

Stworzyliśmy funkcję, która zwróciła ilość znaków. Nie ma potrzeby wywoływać jej ponownie za każdym razem. Zamiast tego utwórz zmienną i zapisz tą ilość do zmiennej.

$eskim_pl_post_length = 0;
function eskim_pl_count_post_chars($content) {

    global $eskim_pl_post_length;
    $eskim_pl_post_length = strlen(wp_strip_all_tags($content));
    $add_text = "<hr>Ilość znaków: $eskim_pl_post_length<hr>";
    return $content.$add_text;
}

Dodanie shortcode jest proste. Służy do tego funkcja add_shortcode, która w najprostszej postaci przyjmuje 2 argumenty – nazwę oraz funkcję. Skorzystaj znowu z anonimowej funkcji, która zwróci długość znaków. Umieść kod w bloku w którym wywołałeś init, tuż za kodem dodającym filtr. Pominę przy tym sprawdzanie czy użytkownik ma prawo do edycji.

add_shortcode(
    'eskim_pl_ile_znakow',
    function () {
        global $eskim_pl_post_length;
        return $eskim_pl_post_length;
    }
);

Po wpisaniu w treści posta Ten tekst ma [eskim_pl_ile_znakow] znaków otrzymasz tekst:

Ten tekst ma [eskim_pl_ile_znakow] znaków.

Menu

Aby zrobić generalny przegląd wszystkich artykułów, musiałbyś wchodzić w każdy z nich i sprawdzać. Znacznie lepszym rozwiązaniem będzie utworzenie listy wszystkich artykułów z ilością znaków w jednym miejscu. Do tego celu utworzymy menu w panelu administratora i dodamy podmenu w którym umieścimy stosowną tabelkę. Po wejściu zostaną wykonane obliczenia.

Na tym etapie powinieneś już rozbić kod na moduły i przenieść odpowiednie funkcje do osobnych plików np. do katalogu includes. Plik z wtyczką powinien ładować stosowne moduły z różnych katalogów. Aby nie wprowadzać zamieszania będę jednak dalej rozbudowywać plik licznik-znakow.php.

Aby dodać pozycję w menu wywołujesz funkcję add_menu_page i dodać ją do akcji admin_menu. Po utworzeniu elementu możesz także dodać submenu poprzez wywołanie add_submenu_page.

Funkcja add menu_page przyjmuje jako argument:

  • tytuł strony,
  • nazwę w menu,
  • uprawnienia – czyli kto je będzie widział
  • unikalną końcówkę linka – identyfikator (tak jak to ma miejsce z każdą stroną)
  • nazwę funkcji, która wyświetli zawartość po kliknięciu
  • link do ikony – np. pliku .png 16×16 pikseli (jeżeli zostawisz to pole puste to domyślnie załaduje się ikona koła zębatego)
  • pozycję w menu – jeżeli chcesz by było na samej górze podaj 1

Funkcja add_submenu_page ma zbliżone parametry z dwoma wyjątkami.

  • na początku podajesz identyfikator menu do którego się podpinasz
  • nie można dodać ikony
  • nie wpisujemy pozycji

Dodajemy kod

add_action('admin_menu', function () {

    add_menu_page(
        'Licznik znaków', // tytuł strony
        'Licznik znaków', // tytuł w menu
        'manage_options', // widoczne tylko osobom, które mogą zmieniać ustawienia
        'menu-licznik-znakow', // identyfikator
        'eskim_pl_all_posts_chars_counter_info', // funkcja, którą zostanie wywołana po kliknięciu
        '',               // link do ikony
        20                // pozycja w menu
    );

    add_submenu_page(
        'menu-licznik-znakow',         // menu do którego się podpinasz
        'Licznik znaków - statystyki', // tytuł strony
        'Statystyki',                  // tytuł w menu
        'manage_options',              // widoczne tylko osobom, które mogą zmieniać ustawienia
        'submenu-licznik-znakow-statystyki', // identyfikator
        'eskim_pl_count_all_posts_chars_render', // funkcja, którą zostanie wywołana po kliknięciu
		);		
});

Funkcja eskim_pl_all_posts_chars_counter_info wyświetla tylko informacje o wtyczce:

function eskim_pl_all_posts_chars_counter_info() {
    ?>
    <div class="wrap">
    <h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
    <hr class="wp-header-end">
    <p>Wtyczka liczy znaki w artykułach</p>
    </div>
<?php
}

Celem funkcji eskim_pl_count_all_posts_chars_render będzie wyświetlenie tabelki ze statystykami. Wykorzystam do tego:

  • current_user_can (uprawnienia) – czy użytkownik ma uprawnienia do podglądu (na wszelki wypadek)
  • get_admin_page_title() – zwraca podany tytuł strony
  • esc_html() – wyświetla znaki html jak zwykły tekst (nie jest wymagana)
  • get_posts(opcje) – pobiera wpisy (obiekty klasy WP_Post). W parametrze możesz podać:
    • numberposts – ile wpisów pobrać (domyślnie 5), -1 oznacza wszystkie
    • category – identyfikator liczbowy kategorii (można podać wiele po przecinku w łańcuchu znaków) – domyślnie 0 (wszystkie)
    • include – tablica z identyfikatorami liczbowymi wpisów z uwzględnieniem przyklejonych (domyślnie pusta tablica co oznacza wszystkie)
    • exclude – tablic z identyfikatorami liczbowymi wpisów, których nie ma być (domyślnie pusta)
    • suppress_filters – czy nie włączać filtrów (domyślnie true) – czasami filtr dodany błędnie przez wtyczkę potrafi namieszać i skrypt może nie działać (doraźnie pomoże wyłączenie tego parametru – ustawienie false)
  • WP_post – obiekt, który otrzymasz po wywołaniu get_post(), zawiera pola:
    • ID – identyfikator wpisu
    • post_author – identyfikator autora
    • post_date – lokalna data publikacji
    • post_modified – lokalna data modyfikacji
    • post_date_gmt – data publikacji z uwzględnieniem GMT
    • post_modified_gmt – data modyfikacji z uwzględnieniem GMT
    • post_content – wpis
    • post_title – tytuł wpisu
    • post_excerpt – zajawka
    • post_status – status wpisu
    • comment_status – status komentarza (czy można komentować? – „open”)
    • ping_status – status pingowania (czy można pingować? – „open”)
    • post_password – hasło do wpisu
    • post_name – identyfikator tekstowy wpisu (końcówka adresu)
    • to_ping – kolejka do pingowania (adresy url)
    • pinged – adresy url już pingowanych stron
    • post_content_filtered – filtr (baza danych)
    • post_parent – rodzic wpisu
    • guid – unikalny identyfikator wpisu bazujący na GUID
    • menu_order – pole używane do sortowania wpisów (numer)
    • post_type – typ (wpis – post lub strona – page)
    • post_mime_type – typ mime wpisu
    • comment_count – ilość komentarzy
    • filter – stopień sanityzacji wpisu (np. „raw”)

Z powyższego obiektu interesuje mnie głównie treść oraz zajawka. Utworzona tabela korzysta z klas WordPress-a.

function eskim_pl_count_all_posts_chars_render() {
		
    if (!current_user_can( 'manage_options' )) return;
    ?>
    <div class="wrap">
    <h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
    <hr class="wp-header-end">
    <?php
    $params = array(
        'numberposts' => -1
    );
    $posts = get_posts($params);

    $all_post_count = 0;
    $all_excerp_count = 0;
    $all_articles_count = 0;
    echo '<h2>Ilości znaków</h2>';
    $posts_list_table = '
        <table class="wp-list-table widefat fixed striped">
        <thead><tr>
            <th>Tytuł</th>
            <th>Artykuł</th>
            <th>Zajawka</th>
        </tr></thead><tbody>';
    foreach ($posts as $post) {
					
        $content_post_count = strlen(wp_strip_all_tags($post->post_content));
        if ($content_post_count < 1) continue;
        $all_post_count += $content_post_count;
        $excerp_count = strlen(wp_strip_all_tags($post->post_excerpt));
        $all_excerp_count += $excerp_count;
        $posts_list_table .= "
            <tr>
                <td>$post->post_title</td>
                <td>$content_post_count</td>
                <td>$excerp_count</td>
            </tr>
        ";
        $all_articles_count++;
    }
    $posts_list_table .= "
        </tbody><tfoot><tr>
            <td>$all_articles_count</td>
            <td>$all_post_count</td>
            <td>$all_excerp_count</td>
        </tr></tbody></table>
    ";
    echo $posts_list_table;
    ?>
     </div>
<?php
}

Tłumaczenie

Jeżeli chcielibyśmy, aby nasza wtyczka obsługiwała także inne języki, musimy nieco zmodyfikować kod, dodać pliki z tłumaczeniami i załadować plugin odpowiedzialny za internacjonalizację (i18n). Przyjęło się, że tekst w pluginach pisze się standardowo w języku angielskim, a następnie tłumaczy na inne.

Nagłówek

Zanim zaczniemy poprawiać tekst, zwróćmy uwagę na nagłówek.

 * Text Domain:       licznik-znakow
 * Domain Path:       /languages

Domain Path to miejsce gdzie będziemy trzymać nasze tłumaczenia. Standardowo znajdują się one tam gdzie wtyczka, ale w osobnym katalogu languages. Tłumaczenia można również umieścić po stronie systemu CMS w languages/plugins/

Text Domain to parametr opcjonalny, który określa od jakiej nazwy będą zaczynały się pliki z tłumaczeniami (np. licznik-znakow-pl_PL.po). Nazwa jest identyczna jak wtyczki i nie można ustawić innej.

Typy plików

Tłumaczenia zawierają jeden główny plik POT (Portable Object Template) w którym znajduje się to co ma zostać tłumaczone oraz pliki PO (Portable Object) i MO (Machine Object).

  • .pot – wszystkie elementy we wtyczce, które mają być tłumaczone
  • .po – właściwe tłumaczenia na dany język np. licznik-znakow-pl_PL.po
  • .mo – „skompilowana” wersja tłumaczenia (powstaje z pliku .po) zrozumiała dla WordPress-a np. licznik-znakow-pl_PL.mo

Dodanie obsługi tłumaczeń

Do obsługi tłumaczeń służy dedykowan funkcja load_plugin_textdomain, w której podajemy nazwę oraz ścieżkę do plików z internacjonalizacją. Następnie dodajemy ją do akcji plugins_loaded. W ten sposób informuje WordPress, aby załadował tłumaczenie (w pierwszej kolejności będzie szukał pliku z rozszerzeniem mo).

add_action('plugins_loaded', function () {
	load_plugin_textdomain( 'licznik-znakow', false, basename( dirname( __FILE__ ) ) . '/languages');
});

W powyższym przykładzie skorzystałem z anonimowej funkcji. Pierwszy parametr w load_plugin_textdomain zawiera nazwę tłumaczenia (taką samą jak nazwa wtyczki). Drugi nie jest używany, a w trzecim podajemy ścieżkę do pluginu w którym musi znaleźć się katalog languages (tam znajdą się tłumaczenia).

Funkcje używane do tłumaczeń

Aby tłumaczenia działały musimy wskazać konkretnie miejsca w kodzie, które będą tłumaczone. Do dyspozycji mamy cały wachlarz funkcji z czego najprostsza to __('tekst', 'nazwa'). Funkcja ta zwraca przetłumaczony tekst (chyba, że nie ma tłumaczenia to zwróci to co napisaliśmy w „tekst”).

echo __( 'Mój tekst', 'licznik-znakow' );

Powyższe wyświetli napis „Mój tekst”. Kiedy dodamy tłumaczenie zmieni się on na ten przetłumaczony.

Wyświetlenie tłumaczenie

_e ('Mój tekst', 'licznik-znakow')

Działa tak samo jak:

echo __( 'Mój tekst', 'licznik-znakow' );

Obsługa liczby mnogiej

_n ('1 tekst', '2 teksty', $ile, 'licznik-znakow') : string

Pozwala na odróżnienie liczby pojedynczej i mnogiej. W trzecim parametrze przekazujemy liczbę na podstawie której wybierane jest tłumaczenie. Wpisując 1, będzie wybrane pierwsze. Podając 2 i więcej – drugie. Poniższe wyświetli tekst: „Mam 2 lata”, a później „Mam 1 rok”.

$wiek = 2;
echo _n( "Mam $wiek rok", "Mam $wiek lata", $wiek, 'licznik-znakow' );
$wiek = 1;
echo _n( "Mam $wiek rok", "Mam $wiek lata", $wiek, 'licznik-znakow' );

Koncepcja ta sprawdza się w przypadku języka angielskiego, ale niekoniecznie będzie działać dla języka polskiego. Podając w zmiennej $wiek liczbę 5 uzyskamy „Mam 5 lata”. Nie jest to poprawnie gramatycznie. W takich przypadkach można zastosować instrukcję warunkową if i ustawiać tekst w zależności od zmiennej, albo (lepsze) postarać się tak konstruować zdania, by były poprawne w języku polskim i angielskim.

Obsługa liczby mnogiej później

_n_noop('1 tekst', '2 teksty') : string

Funkcja ta działa podobnie jak _n, z tą różnicą, że nie przekazujemy do niej żadnej wartości. Tą możemy przekazać później korzystając z instrukcji translate_nooped_plural ($npop, 'liczba', 'nazwa').

$tekst = _n_noop( "mało", "dużo" );
echo 'jeden to '.translate_nooped_plural ($tekst, 1, 'licznik-znakow');
echo ', a dwa to '.translate_nooped_plural ($tekst, 2, 'licznik-znakow');

Powyższe wyświetli „jeden to mało, a dwa to dużo”.

Kontekst

_x('tekst', 'kontekst', 'nazwa') : string
_ex('tekst', 'kontekst', 'nazwa')

Czasami może zajść potrzeba dodania dodatkowej informacji, która podpowie tłumaczącemu tekst w jakim kontekście jest on używany. Przykładowo – zdanie „post”, może oznaczać „post” lub „wpis”. To pierwsze (post) będzie użyte gdzieś w tekście np. jako tytuł jakiegoś elementu. Drugie zaś (wpis) może być elementem ustawień. Osoba, która tłumaczy tekst widzi tylko powielone dwa wyrazy „post” i nie wie czego dotyczą. W takich przypadkach przychodzi do pomocy funkcja _x('tekst', 'kontekst', 'nazwa') oraz _ex('tekst', 'kontekst', 'nazwa'). Pierwsza zwraca wartość, druga wyświetla (podobnie jak to ma miejsce z __() i _e()

_ex( 'post', 'settings', 'licznik-znakow.php' );
echo '<h2>'._x( 'post', 'article title', 'licznik-znakow' ).'</h2>';

Kontekst i liczba mnoga

_nx ('pojedyncza', 'mnoga', $ile, 'kontekst', 'nazwa') : string
_nx_noop ('pojedyncza', 'mnoga', 'kontekst', 'nazwa') : string

Istnieje także funkcja w której podajemy liczbę pojedynczą i mnogą oraz kontekst – połączenie _n i _x, czyli _nx. Podobnie jest z odmianą funkcji _n_noop, czyli _nx_noop

$ile = 2;
echo _nx ('1 mój tekst', '2 moje teksty', $ile, 'w treści', 'licznik-znakow');
$tekst = _nx_noop ('1 mój tekst', '2 moje teksty', 'w treści', 'licznik-znakow');
echo translate_nooped_plural ($tekst, 1, 'licznik-znakow');

Komentarz

Możemy przekazać osobie tłumaczącej większą ilość informacji. Do tego celu służy komentarz, który umieszczamy przed funkcją i który musi zaczynać się od słowa translate:

// translate: Wyraz w liczbie pojedynczej i mnogiej
echo _n( "moose", "moose", $ile, 'licznik-znakow' );

Bez tagów HTML

esc_html__('tekst', 'licznik-znakow'): string
esc_html_e('tekst', 'licznik-znakow')

esc_html_x('tekst', 'kontekst', 'nazwa') : string

Jeżeli z jakiś powodów (np. komentarz użytkownika) nie chcemy wyświetlać na stronie tagów HTML to możemy użyć odmiany powyższych funkcji z esc_html. Wywołanie spowoduje, że tagi jak np. <h1> będą wyświetlały się jako <h1> – nie będą interpretowane przez przeglądarkę jako kod, ale tekst.

esc_html_e ('<h1>Super</h1>', 'licznik-znakow');

Powyższe wyświetli na stronie: <h1>Super</h1>

Do użycia w atrybutach

esc_attr__('tekst', 'licznik-znakow'): string
esc_attr_e('tekst', 'licznik-znakow')
esc_attr_x('tekst', 'kontekst', 'nazwa') : string

Podobnie jak z esc_html, ale zwrócony tekst możemy używać bezpiecznie w atrybutach.

Wprowadzenie zmian w kodzie

Poniżej tylko fragmenty tego jak wygląda tłumaczenie. Nie jest to skomplikowane.

$add_text = "<hr>".__('Chars count', 'licznik-znakow').": $eskim_pl_post_length<hr>"; // Ilość znaków
(...)
add_menu_page(
    _x('Chars counter','page title', 'licznik-znakow'),  // Licznik znaków - tytuł strony
    _x('Chars counter', 'menu title', 'licznik-znakow') // Licznik znaków - tytuł w menu
(...)
__('<th>Tytuł</th>', 'licznik-znakow')

Tłumaczenie

Do tworzenia pliku z tłumaczeniem, można użyć edytora gettext. Edycję plików z tłumaczeniami da się też przeprowadzać w wygodnym POEditor. Ja osobiście wybrałem wtyczkę Loco Translate do WordPress-a, która utworzy właściwy plik z tłumaczeniem i pozwoli dodawać języki. Nie trzeba plików kopiować na serwer, a sama wtyczka pozwala spolszczyć wszystko co się da w naszej witrynie.

Po instalacji wyświetli się menu Loco Translate z którego wybieramy Plugins, a następnie na liście odszukujemy Licznik znaków.

Plik źródłowy

Najpierw musimy pobrać dane z pliku i utworzyć plik źródłowy z którego będziemy tłumaczyć – POT. Robimy to klikając Create template. Jeżeli wszystko jest dobrze w naszym tłumaczeniu, otrzymamy podgląd tego co ma być przetłumaczone – wliczając kontekst i ewentualne komentarze. Klikamy Save, aby utworzyć plik. Jeżeli zmienimy coś w kodzie to możemy łatwo zaktualizować plik w tym miejscu – przycisk Sync

loco
Wszystkie pobrane teksty ze strony z programu Loco Editor

Pliki z językiem

Na górze strony jest ścieżka prowadzącą do pliku POT- Plugins / Licznik znaków / licznik-znakow.pot. Naciskamy na Licznik znaków, aby przejść wyżej. W tym momencie możemy edytować ponownie wspomniany plik POT klikając Edit template (będziemy to robić jak zaktualizujemy skrypt i dodamy / poprawimy jakieś teksty), albo dodać nowe język (New language). Dodajemy oczywiście język i z menu wybieramy język polski. Wybieramy ścieżkę na dole prowadzącą do plugins/licznik-znakow/languages/ (nasz katalog z językami) i wybieramy Start translating.

Jeżeli wszystko wykonaliśmy dobrze to w katalogu languages, tam gdzie znajduje się wtyczka, utworzą się pliki z tłumaczeniem. Tłumaczymy, a po zakończeniu zapisujemy (Save).

loco
Tłumaczenie na język polski

Jeżeli potrzebujemy zaktualizować dane z pliku POT – naciskamy Sync. Po uzupełnieniu, zapisaniu i przeładowaniu strony, język wtyczki dostosuje się do domyślnych ustawień WordPress-a.

Ustawienia

Większość wtyczek posiada ustawienia do konfiguracji działania wtyczki. Można ją dodać w dowolnym miejscu w menu Ustawienia, albo oczywiście tam gdzie mamy menu wtyczki. Skupmy się na pierwszym i stwórzmy osobny element, który nazwiemy Licznik znaków. Dodamy do niego następujące ustawienia:

  • monetyzacja – pole z wartością 1000 znaków w złotówkach
  • pokaż pod artykułem – pole wyboru, które pokaże liczbę znaków nad artykułem osobie zalogowanej

API

WordPress udostępnia API, które pozwala na automatyczne utworzenie strony z ustawieniami i zapisywanie oraz odczytywanie wprowadzonych zmian.

W ramach ustawień tworzymy sekcje w których znajdują się pola. Później całość podpinamy pod utworzony lub istniejący element w menu (np. Ustawienia). Działania, które musimy wykonać będą wyglądały następująco:

  1. Tworzymy nowe podmenu w menu Ustawienia.
  2. Tworzymy nowy zestaw ustawień poleceniem register_setting.
  3. Dodajemy sekcje poleceniem add_settings_section – jest to wizualny element na stronie.
  4. Dodajemy pola poleceniem add_settings_field i podpinamy je pod ustawienia oraz sekcję (nieobowiązkowe).
  5. Powyższe funkcje związane z ustawieniami podpinamy pod akcję admin_init
  6. Zapisujemy dane z użyciem add_option, a pobieramy poprzez get_option.

Utworzenie menu

Podmenu w ustawieniach możemy utworzyć za pomocą add_options_page, która tak naprawdę wywołuje znaną już funkcję do tworzenia podmenu add_submenu_page, ale w nazwie strony menu wpina options-general.php. Oznacza to, że nie wpisujemy po prostu pierwszego parametru, bo jest on już wypełniony. Oczywiście nic nie stoi na przeszkodzie, by zamiast add_options_page, użyć add_submenu_page, ale to rozwiązanie jest o tyle lepsze, że w przypadku aktualizacji WordPress-a nie musimy martwić się, że nagle identyfikator menu ustawień będzie nazywał się inaczej.

add_options_page(
		__('Settings','licznik-znakow'),
		__('Chars counter','licznik-znakow'),
		'manage_options',
    'eskim_pl_chars_counter_settings_menu',
		'eskim_pl_chars_counter_settings_page_render',
);

Oczywiście podpinamy to pod już istniejącą funkcję inicjalizującą menu.

Ostatnim parametrem, jest funkcja która zwróci widok. Musimy ją utworzyć. Później wpiszemy odpowiedni kod, który wyświetli stosowne dane.

function eskim_pl_chars_counter_settings_page_render() {
   
}

Rejestracja grupy ustawień

Do utworzenia nowej grupy ustawień służy.

register_setting ('menu', 'nazwa', 'parametry')

  • menu– menu pod które podpiąć ustawienia. Można to zrobić również pod standardowe elementy wpisując – 'general’ (Ogólne), 'discussion’ (Dyskusja), 'media’ (Media), 'reading’ (Czytanie), 'writing’ (Pisanie) i 'options’
  • nazwa – unikatowa nazwa tej grupy ustawień (do której później będziemy się odwoływać) – eskim_pl_chars_counter_settings
  • parametry – tablica zawierająca dodatkowe, opcjonalne ustawienia:
    • type – jaki typ danych ma być użyty do przechowywania ustawień: 'string’ (domyślnie), 'boolean’, 'integer’, 'number’, 'array’, 'object’
    • description – opis grupy ustawień
    • sanitize_callback – funkcja, która „oczyszcza” ustawienia np. jeżeli użytkownik wpisze jakiś kod, który mógłby zaszkodzić, to możemy go traktować jako tekst
    • show_in_rest (bool|array)- czy pokazywać w API Rest – w tym miejscu można przekazać schemat (scheme)
    • default – wartość domyślna dla opcji

Utwórzmy nową grupę ustawień dla naszego skryptu i podepnijmy ją pod menu

register_setting (
   'eskim_pl_chars_counter_settings_menu',
   'eskim_pl_chars_counter_settings'
);

Nasze ustawienia zawierają pola, które mają różne formaty, więc teoretycznie moglibyśmy utworzyć dwie metody do rejestracji każdego z nich. Przechowywalibyśmy wtedy w bazie liczbę oraz boolean. Uprościmy to jednak i będziemy stosować po prostu string dla całości. Przy dużej ilości pól ustawień zaleca się korzystanie z tablic z której od razu ładujemy i zapisujemy wszystkie dane w ramach jednego zapisu.

Podpięcie sekcji

Podpięcie sekcji, która zgrupuje nasze opcje, robi się poleceniem

add_settings_section('nazwa', 'tytuł', 'funkcja', 'menu')

  • nazwa – unikatowa nazwa sekcji
  • tytuł – formatowana nazwa sekcji, używana jako nagłówek
  • funkcja – wypisuje dodatkowe informacje między nagłówkiem, a opcjami
  • menu – menu pod, które podpinamy sekcję – to, które utworzyliśmy przed chwilą, czyli eskim_pl_chars_counter_settings_menu

Utwórzmy nową sekcję i nazwijmy ją Ustawienia wtyczki:

add_settings_section (
   'eskim_pl_chars_counter_settings_section',
   '<h2>'.__('Section settings').'</h2>',
   'eskim_pl_chars_counter_settings_section_render',
   'eskim_pl_chars_counter_settings_menu'
);

Musimy jeszcze dodać funkcję, która przekaże jakiś widok. Podajmy tutaj informacje o tym jaki jest cel sekcji.

function eskim_pl_chars_counter_settings_section_render() {
   _e('<h2>Różne ustawienia</h2>');
}

Chcemy, aby sekcja pojawiła się w momencie kliknięcia w menu. Aby to zrobić musimy przy renderowaniu widoku poinformować o tym WordPress. Służy do tego funkcja do_settings_section. Ma ona tylko jeden parametr – unikatową nazwę menu, którą przekazaliśmy jak tworzyliśmy sekcję.

Do utworzonej wcześniej funkcji podajemy:

function eskim_pl_chars_counter_settings_page_render() {
   do_settings_sections('eskim_pl_chars_counter_settings_menu');
}

Opcje

Dane będą przechowywane i odczytywane z bazy. Służą do tego odpowiednio polecenia

  • add_option – tworzy odpowiednie miejsce w bazie
    add_option('nazwa', 'wartość', '', 'autoload')
  • get_option – pobiera dane z bazy
    get_option('nazwa', 'wartość domyślne')
  • update_option – aktualizuje dane w bazie
    update_option('nazwa', 'wartość', 'autoload')

Dla powyższych funkcji, poszczególne pola oznaczają:

  • nazwa – unikatowa nazwa opcji
  • wartość – wartość jaką przekazujemy
  • wartość domyślna – wartość jaka ma być zwrócona, jeżeli opcji nie ma w bazie
  • autoload – czy opcja ma być automatycznie zapisywana i odczytywana

Zapis w bazie

Potrzebujemy dwóch pól:

  • monetyzacja – pole tekstowe w którym podajemy liczbę
  • pokaż pod artykułem – haczyk z zaznaczeniem

Dodanie nowego pola do bazy danych odbywa się z wykorzystaniem funkcji add_option, która utworzy odpowiedni wpis w bazie i nada mu wartość początkowa – o ile opcja już nie została dodana wcześniej.

add_option('nazwa', 'wartość', '', 'autoload')

  • nazwa – unikatowa nazwa opcji
  • wartość – opcjonalna wartość początkowa
  • autoload (string|bool) – czy ładować opcje przy starcie WordPressa (domyślnie – 'yes’)

Dodajmy je na początku akcji admin_init:

	add_option('eskim_pl_chars_counter_monetize_option', 100);
	add_option('eskim_pl_chars_counter_article_option', false);

Odczyt pól

Teraz podepniemy pola pod sekcję za pomocą add_settings_field

add_settings_field('nazwa', 'tytuł', 'funkcja', 'menu', 'sekcja', 'parametry')

  • nazwa – unikatowa nazwa pola
  • tytuł – formatowana nazwa pola, używana jako etykieta
  • funkcja – funkcja, która wypełnia pole
  • menu – menu pod, które podpinamy sekcję – to, które utworzyliśmy, czyli eskim_pl_chars_counter_settings_menu
  • sekcja – opcjonalna nazwa sekcji pod które podpinamy pole, czyli eskim_pl_chars_counter_settings_section
  • parametry (array) – opcjonalna tablica z dodatkowymi opcjami
    • etykieta – nazwa pola dla danych umieszczonych tagiem <label>
    • class – klasa dodawana to taga <tr>

Potrzebujemy utworzyć dwa pola i podpiąć pod nie dwie funkcje:

	add_settings_field (
		'eskim_pl_chars_counter_monetize_field',
		__('Value for 1000 chars'),
		'eskim_pl_chars_counter_monetize_fill',
		'eskim_pl_chars_counter_settings_menu ',
		'eskim_pl_chars_counter_settings_section'
	);

	add_settings_field (
		'eskim_pl_chars_counter_article_field',
		__('Value for 1000 chars'),
		'eskim_pl_chars_counter_article_fill',
		'eskim_pl_chars_counter_settings_menu ',
		'eskim_pl_chars_counter_settings_section'
	);

Musimy utworzyć dwie funkcje, którymi wypełnimy pola

function eskim_pl_chars_counter_monetize_fill() {

}

function eskim_pl_chars_counter_article_fill() {

}

Do odczytywania danych dla pola z bazy służy funkcja get_option

get_option('nazwa', 'wartość domyślne')

Podepnijmy ją pod utworzone funkcje, podając nazwę pola jaką utworzyliśmy wcześniej przez add_option oraz dodajmy stosowny kod HTML. May pole tekstowe przechowujące liczby oraz pole z opcjami.

function eskim_pl_chars_counter_monetize_fill() {
   ?>
   <input type="number" value="
   <?php echo get_option('eskim_pl_chars_counter_monetize_option'); ?>
   />
   <?php
}

function eskim_pl_chars_counter_article_fill() {
   ?>
   <input type="checkbox" value="
   <?php echo get_option('eskim_pl_chars_counter_article_option'); ?>
   />
   <?php
}

Cały kod

Cały kod będzie wyglądać mniej więcej tak:

<?php

/**
 * Plugin Name:       Licznik znaków
 * Plugin URI:        https://eskim.pl/wlasna-wtyczka-w-wordpress-na-przykladzie-licznika-znakow/
 * Description:       Liczy znaki w tekście.
 * Version:           1.0
 * Requires at least: 5.2
 * Requires PHP:      5.6
 * Author:            Maciej Włodarczak
 * Author URI:        https://eskim.pl
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       licznik-znakow
 * Domain Path:       /languages
 */

$eskim_pl_post_length = 0;
function eskim_pl_count_post_chars($content) {

    global $eskim_pl_post_length;
    $eskim_pl_post_length = strlen(wp_strip_all_tags($content));
    $add_text = "<hr>".__('Chars count','licznik-znakow').": $eskim_pl_post_length<hr>";
    return $content.$add_text;
}

add_action('plugins_loaded', function () {
	load_plugin_textdomain( 'licznik-znakow', false, basename( dirname( __FILE__ ) ) . '/languages');
});

add_action('init', function () {
   	if (current_user_can('edit_others_posts')) {
		
		add_filter('the_content', 'eskim_pl_count_post_chars');
	} 
	add_shortcode(
	    'eskim_pl_ile_znakow',
       function () {
			    global $eskim_pl_post_length;
			    return $eskim_pl_post_length;
		   }
    );
});

add_action( 'admin_init', function() {

  add_option('eskim_pl_chars_counter_monetize_option', 100);
  add_option('eskim_pl_chars_counter_article_option', false);

	register_setting (
	   'eskim_pl_chars_counter_settings_menu',
	   'eskim_pl_chars_counter_settings'
	);

	add_settings_section (
	   'eskim_pl_chars_counter_settings_section',
	   '<h2>'.__('Section settings').'</h2>',
	   'eskim_pl_chars_counter_settings_section_render',
	   'eskim_pl_chars_counter_settings_menu'
	);

	add_settings_field (
		'eskim_pl_chars_counter_monetize_field',
		__('Value for 1000 chars'),
		'eskim_pl_chars_counter_monetize_fill',
		'eskim_pl_chars_counter_settings_menu ',
		'eskim_pl_chars_counter_settings_section'
	);

	add_settings_field (
		'eskim_pl_chars_counter_article_field',
		__('Value for 1000 chars'),
		'eskim_pl_chars_counter_article_fill',
		'eskim_pl_chars_counter_settings_menu ',
		'eskim_pl_chars_counter_settings_section'
	);
});

add_action('admin_menu', function () {

    add_menu_page(
        __('Chars counter','licznik-znakow'), // tytuł strony
        __('Chars counter','licznik-znakow'), // tytuł w menu
        'manage_options',  // widoczne tylko osobom, które mogą zmieniać ustawienia
        'menu-licznik-znakow', // identyfikator
        'eskim_pl_all_posts_chars_counter_info', // funkcja, którą zostanie wywołana po kliknięciu
        '',               // link do ikony
        20                // pozycja w menu
    );

    add_submenu_page(
        'menu-licznik-znakow',          // menu do którego się podpinasz
        __('Chars counter','licznik-znakow').' - '.__('statistics','licznik-znakow'), // tytuł strony
        __('Counter table','licznik-znakow'),  // tytuł w menu
        'manage_options',               // widoczne tylko osobom, które mogą zmieniać ustawienia
        'submenu-licznik-znakow-statystyki', // identyfikator
        'eskim_pl_count_all_posts_chars_render', // funkcja, którą zostanie wywołana po kliknięciu
		);

    add_options_page(
    		__('Settings','licznik-znakow'),
    		__('Chars counter','licznik-znakow'),
    		'manage_options',
    		'eskim_pl_chars_counter_settings_menu',
        'eskim_pl_chars_counter_settings_page_render'
    );	
});

function eskim_pl_chars_counter_settings_page_render() {
   do_settings_sections('eskim_pl_chars_counter_settings_menu');
}

function eskim_pl_chars_counter_settings_section_render() {
   _e('<h2>Różne ustawienia</h2>');
}

function eskim_pl_chars_counter_monetize_fill() {
   ?>
   <input type="number" value="
   <?php echo get_option('eskim_pl_chars_counter_monetize_option'); ?>
   />
   <?php
}

function eskim_pl_chars_counter_article_fill() {
   ?>
   <input type="checkbox" value="
   <?php echo get_option('eskim_pl_chars_counter_article_option'); ?>
   />
   <?php
}

function eskim_pl_all_posts_chars_counter_info() {
    ?>
    <div class="wrap">
    <h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
    <hr class="wp-header-end">
    <p>Wtyczka liczy znaki w artykułach</p>
    </div>
    <?php
    }

function eskim_pl_count_all_posts_chars_render() {
		
    if (!current_user_can( 'manage_options' )) return;
    ?>
    <div class="wrap">
    <h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
    <hr class="wp-header-end">
    <?php
    $params = array(
        'numberposts' => -1
    );
    $posts = get_posts($params);

    $all_post_count = 0;
    $all_excerp_count = 0;
    $all_articles_count = 0;
    echo '<h2>Ilości znaków</h2>';
    $posts_list_table = '
        <table class="wp-list-table widefat fixed striped">
        <thead><tr>
            <th>Tytuł</th>
            <th>Artykuł</th>
            <th>Zajawka</th>
        </tr></thead><tbody>';
    foreach ($posts as $post) {
					
        $content_post_count = strlen(wp_strip_all_tags($post->post_content));
        if ($content_post_count < 1) continue;
        $all_post_count += $content_post_count;
        $excerp_count = strlen(wp_strip_all_tags($post->post_excerpt));
        $all_excerp_count += $excerp_count;
        $posts_list_table .= "
            <tr>
                <td>$post->post_title</td>
                <td>$content_post_count</td>
                <td>$excerp_count</td>
            </tr>
        ";
        $all_articles_count++;
    }
    $posts_list_table .= "
        </tbody><tfoot><tr>
            <td>$all_articles_count</td>
            <td>$all_post_count</td>
            <td>$all_excerp_count</td>
        </tr></tbody></table>
    ";
    echo $posts_list_table;
    ?>
     </div>
<?php
}

?>

Co dalej?

To było tylko wprowadzenie do tworzenia pluginów. WordPress pozwala na znacznie więcej interakcji i udostępnia wiele prostych funkcji, które pozwalają m.in. na:

  • dodanie opcji
  • zapisywanie danych w bazie
  • obsługę zdarzeń włączania, wyłączania i odinstalowania pluginu
  • obsługę taksonomii
  • lokalizację

Oprócz tego możesz modyfikować działanie innych wtyczek np. domyślnego edytora WordPress-a – Guttenberg

Link do książki udostępnionej przez WordPress-a: https://developer.wordpress.org/plugins/
Link do dokumentacji edytora Guttenberg: https://developer.wordpress.org/block-editor/

Wtyczka dostępna na GitHub – licznik-znakow-wordpress-plugin

Podsumowanie

  1. Utwórz katalog z plikiem wp-content/plugins/licznik-znakow/licznik-znakow.php. Katalog musi mieć taką samą nazwę jak główna nazwa pliku z pluginem.
  2. Dodaj podstawowe informacje o wtyczce – nazwę, autora itd.
  3. Hooki służą do dodania kodu do WordPressa. Dzielą się na
    • akcje – umożliwiają dodanie kodu w różnych momentach uruchamiania strony. Dodanie akcji lub kodu do akcji odbywa się poprzez użycie funkcji add_action. Wywołanie – przy użyciu do_action
    • filtry – modyfikują istniejący kod i zwracają (najczęściej) jego zmienioną wersję. Dodanie filtra lub kodu do filtra odbywa się poprzez użycie funkcji add_filter. Do wywołanie i pobrania wartości służy apply_filters
  4. Zawartość posta można przechwycić wywołując filtr the_content.
  5. Do usunięcia HTML-a można użyć funkcji wp_strip_all_tags, dostępnej dopiero po załadowaniu skryptów.
  6. Akcja init wywoływana jest po załadowaniu strony, ale przed jej wyświetleniem.
  7. Skróty kodowe (shortcodes) umożliwiają wyświetlenie wyniku kodu w dowolnym miejscu na stronie.
  8. Dodanie obsługi skrótów kodowych odbywa się z użyciem funkcji add_shortcode.
  9. Dodanie menu wymaga przekazania do akcji admin_menu funkcji add_menu_page
  10. Dodanie podmenu wymaga przekazania do do akcji admin_menu funkcji add_men