Dezvoltarea unui plugin WordPress pentru protejarea conținutului [632288]

Universitatea “Politehnica” din București
Facultatea de Electronică, Telecomunicații și Tehnologia Informației

Dezvoltarea unui plugin WordPress pentru protejarea conținutului
cu reguli avansate de acces pe bază de rol și adresă IP

Proiect de diplom ă
prezentat ca cerință parțială pentru obținerea titlului de
Inginer în domeniul Electronică și Telecomunicații
programul de studii de licență Rețele și Software de Telecomunicații

Conducător științific Absolvent: [anonimizat]. Eduard -Cristian POPOVICI Eusebiu OPRINOIU

2018

[Anexa 1]

[Anexa 5]

Cuprins

Cuprins ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. ……………. 4
Lista figurilor ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. …. 6
Lista tabelelor ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. … 7
Lista acronimelor ………………………….. ………………………….. ………………………….. ………………………….. ………………………. 8
Introducere ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. …….. 9
Motivația alegerii temei ………………………….. ………………………….. ………………………….. ………………………….. …… 9
Gradul de noutate al temei ………………………….. ………………………….. ………………………….. ………………………….. 9
Obiectivele generale ale proiectului ………………………….. ………………………….. ………………………….. ………. 10
Metodologia folosită ………………………….. ………………………….. ………………………….. ………………………….. ……… 10
Contribuția student: [anonimizat] 11
Capitolul 1: Plugin -urile WordPress – Privire de ansamblu ………………………….. ………………………….. ….. 12
1.1. Ce este un plugin ………………………….. ………………………….. ………………………….. ………………………….. …….. 12
1.2. Avantajele plugin -urilor ………………………….. ………………………….. ………………………….. …………………….. 12
1.3. Tipuri de plugin -uri ………………………….. ………………………….. ………………………….. ………………………….. .. 14
1.4. Cum funcționează inter acțiunea dintre WordPress și plugin -uri ………………………….. ……… 14
1.5. Lista de API -uri folosite la dezvoltarea plugin -ului ………………………….. ………………………….. …. 17
Capitolul 2: Instalarea, configurarea și utilizarea plugin -ului ………………………….. ………………………….. .. 19
2.1. Instalarea plugin -ului ………………………….. ………………………….. ………………………….. …………………………. 19
2.2. Panoul de control pr incipal ………………………….. ………………………….. ………………………….. ………………. 22
2.3. Setările de acces în funcție de rol și adresă IP ………………………….. ………………………….. ………….. 24
2.4. Setările pentru motoarele de căutare ………………………….. ………………………….. …………………………. 25
2.5. Setările Google Analytics ………………………….. ………………………….. ………………………….. …………………… 26
2.6. Setările de culoar e ………………………….. ………………………….. ………………………….. ………………………….. ….. 27
2.7. Setările de tipografie ………………………….. ………………………….. ………………………….. ………………………….. 28
2.8. Setările de conținut pentru modurile predefinite ………………………….. ………………………….. ……. 29
2.9. Setările de conținut pentru modul personalizat ………………………….. ………………………….. ………. 30
2.10. Modul de funcționare al plugin -ului ………………………….. ………………………….. …………………………. 30
Capitolul 3: Structura internă a plugin -ului ………………………….. ………………………….. ………………………….. ….. 32
Capitolul 4: Secu rizarea plugin -ului ………………………….. ………………………….. ………………………….. ……………….. 33
4.1. Blocarea accesului direct la fișiere ………………………….. ………………………….. ………………………….. ….. 33
4.2. Verificarea drepturilor de acces ………………………….. ………………………….. ………………………….. ………. 34
4.3. Utilizarea cheilor Nonce ………………………….. ………………………….. ………………………….. ……………………. 35
4.4. Validarea, sanitizarea și escaparea opțiunilor ………………………….. ………………………….. ………….. 36
Capitolul 5: Internaționalizarea plugin -ului ………………………….. ………………………….. ………………………….. ….. 40

5.1. Internaționalizarea codului ………………………….. ………………………….. ………………………….. ………………. 40
5.2. Crearea fișierului de traducere pentru limba română ………………………….. ………………………… 42
Capitolul 6: Formatarea codului sursă și documentarea acestuia ………………………….. …………………….. 44
6.1. Formatarea codului sursă folosind standardele de codare WordPress ……………………….. 44
6.2. Documentarea codului sursă folosind standardele DocBlock ………………………….. …………… 46
6.3. Verificarea automată a formatării cu ajutorul unui sniffer de cod ………………………….. …… 49
Concluzii ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. ……….. 51
Provocări la dezvoltarea plugin -ului și soluțiile găsite ………………………….. ………………………….. …… 51
Opinia personală despre procesul de dezvoltare al plugin -ului ………………………….. ……………….. 51
Posibile îmbunătățiri viitoare ………………………….. ………………………….. ………………………….. ………………….. 51
Bibliografie ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. ….. 52
Anexa 1: Determinarea accesului în funcți e de rol și adresă IP ………………………….. …………………………. 53
Anexa 2: Panoul de control și sanitizarea opțiunilor ………………………….. ………………………….. ………………. 61
Anexa 3: Popularea dinamica a librariei de fonturi folosind Google Fonts API ………………………….. 72

Lista figurilor

[se va complete la final]

Lista tabelelor

[se va complete la final]

Lista acronimelor

[se va complete la final]

Introducere

WordPress este cel mai popular sistem open source de gestionare a conținutului existent la
momentul actual oferind posibilitatea de a publica, edita, modifica, organiza, șterge și între ține
conținutul unei baze de date dintr -o singură interfață centra lizat ă.
Datorită ușurinței de utilizare și a posibilită ților mari de personalizare prin intermediul temelor
si plugin -urilor, WordPress a devenit lider de piaț ă, fiind utilizat de nume mari precum Google,
Facebook, Microsoft, Disney, Sony, Mozilla, LinkedIn, TechCrunch, Bloomberg , New York Times,
CNN, etc.
Si totuși, ce înseamnă asta transpus în cifre? Conform unui studiu realizat de W3Tech , o
companie ce monitorizeaz ă utilizarea tehnologii lor web, WordPress este motorul din spatele a
30% din totalul site -urilor existente in Internet si a 60% din totalul site-urilor al căror s istem
de gestionare a conținutului este cunoscut .
În cadrul acestei teze va fi prezentat un plugin WordPress al cărui rol este de a preveni accesul
public la conținutul site-urilor WordPress in diferite sta dii ale ac estora : când sunt în
construcție , în stadiu de mentenanță sau când accesul la conținut este permis doar unui număr
restrâns de oameni. (site -uri interne pentru companii , platforme de curs private, etc)
Folosind acest plu gin, administratorii site-urilor pot controla în mod granular accesul
utilizatorilor în funcție de rolul acestora ( pentru utilizatorii care au cont -uri) sau în funcție de
adresa IP a acestora (pentru utilizatorii care nu au cont -uri), având posibilitatea de a folosi atât
IPv4 cât și IPv6 în format standard, wildcard, CIDR sau interval de IP -uri.

Motiva ția alegerii temei

Mi-am ales această te mă deoarece sunt pasionat de tehnologie și programare web și consider
că dezvoltarea unei astfel de aplicații W ordP ress mă poate aduce mai aproape de obiectivul meu
profesional: lansa rea unui magazin online cu produse digitale ( teme și plugin -uri premium
pentru WordPress )
În plus, d ezvoltarea acestui plugin reprezintă un exercițiu excelent pentru înțelegerea
diferitelo r tehnologii și procese de lucru utilizate în cadrul pachetelor software distribuite
global precum și o întelegere foarte bun ă a arhitecturii WordPress , a API -urilor acestuia, a
modalităților de integrare cu alte pachete software și nu în ultimul rând, securizarea ,
internaționalizarea și documentarea inline a acestuia .
Mai mult , dezvoltarea modulului de acces pe bază de rol și adresă IP din cadrul acestui plugin
va deveni un punct excelent de plecare pentru dezvoltarea unui firewall web în cadrul un plug in
de securitate pe care am de gând să îl lansez în viitor.

Gradul de noutate al temei

Există mai multe plugin -uri asemanatoare, atât gratuite cât și premium , însa în majoritate
acestea sunt pline de bug -uri, sunt greu de configurat sau pur și simplu au un design oribil .

Ce e șocant însă e că în majoritate acestea folosesc framework -uri externe pentru panoul de
control , încarcând inutil în memorie librării masive de cod. Acest lucru duce la un timp mar e de
încarcare al site-ului și uneori la probleme de memorie pe serverele cu pu ține resurse.
În plus, foarte puține plugin -uri conțin un modul de control al accesului în funcție de adresă IP
iar când acesta există , accesul se face în funcție de IP individual în format IPv4.
Drept urmare, în urma investigațiilor mele, nu există nici un plugin WordPress care îndeplinește
toate cerințele de mai jos:
– ușor de configurat
– intuitiv la navigare
– impact minim la timpul de înc ărcare
– design pl ăcut cu posibilități de personalizare în panou l de control
– posibilit ăți avansate de configurare a accesului în funcție de rol și adresă IP, cu
posibilitatea de a folosi atât IPv4 cât și IPv6 în format standard, wildcard, CIDR sau
interval de IP -uri
– neintruziv în panoul de control WordPress (fără panouri publicitare, upsell -uri la
versiuni premium, culori stridente, etc )

Obiectivele generale ale proiectului

Proiectul de licență își propune următoarele obiective:
– prezentarea conceptelor care stau la baza dezvoltării unui plugin WordPress (redat în
cadrul capitoul ui 1 al tezei )
– dezvoltarea unui plugin WordPress cu rolul de a preveni accesul public la conținutul site –
urilor WordPress in diferite stadii ale acestora: când sunt în construcție, în stadiu de
mentenanță sau când accesul la conținut este p ermis doar unui număr restrâns de
utilizatori (redat în capitolele 2 -7 ale tezei )

Metodologia folosit ă

Ca metodologie de lucru, am avut într -o primă etapă o abordare axată pe cercetarea pe Internet
a diverselor plugin -uri WordPress cu aplicabilități asemănătoare . Această etapă de studiu a
durat aproximativ un an, timp în care am studiat documentația oficială WordPress și am parcurs
mai multe cărți , cursuri și articole de specialitate scrise de experți în domeniu.
Pentru întregirea cunoștințelor și o î nțelegere mai bună a arhitecturii și a standardelor de
codare WordPress am studiat atât codul WordPress cât și codul mai multor plugin -uri etalon
din industrie : WooCommerce, Easy Digital Downloads, MonsterInsights, WPForms, Advanced
Custom Fields , Yoast SEO, etc.
Am participat de asemenea la mai multe conferințe și evenimente WordPress precum
WordCamp Romania, WordCamp Europa și Bucharest WordPress Meetup , în cadrul cărora am
luat legătura cu specialiști din domeniu .

A doua etapă a constat in defin irea foarte clară a funcționalității și a interfeței grafice, atât
pentru panoul de control, cât și pentru partea de front -end. Acest pas a constat în crearea mai
multor machete grafice în Photoshop (pentru partea de front -end) și a unei organizări riguroa se
a setărilor în panoul de control.
După ce am definit foarte bine structura finală a plugin -ului am trecut la cea de -a treia etapă :
dezvoltarea propriu -zisă a plugin -ului. Dezvoltarea acestuia am facut -o în PHP, folosind
multiple API -uri, atât native Wor dPress, cât și externe: Plugin API, Settings API, Options API,
Transients API, Google Fonts API, etc .
Foarte importantă în cadrul acestui pas a fost scrierea codului sursă folosind standardele de
codare WordPress si documentarea inline a acestuia folosind standardele DocBlock . Această
practică permite o colaborare ușoara cu alți developeri , acceptarea pachetului software în
repository -ul official de la WordPress și de ce nu, generarea automat ă de documentație tehnică
folosind soft -uri precum phpDocumentor sau DocBlox.
Ulima etapă a constat în testarea propriu -zisă a produsului final. În cadrul acesteia am testat
toate scenariile posibile de configurare, folosind atât valori valide cât și invalide. Am rugat de
asemenea mai mul te persoane (atât programatori cu experiență , cât și utilizatori normali de
WordPress ) să testeze plugin -ul și să-mi ofere feedback atât pe partea de uzabilitate, cât și pe
partea de tehnică , cu scopul de a identific a bug-uri vizuale sau de funcționalitate.

Contribu ția studentului

Contribuția originală a studentului constă în:
– dezvoltarea unui plugin WordPress pentru protejarea conținutului pe site -urile private,
aflate în construcție sau în stadiu de mentenanță
– crearea unui panou de control cu opțiuni avansate de customizare a modu lui de
funcționare și a aspectului interfeței afișate vizitatorilor fără drepturi de acces
– implementarea unui mod de acces fără restricții în funcție de rolul utilizatorului sau
adresa IP a acestuia cu posibilitatea de a folosi atât IPv4 cât și IPv6 în format standard,
wildcard, CIDR sau interval de IP -uri
– internaționalizarea plugin -ului și crearea unui fișier de traducerea pentru limba româna
cu scopul de a permite utilizarea acestuia în limba preferată a utilizatorului (engleză sau
română, cu posibilitatea de a adăuga în viitor suport și pentru alte limbi )
– securizarea codului sursă prin validarea, sanitizarea și limitarea datelor acceptate în
panoul de control
– dezvoltarea plugin -ului folosind multiple API -uri, atât native WordPress, cât și externe:
Plugin API, Settings API, Options API, Transients API, Google Fonts API, etc
– scrierea codului sursă folosind standardele de codare WordPress si documentarea inline
a acestuia folosind standardele DockBlock

Capitolul 1: Plugin -urile WordPress – Privire de ansamblu

1.1. Ce este un plugin

Un plugin WordPress este un script PHP care extinde sau modifică funcționalitatea de bază
existent ă în core -ul WordPress. Cu alte cuvinte, plugin -urile sunt instalate în WordPress pentru
a adăuga sau a modifica anumite caracteristici . Modul de funcționare al acestora este foarte
asemănător cu cel al aplicaț iilor terțe pe telefoanele mobile.
Com plexitatea acestora variază de la un caz la altul în funcție de problema pe care încearcă să o
rezolve . Putem vorbi în cadrul acestui ecosistem software atât despre plugin -uri simple, care
aduc schimbari minore la co mportamentul implicit , cât și despre plugin -uri complexe cu
funcționalități precum ecommerce, caching sau securitate.
Datorită unei structuri foarte bune și a diverselor API -uri disponibile în WordPress , nu există
nici o limită când vorbim de posibilitățile de extindere a funcționalității prin intermediul plugin –
urilor.

1.2. Avantajele plugin -urilor

WordPress oferă o mulțime de avantaje prin folosirea plugin -urilor . Cele mai importante sunt:

Nemodificarea core -ului WordPress:
Unul din principalele avantaje în utilizarea plugin -urilor este posibilitatea de a modifica
comportamentul WordPress fără a atinge fișierele din core.
Modificarea acestora duce la pierderea posibilității de a face update fără pierderea schimb ărilor
la lansări de versiuni noi ale pachetului software precum și instabilitate în execuție. Foarte
multe fișiere în WordPress sunt interdependente , modificarea unuia putând duce la efecte
neașteptate .

Conveniență și ușurință în dezvoltarea codului
Un alt av antaj îl reprezintă existența API-urilor. Multe funcționalități comune sunt disponibile
în core -ul WordPress și pot fi utiliza te direct în codul plugin -ului. Astfel, nu mai este nevoie să
construim de la zero anumite module. De exemplu, funcția wp_mail() ne permite să trimitem
mail -uri programatic fără a mai fi nevoie sa creăm funcționalitatea din spate. E de ajuns
introducerea unor parametrii la intrare și WordPress se ocupă de restul.
Utilizarea functionalităților deja existente în WordPress ne poate aj uta să reducem în mod
considerabil timpul necesar dezvoltării unui plugin. Mai mult, acest lucru permite maximizarea
compatibilității între plugin -ul nostru, core -ul WordPress și restul plugin -urilor instalate.

Ca o concluzie, când dezvoltăm pachete softw are pentru un anumit ecosistem (fie că vorbim de
WordPress sau de un alt sistem ) trebuie să folosim pe cât posibil funcțiile deja existente .

Separarea plugin -urilor de teme
Un plugin poate prelua contolul asupra procesul de randare devenind în acest fel o “temă”. În
mod similar, o temă poate include și funcționalitate de plugin. Din acest motiv, diferența dintre
cele două poate deveni destul de confuză pentru un programator neexperimentat.
Totusi, de ce nu includem codul unui plugin direct în te mă? Răspunsul este unul simplu: plugin –
urile sunt menite să adauge funcționalita ți noi pe când rolul temelor este de a controla felul în
care informația stocată în baza de date este afișata utilizatorilor. În acest fel avem o separație
clară între functionalit ate și prezentare.
WordPress este construit modular pentru a putea schimba cu ușurință tema și o data cu ea
întregul aspect al site -ului. Dacă funcțiile plugin -ului ar fi inclus e în temă , la schimbarea acesteia
am pierde toată funcționalitatea necesară unei bune funcționări. Când avem o separație clară
între cele dou ă, schimbarea temei nu ne afectează în nici un fel .

Actualizări software automate
WordPress permite actualizarea automată a unui plugin la ultima versiune. Pentru plugin -urile
instalate din rep ository -ul oficial se primesc notificări în mod automat la apariția de noi
versiuni.
Pentru restul, dezvoltatorul plugin -ului trebuie să definească o locație externă și să facă
integrarea cu un sistem de update -uri. În lipsa acestor adăugiri, plugin -urile nu pot fi actualizate
automat. (doar manual, prin FTP)
Notă: Actualizarea softurilor la zi este foarte important ă. Acesta este singurul mod în care un site
poate fi menținut făra bug-uri și vulnerabilități de securitate .

Ușor de distribuit și reutilizat
Plugin -urile sunt ușor de distribuit. Este mult mai ușor de partajat un plugin decât să cerem
cuiva să modifice codul sursă în cadrul unei teme sau chiar din core -ul WordPress.
Încapsularea codului în plugin -uri permite totodată reutilizarea acestuia într e site -uri cu o
ușurință extraordinară . Instalarea unui plugin dureaza câteva secunde.

Protec ție la erori
Activarea unui plugin defectuos în WordPress nu va strica întregul site. Dacă plugin -ul
declanșează o eroare fatală , WordPress îl va dezactiva în mod automat înainte ca acesta să poată
rula. Există situații când astfel de plugin -uri defectuase cauzează ecrane albe datorită erorilor,
însa mulțumită unui sistem bine pus la punct, simpla redenumire a director -ului de root al
plugin -ului va duce la dezactivarea acestuia , administratorul site -ului putând reveni cu usurință
la starea anterioară.

Pe de altă parte, când se fac modificări direct în core -ul WordPress , o eroare fatală poate duce
la daune definitive, mai ales când sunt executate instrucțiuni ce afectează baza de date.

1.3. Tipuri de plugin -uri

WordPress utilizează mai multe tipuri și stări pentru plugin -uri. O bu nă înțelegere a rolului și a
diferențelor dintre ele este foarte impor tant ă pentru procesul de administrare, respectiv
dezvoltare a acestora .

Activ : plugin -ul este activ și rulează în WordPress .
Inactiv : plugin -ul este instalat , dar nu este activ. Codul plugin -ului nu este executat.
Obligatoriu : plugin -ul este instalat în directorul wp-content/mu -plugins și este încarcat
automat. Singura modalitate prin care acesta poate fi dezactivat este prin ștergerea sa manuală
prin FTP.
Drop -In: funcțiile core ale WordPress pot fi înlocuite cu plugin -uri de tip Drop -In. Ace ste plugin –
uri sunt un singur fișier PHP specificat în mod explicit în directorul wp-content . Dacă
WordPress detectează un astfel de fișier , acesta va fi încărcat în mod automat și afișat sub
meniul de Drop -Ins. În present există 10 plugin -uri de acest tip:
– advanced -cache.php – plugin avansat de caching
– db.php – clasă personalizată pentru procesarile în baza de date
– db-error.php – mesaj personalizat de eroare pentru procesările în baza de date
– install.php – script personalizat de instalare
– maintenance.ph p – mesaj personalizat de mentenanță
– object-cache.php – cache de obiecte extern
– sunrise.php – mapare avansată a domeniilor pentru multisite
– blog-deleted.php – mesaj personalizat la stergerea unui blog din multisite
– blog-inactive.php – mesaj personalizat la inactivarea unui blog din multisite
– blog-suspended.php – mesaj personalizat la suspendarea unui blog din multisite

1.4. Cum funcționează interacțiunea dintre WordPress și plugin -uri

WordPress pune la dispoziție mai multe API -uri pentru a fi utilizate în dezvoltarea plugin -urilor.
Fiecare din ele permite interacțiunea cu WordPress într -un mod diferit. Mai jos sunt enumerate
principalele API -uri disponibile si funcțiile pe care acestea le îndeplinesc:
Plugin API
Acest API pune la dispoziție un set de cârlige care permit accesul plugin -urilor la anumite părți
ale WordPress. WordPress conține două tipuri diferite de cârlige : acțiuni și filtre .

Cârligul de tip acțiune permite declanșarea unei anumite bucăți de cod la anumite punct e în
timpul execuției . De exemplu, o funcție poate rula înainte sau după urcarea unui fișier media în
librăria WordPress .
Cârligul de tip filtr u modifică valoarea unei variabile înainte sau după preluarea valorii acesteia
din baza de date.

Settings API
Prin utilizarea acestui API se pot crea setări sau secțiuni de setări pentru plugin -uri. Principalul
avantaj în folosirea acestuia este securitatea. Toate datele setărilor sunt curățate de elementele
nedorite. Astfel, programatorii nu trebuie să -și facă griji î n privința atacurilor de tip XSS sau
CSRF în timpul salvării informațiilor în baza de date.

Options API
Prin utilizarea acestui API se stochează și se preiau opțiunile salvate în baza de date. Acesta
dispune de capacitatea de a crea noi opțiuni, de a actu aliza opțiunile existente, de a le șterge sau
de a prelua orice opțiune deja definită.

Transients API
Prin utilizarea acestui API se pot crea opțiuni temporare. Ca structură este foarte similar cu
Options API, singura diferență fiind faptul că toate opțiu nile sunt salvate cu un timp de expirare.

Metadata API
Prin utilizarea acestui API se face accesarea și manipularea diferitelor tipuri de obiecte
WordPress de tip metadata. Metadatele sunt perechi de tip cheie -valoare și conțin informații
suplimentare des pre obiectul părinte. În WordPress există 3 tipuri implicite de metadata: post
metadata, comment metadata și user metadata.

Rewrite API
Prin utilizarea acestui API se pot crea reguli personalizate de rescriere a permalink -urilor. Cu
ajutorul lui se pot ad ăuga elemente statice (end -point -uri) în cadrul link -urilor precum /post-
type/, etichete de structură precum %postname% și se pot crea feed -uri RSS personalizate.

Database API
Prin intermediul acestui API se accesează baza de date WordPress. Acesta dispune de
capacitatea de a crea, de a actualiza, de a șterge sau de a recupera înregistrări din baza de date
pentru a fi utilizate la dezvoltarea plugin -uri.

Shortcode API
Acest API permite adaugarea de su port pentru shortcode -uri în cadrul conținutului. Un
shortcode este un cârlig care permite apelarea unei funcții PHP prin adăugarea unor elemente
precum [shortcode] în conținutul unui post sau a unei pagini.
Prin folosirea shortcode -urilor se permite utili zatorilor finali să controleze unde se execută o
anumita bucată de cod.

Widgets API
Acest API permite crea rea, modifica rea sau distruge rea widget -urilor afișate în orice bară
lateral ă înregistrată de tema activ ă. De asemenea, acest API permite ca mai multe instanțe ale
aceluiași widget să fie utilizate concomit ent.

Dashboard Widgets API
Acest API permite crearea de widget -uri pentru panoul de control al administratorilor. Widget –
urile apar în mod automat pe panoul de control și conțin toate eleme ntele standard de
personalizare, inclusive funcțiile de drag -and-drop și de ascundere în funcție de preferințele
fiecărui utilizator.

HTTP API
Prin intermediul acestui API se pot trimite cereri HTTP în baza cărora se poate prelua con ținut
de la o adresă URL externă sau se poate trimite conținut către aceasta .
În present există mai multe metode de a trimite cereri HTTP. Acest API standardizează acest
proces și testează fiecare metodă înainte de executare pentru a determina metoda corectă în
funcție de conf igurația serverului.

REST API
Acest API pune la dispoziție o serie de end -point -uri pentru diferite tipuri de date WordPress
ce permit programatorilor să interacționeze cu site -uri și aplicații externe prin trimiterea și
primirea de obiecte JSON.
JSON es te un format de date standard deschis , ușor de interpretat , ce permite citirea, crearea și
actualizarea conținutului WordPress din JavaScript sau din aplicații externe , indiferent de
limbajul de programare folosit la dezvoltarea acestora.

File Header API
Acest API permite interpretarea antetelor definite în fișiere . Temele si plugin -urile WordPress
conțin unul sau mai multe fișier e. Dintre acestea, unul este întotdeauna de tip obligatoriu și
conține meta -informații precum Nume, Descriere, Versiune, Autor, Licența, etc.

Aceste informații sunt plasate în interiorul unui bloc -comentariu aflat la începutul fișierului sub
forma unor însiruiri de informații de tip Nume: Valoare .

Filesystem API
Acest API a fost create initial pentru funcția de actualizare automat a a WordPress -ului. În
present, acesta abstractizează funcționalitatea necesară pentru citirea și scrierea fișierelor
locale pe server în mod securizat.
Orice plugin sau temă ce are nevoie să scrie fișiere la nivel local trebuie să folosească acest API.

Theme Modification API
Prin utilizarea acestui API se stochează și se preiau opțiunile specific temelor salvate în baza de
date. Acesta dispune de capacitatea de a crea noi opțiuni pentru teme , de a le actualiza sau de a
le șterge pe cele existente . Acest API este foarte similar cu Options API .

Theme Customization API
Prin utilizarea acestui API se pot crea controale în panoul de personalizare a temelor unde
administratorii pot schimba setările acestora și pot previzualiza în timp real efectele
modificărilor efectuate . Acest API este foarte similar cu Settings API.

Suprascrierea f uncții lor pluggable
Pe lângă API -uri, WordPress include și funcții pluggable. Acestea ne permit suprascrierea unor
funcții de bază pentru a altera comportamentul implic it. De exemplu, funcția wp_mail() poate
fi suprascrisă pentru a trimite mail -uri prin intermediul SMTP și nu prin metoda implicită care
poate activa foarte ușor filtrele de spam ale clienților de email.
Toate funcțiile pluggable sunt definite în /wp-include/pluggable.php .
Există de asemenea o serie de funcții predefinite ce pot fi folosite când WordPress efectuează
anumite task -uri precum activarea, dezactivarea sau stergerea definitivă a unui plugin. Cu
ajutorul acestor funcții putem crea setări implicite sau prelucrări speciale în baza de date la
activare sau dezactivare și foarte important , putem face curățenie în baza de date la
dezinstalare.

1.5. Lista de API-uri folosite la dezvoltarea plugin -ului

Pentru dezvoltarea plugin -ului prezentat în aceasta lucrare au fost utilizate următoarele API –
uri:

API-uri WordPress folosite
Nume API Utilizare API
Plugin API Acest API este esențial la dezvoltarea plugin -ului. Cu ajutorul lui definim
momentele de timp la care se execută anumite secțiuni de cod. Fără el nu se
poate face integrarea cu WordPress.
Settings API Acest API este folosit la crearea panoului de control , integrarea acestuia în
interfața WordPress și la securizarea opținilor înainte de salvarea datelor în
baza de date .
Options API Acest API este folosit pentru stocarea, preluarea și modificarea opțiunilor
salvate în baza de date. Cu ajutorul lui manipulăm informațiile introduse de
administrator în panoul de control.
Transients API Acest API este folosit pentru crearea de opțiuni temporare. În cazul nostru,
el este folosit pentru stocarea listei de font -uri Google și la reactualizarea
automată a acesteia din 30 în 30 de zile.
Metadata API Acest API ne permite stocarea de informație adițională. În cazul n ostru, el
este folosit pentru a ține minte când un administrator ascunde notificările
afișate în panoul de control .
HTTP API Acest API este folosit pentru trimiterea de cereri HTTP către site -uri și
aplicații externe. În cazul nostru, el este folosit pentru a prelua lista de font –
uri Google . (inclusive lista locală, când serviciile Google nu funcționează )
Filesystem API Acest API este folosit pentru manipularea fișierelor locale. În cazul nostru, el
este folos it pentru accesarea fișierului JSON de backup cu lista locală de font –
uri Google. (accesat când serviciile Google nu funcționează)
File Header API Acest API permite interpretarea antetelor definite în fișiere. În cazul nostru,
el este folosit la crearea a ntetului din fișierului principal raindrop.php
pentru a permite identificarea acestuia ca plugin.

Tabel 1.1 . API-uri WordPress folosite la dezvoltarea plugin -ului

API-uri externe folosite
Nume API Utilizare API
Google Fonts API Acest API permite utilizarea font -urilor Google. În cazul nostru, el este
folosit pe pagina de întâmpinare pentru a încărca font -urile definite de
administrator în panoul de control. (cu suport pentru limbile latine,
inclusive cele cu diacritice )

Tabel 1. 2. API-uri extern e folosite la dezvoltarea plugin -ului

Capitolul 2: Instalarea, c onfigurarea și utilizarea plugin -ului

În cadrul acestui capitol presupunem că avem deja un server sau un mediu de test pe
localhost cu un site WordPress gata configurat.
Stiva software recomandată de WordPress este :
– Apache sau Nginx
– PHP versiunea 7.2
– MySQL versiunea 5.6 sau MariaDB versiunea 10
– Suport HTTPS

2.1. Instalarea plugin -ului

În WordPress există 3 metode de a instala un plugin:
– Instal are automat ă din repository -ul oficial folosind panoul de control WordPress
– Instalare automată din sursă locală folosind panoul de control WordPress
– Instalare manuală prin SFTP
Deoarece plugin -ul nostru nu este distribit prin intermediul repository -ul oficial ne vom
concentra atenția asupra ultimelor două metode de instalare.
Pentru a putea face instalarea unui plugin trebuie sa fim conecta ți în panoul de control cu un
utilizator administrator sau cu drepturi de management pentru plugin -uri. Drepturile de
acces necesare în acest caz sunt:
– install_plugins
– activate_plugins
– delete_plugins
– update_plugins

Instalare automată din sursă locală folosind panoul de control WordPress
Instalarea automată este cea mai ușoară metodă de instalare deoarece WordPress gestionează
transfer ul de fișiere. Pentr u a efectua o instalare automată trebuie să ne conectăm în panoul de
control și să urmă rim pasii de mai jos:
1. Descarcă o arhivă a plugin -ului pe calculatorul local în format .zip
2. Navighează la meniul Plugins și apasă pe butonul “Add New”
3. Apasă pe butonul “Upload Plugin” și selectează arhiva .zip descărcată anterior
4. Efectuează instalarea plugin -ului apasând pe butonul “Install Now”
5. Activează plugin -ul apăsând pe link -ul afișat în mesajul de confirmare a instalării sau din
meniul “Plugins”

Figura 2.1. Instalare automată din sursă locală

Instalare manuală prin SFTP
Instalarea manuală presupune presupune descărcarea plugin -ului și urcarea acestuia în formă
dezarhivată pe server via SFTP. Pentru a efectua o instalare manuală trebuie să urmari m pașii
de mai jos:
1. Descarcă plugin -ul pe calculatorul local
2. Dacă plugin -ul a fost descărcat în format .zip efectuează dezarhivarea acestuia
3. Urcă directorul plugin -ului prin SFTP în directorul /wp-content/plugins/
4. Activează plugin -ul din meniul “Plugins”

Figura 2.2. Instalare manuală prin SFTP

Activarea plugin -ului este un moment foarte important din punct de vedere al execuției
codului . La apăsarea butonului de activare se execută un script care crează o opțiune de tip
array() în baza de date cu toate valorile implicite pentru setările plugin -ului. Dacă există fișier
de traducere pentru limba curentă a site-ului, aceste opțiuni sunt sunt salvate cu valorile lor
traduse. Dacă nu, valorile sunt setate în limba engleză.
Foarte important de precizat, aceste valori sunt salvate doar la prima activare pentru a evita
pierderea setărilor personalizate de utilizator în urma mai multor activări/dezactivări
successive.
Dezactivarea plugin -ului activează de asemenea un script , însă momentan acesta nu face
nimi c. A fost adăugat în plugin pentru a permite extinderea lui în viitor, dacă este cazul.
Ștergerea plugin -ului este iar un moment foarte important din punct de vedere al execuției
codului. La apăsarea butonului de ștergere se execută script -ul din fișierul uninstall.php
aflat în directorul rădăcină al plugin -ului. Rolul lui este de a curăța baza de date în momentul în
care utilizatorul decide să facă o ștergere definitivă a plugin -ului.
După încărcarea întregului cod în memorie există un moment critic pentru execuția fără
probleme a plugin -ului. Acesta ne permite verificarea stivei software instalată pe server și
previne execuția codului dacă se detectează nu îndeplinim minimul de cerințe pentru rularea
plugin -ului.

În cazul nostru, facem verificarea pentru versiunea de PHP. Pentru o execuție fără probleme
avem nevoie de minim PHP 5.3. Evident, aceasta este o versiune foarte veche cu breșe de
securitate pe care nimeni nu ar mai trebui să o folosească în ziua de azi.
Dacă detectăm o versiune nesuportată de PHP prevenim execuția codului și afișăm un mesaj în
panoul de control sfătuind administratorii să treacă la o versiune mai nouă.

Figura 2.3. Mesaj de eroare la verificarea versiunii de PHP

2.2. Panoul de control principal

După activarea cu success a plugin -ului trebuie să îl configurăm. Panoul de control al acestuia
se poate găsi sub meniul de Setări al WordPress. Pentru relevanță, numele sub care este listat
plugin -ul în acest meniu se schimbă dinamic, în funcți e de modul în care rulează: În construcție,
În mentenanță, Website privat sau Mod personalizat.
În momentul apăsării pe link -ul de setări suntem întâmpinați de panoul de control principal al
plugin -ului.
Deoarece toate setările plugin -ului au valori implicite, singurele opțiuni ce necesită configurare
sunt cele afișate în cadrul acestui panou . Toate celelalte , accesibile prin intermediul tab -urilor
și subtaburilor , sunt opționale .
Acest mod de funcționarea al plug in-ului cu un minim de configurare a fost gândit încă din faza
de proiectare. (s-a do rit obținerea unui plugin ușor de folosit de către utilizatorii începători ,
păstrând totodată posibilitatea de personalizare granulară pentru utilizatorii experimentați )
Ca o configurație minim ă, pentru utilizarea plugin -ului trebuie setat în mod explicit statusul
acestuia ca activ și selectat unul din cele 4 moduri în care plugin -ul poate rula :
– În construcție
– În mentenanță
– Webs ite privat
– Mod personalizat
Notă: Codul sursă al panoului de control și sanitizarea opțiunilor ce pot fi controlate prin
intermediul acestuia poate fi văzut în Anexa 2 .

Figura 2.4. Setările plugin -ului – Panoul de control principal

Înainte de a trece mai departe la prezentarea restului de setări trebuie să explicăm fiecare mod
de funcționare în parte pentru o mai bună înțelegere a acestora :

Modul “În construcție”
Acest mod a fost creat special pentru site -urile aflate în stadiu de construcție, care nu au fost
publicate/indexate pe internet . Pe lângă un design dedicat cu setări separate de prezentare și
conținut există două directive setate în mod automat:
– Răspunsul serverului de tip HTTP 200 (OK)
– Instrucțiunile de indexare SEO ca noindex, follow , indiferent de setările generale de
indexare ale site -ului

Modul “În mentenanță”
Acest mod a fost creat special pentru site -urile aflate în stadiu de mentenanță care sunt deja
publicate/indexate pe internet . Pe lângă un design dedicat cu setări separate de prezentare și
conținut există două directiv e setate în mod automat:
– Răspunsul serverului de tip HTTP 503 ( Temporar indisponibil )
– Instrucțiunile de indexare SEO ca index, follow pentru site -urile publice sau noindex,
nofollow pentru site -urile private

Modul “Website privat”
Acest mod a fost creat special pentru site -urile private pentru care accesul la conținut este
permis doar unui număr restrâns de oameni. (site -uri interne pentru companii, platforme de

curs private, etc) Pe lângă un design dedicat cu setări separate de pre zentare și conținut există
două directive setate în mod automat:
– Răspunsul serverul ui de tip HTTP 200 (OK)
– Instrucțiunile de indexare SEO ca noindex, nofollow , indifferent de setările generale de
indexare ale site -ului

Modul “ Mod personalizat ”
Acest mod a fost creat pentru a oferi flexibilitate maximă la configurare. Prin utilizarea acestuia,
administratorii pot controla tipul de răspuns al server -ului , instrucțiunile de indexare și mai
ales partea de design. Acesta este singurul mod care permite adăugarea de cod HTML pentru
personalizarea paginii de întâmpinare afișată utilizatorilor fără acces.

2.3. Setările de acces în funcție de rol și adresă IP

Mergând mai departe la setările de acces, putem defini cu exact itate cine poate accesa fără
restricții conținutul site -ului . Accest acces poate fi configurat în funcție de rolul utilizatorilor
(pentru persoanele care au credențiale de acces ) sau în funcție de adresa IP a acestora. (pentru
persoanele care nu au credențiale de acces )

În cadrul acestui panou, lista cu roluri este populat ă automat pe baza celor deja existente in
WordPress. În cazul în care avem înregistrate unul sau mai multe roluri custom (în afară de cele
existente în WordPress în mod implicit ), această listă este ordonată în mod alfabetic. Dacă avem
doar rolurile implicite, lista este ordonată în funcție de drepturile de acces , de la rolul cu cele
mai multe drepturi, la cel cu cele mai puține.
Ca metodă de siguranță, rolul de administrator va avea întotdeauna acces nelimitat pe site. Dacă
la configurare un utilizator de select ează rolul de administrator din listă , în urma salvării, acesta
va fi selectat înapoi. Pentru conveniență s -ar fi putut ascunde acest rol din listă , însă am dorit a
indic a într-un mod subtil , fără explicații text că utilizatorii cu rol de administrator au
întotdeauna acces.

Pentru reguli le de acces în funcție de adres a IP a utilizatorului se acceptă atât IPv4 cât și IPv6
în format standard, wildcard, CIDR sau interval de IP -uri. Ca logică, toate regulile, indiferent de
tipul lor, sunt transformat e în format CIDR care la rândul lor sunt transformat e în interval e de
IP-uri.
IP-ul utilizatorului si capetele intervalelor obținute sunt transformate în binar . Pe baza unor
verificări bi t cu bit se determină dacă IP-ul utilizatorul ui se află în intervalul de acces. S-a ales
utilizarea acestei metode și nu folosirea de calcule matematice datorită limitărilor impuse de
variabilele PHP : acestea lucrează pe 64 de biți ceea ce face imposibilă utilizarea lor pentru
stocarea valorilor numerice ale IP -urilor în format IPv6 . (valori pe 128 de biți)

Figura 2.5. Setările plugin -ului – Setările de acces în funcție de rol și adresă IP

Notă: Algoritmul pentru determinarea accesul ui și a modului în care plugin -ul preia controlul
asupra interfeței WordPress când un utilizator nu are dreptul de acces poate fi văzut în Anexa 1 .

2.4. Setările pentru motoarele de căutare

Ecranul următor este dedicat setărilor specifice motoarelor de căutare. Fără nici o
configurație , setările SEO sunt generate automat în funcție de titlul și descrierea site -ului
adăugate în panoul de setări generale WordPress. În cele mai multe cazuri, aceste valori sunt
de ajuns însă există situații când se preferă u tilizarea altor valori.
Din această secțiune a panoului de control administratorii pot controla directivele <meta>
pentru titlu și descriere în mod independent de valorile generale .

Figura 2.6. Setările plugin -ului – Setările pentru motoarele de căutare

2.5. Setările Google Analytics

În anumite situații se dorește monitorizarea traficului pe site chiar dacă accesul la conținutul
acestuia este blocat. De aceea am adăugat si o secțiune pentru integrarea cu Google Analytics .
Pentru a evita atacurile de ti p XSS am decis interzicerea adăugării scriptului de tracking în mod
direct de către utilizatori . În schimb, script -ul este adăugat în mod automat de plugin în
momentul în care se completează ID -ul proprietății din Google Analytics.
Ca îmbunătățire viitoare pentru această secțiune am putea implementa autentificarea plugin –
ului în contul Google prin intermediul OAuth2 . Acest lucru ar permite personalizarea dinamică
a scriptului de tracking în funcție de setările profilului definit în Google Analytics. ( în pre sent
script -ul este cel standard, fără support pentru opțiunile avansate de tracking )

Figura 2.7. Setările plugin -ului – Setările Google Analytics

2.6. Setările de culoare

Dacă până acum am vorbit numai de spre setări de funcțional itate , acum este momentul să
trecem la setările care influențează aspectul paginii prezentate vizitatorilor fără acces.
În cadrul secțiunii de setări pentru culori administratorii au posibilitatea de a seta o culoare
princială pe baza căreia se generează în mod d inam ic paleta de culori folosită la crearea paginii
publice. Tot aici se pot configura și culorile textelor afișate. (un titlu și o descriere)
Pentru utilizatorii care doresc control granular asupra aspectului ceasului afișat pe pagina
publică , tot de aici se pot controla și culorile pentru elementele principale ale acestuia: culoarea
feței ceasului, culoarea numerelor și culorile individuale ale limbilor.
În lipsa unor valori setate de administrator, culorile ceasului sunt generate automat, fie folosind
valori implicite setate la activarea inițială a plugin -ului, fie pe baza culorii principale.

Figura 2.8. Setările plugin -ului – Setările de culoare

Deoarece majoritatea oamenilor sunt utilizatori normali, fără cunoștințe tehnice de design,
cont roalele setărilor au fost implementate sub forma unor selectoare de culoare vizuale.

Figura 2. 9. Setările plugin -ului – Control de selectare a culorii

2.7. Setările de tipografie

Complementar setărilor de culoare există si o secțiune dedicat ă tipograf iei. De aici
administratorii pot controla fonturile textelor și stilizarea acestora folosind proprietăți precum
font-weight și text-transform .
Lista de font -uri disponibil e este populat ă dinamic , în ordine alfabetică , folosind Google Fonts
API. Aceasta este salvată în cache pentru 30 de zile folosind un transient pentru a preveni
efectuarea de request -uri către Google la fiecare încărcare de pagină. Pentru situațiile în care la
cererea de reactualizare a liste i se primește ca răspuns o eroare (endpoint API nedisponibil ,
eroare HTTP, etc ), lista este populat ă pe baza unui fișier JSON local .
Notă: Algoritmul de p opulare dinamică a librăriei de font -uri poate fi văzut în Anexa 3.

Figura 2. 10. Setările plugin -ului – Setările de tipografie

2.8. Setările de conținut pentru modurile predefinite

În cadrul secțiunii de setări pentru conținut se pot configura în mod individual text ele, imaginea
atașată fiecarui mod predefinit (în construcție, în mentenanță și website privat) și vizibilitatea
unui buton de conec tare în panoul de control WordPress.
Pentru câmpurile de titlu și descriere au fost setate valori implicite la activarea plugin -ului .
Acestea pot fi modificate de administrator sau eliminate total.
Pentru o personalizare mai bună, pentru fiecare mod se fol osește o altă ilustrație ca valoare
implicită a câmpului de configurare a imaginii.
Pentru butonul de selecție a imaginii s -a făcut integrare nativă cu librăria media din WordPress .
După selecția unei imagini controlul acestei setări se schimbă în mod dina mic pentru a conține
un buton de ștergere a imaginii selectate și o fereas tră de previzualizare a acesteia.

Figura 2.11. Setările plugin -ului – Setările de conținut pentru modurile predefinite

2.9. Setările de conținut pentru modul personalizat

Modul personalizat este cel care ne permite controlul total asupra conținutului și al design -ului
prin posibilitatea adăugării de cod HTML în panoul de control . Tot din această interfață putem
controla și header -ul HTTP returnat de server.
– HTTP 200 (OK)
– HTTP 503 (Temporar indisponibil)
La folosirea acestui mod, directivele de indexare precum si codul Google Analytics se adaugă
manual în câmpul de cod HTML.

Figura 2.12. Setările plugin -ului – Setările de conținut pentru modul personalizat

2.10. Modul de funcționare al plugin -ului

Acum, că am terminat de prezentat partea de configurare a plugin -ului e momentul să
recapitulăm câteva idei menționate anterior și să prezentăm modul de funcționare al acestuia ,
pas cu pas .
Fără a intra mult în detaliile tehnice din spate , după activarea și configurarea plugin -ului, acesta
verifică dacă vizitatorii au drepturi de acces nerestricționat pe site conform logicii prezentate
în subcapitolul 2.3.

Dacă răspunsul este DA, WordPress continuă încărcarea site -ului în mod normal oferind acces
la conținut. Dacă răspunsul este NU , plugin -ul nostru preia controlul asupra interfeței, atât în
panoul de control, cât și pe partea de front -end prin afișarea unei pagini de întâmpinare , cu
excepția pagin ii de conectare în cont.
Desig n-ul și conținutul paginii publice diferă în funcție de modul utilizat și de configurațiile
effectuate de administrator în panoul de control.
Pentru implementarea acestei pagini s-a folosit o versiune foarte mult simplificată a
framework -ului de frontend “Foundation for Sites” dezvoltat de ZURB. ( publicat sub licență
MIT )
Ilustrațiile folosite au fost cumpărate de subsemnatul, Eusebiu Oprinoiu, de la studioul Kit8 ,
prin intermediul CreativeMarket.com .

Figura 2.13. Pagina de întâmpinare cu valori implici te pentru cele 3 moduri predefinite

Capitolul 3: Structura internă a plugin -ului

[se va completa]

Capitolul 4: Securizarea plugin -ului

Securizarea codului este un subiect foarte important pe care orice programator are datoria să
îl cunoască și să îl aplice . Folosirea unui singur plugin cu breșe de securitate p oate duce la
compromiterea întregului server , expunerea publică a date lor confidențiale sau chiar pierderea
în totalitate a datelor .
Cele mai frecvente tipuri de atacuri sunt cele de tip Injecție SQL, XSS sau CSRF. Din fericire,
WordPress ne pune la dispoziție o mulțime de metode pentru protecția împotriva acestora. Ține
doar de noi să le utilizăm în procesul de dezvoltare al soft -ului.
Ca regulă principală, ca programatori, nu trebuie să avem încredere în nimeni . Orice informație
utilizată sau prelucrată trebuie considerat ă ca fiind invalidă și verificată, indiferent care este
sursa acesteia. ( din baza de date proprie, de la utilizator sau din sursă externă )
Același lucru este valid și pentru cererile de afișare sau prelucrare a informațiilor. Fără o
verificare în prealabil a accesului, nici o astfel de cerere nu trebuie să primească răspuns sau să
execute instrucțiunile primite .

4.1. Blocarea accesului direct la fișiere

Una din cele mai simple metode de securizare a plugin -urilor , dar totodata frecvent ignorată de
programatori, este blocarea accesului direct la fișiere. Deși în majoritate fișierele plugin -urilor
nu execută cod în mod direct, există cel puțin 2 fișiere care pot cauza probleme dacă nu sunt
protejate .
În cazul nostru este vorba de fișierul principal al plugin -lui, raindrop.php și fișierul cu script –
ul de dezinstalare, uninstall.php .
În mod normal, pe un server bine configurat și securizat nu se permite accesul direct la fișiere.
Cu toate acestea, e bin e să implementăm metode adiționale de protecție, direct în codul plugin –
ului, pentru utilizatorii care nu au server -ul bine configurat.
Pentru protejarea fișierelor sensibile este de ajuns să verificăm existența unor constante setate
de WordPress . Dacă ace stea nu există știm sigur că fișierele au fost chemate în mod direct și
codul existent în acestea nu trebuie executat.

if ( ! defined( 'WPINC' ) ) {
exit;
}

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}

4.2. Verificarea drepturilor de acces

Verificarea drepturilor de acces este esențială , fie că vorbim de utilizatori i autentificați în
panoul de control sau nu. WordPress are o funcție dedicată pentru astfel de verificări și este
recomandată u tilizarea ei în defavoarea împlementărilor propri i.
Funcția este current_user_can() și acceptă ca parametr ii de intrare denumiri de drepturi
de acces . (native WordPress sau înregistrate de alte plugin -uri)
O reprezentare vizuală a drepturilor de acces implicite disponibile în WordPress poate fi văzută
în figura de mai jos. În această listă sunt incluse și drepturile de acces disponibile pe instanțele
de WordPress Multisite .

Figura 4.1. Securizarea plugin -ului – Drepturile de acces implicite ale unui administrator
Pentru dezvoltarea acestui plugin s -au folosit multiple verificări ale drepturilor de acces
folosind funcția current_user_can() , însă în majoritate , acestea au fost folosite pen tru
afișarea de notificări în mod exclusiv administratorilor. Un astfel de exemplu este funcția prin
care afișăm eroarea de stivă software când se detectează o versiune veche de PHP.

public function update_php_warning() {
if ( current_user_can( 'manage_options' ) ) {
// Mesaj afișat administratorului.
}
}

Pentru verificarea accesului s-a ales manage_options deoarece doar administratorii au acest
drept de acces.
Pentru accesul în pagina de setări a plugin -ului nu a fost nevoie de o im plementare pe bază de
current_user_can() . Pentru adăugarea acesteia în meniul WordPress s-a folosit funcția
add_submenu_page() care ne permite să specificăm ce capabilitate este necesară pentru
accesarea acesteia.

/**
* Create the options page.
*
* Create an option page under the Settings menu from where we will be able
* to control how our plugin behaves.
*
* @since 1.0.0
*/
public function add_options_page() {
$raindrop = get_option( 'raindrop' );

switch ( $raindrop[ 'general -settings' ]['mode'] ) {
case 'under-construction' :
$menu_title = esc_html__( 'Under Construction' , 'raindrop' );
break;
case 'under-maintenance' :
$menu_title = esc_html__( 'Under Maintenance' , 'raindrop' );
break;
case 'private -website' :
$menu_title = esc_html__( 'Private Website' , 'raindrop' );
break;
default:
$menu_title = esc_html__( 'Raindrop' , 'raindrop' );
}

add_submenu_page(
'options -general.php' , // Parent menu slug.
esc_html__( 'Raindrop' , 'raindrop' ), // Page title.
$menu_title, // Menu title.
'manage_options' , // Capability.
'raindrop' , // Options page slug.
array( $this, 'options_page_content' ) // Callback function to output the content.
);
}

4.3. Utilizarea cheilor Nonce

Cheile Nonce , denumite pe scurt de la abrevierea cuvintelor “numer used once ”, sunt string -uri
unice de caractere , cu rol de parole temporare și cu o durată de viață limitată . Ele sunt atașate
în mod dinamic pentru:
– un utilizator
– un interval de timp de 24 de ore
– o acțiune precum salvare a, ștergere a și actualizare a datelor
– un obiect precum un articol, o pagină, un link, o setare , etc
Scopul lor este de a oferi o protecție suplimentară la atacurile de tip CSRF. Fără existența unui
nonce, aceste tipuri de atacuri sunt foarte ușor de efectuat.
De exemplu, o persoană rău intenționată poate constru i un link cu instrucțiuni de ștergere a
unei pagini din baza de date. Deși atacatorul nu poate folosi acel link pentru executarea
comenzii , îl poate păcăli pe administratorul site -ului, care are drept uri de acces, să acceseze
link-ul și automat , să execute instrucțiunea de ștergere .
Luând un exemplu arbitrar, când folosim nonce -uri, ștergerea unei pagini cu ID-ul #50 , este
posibilă doar pentru 24 de ore , doar dacă este cerută de utilizatorul cu nonce -ul 83a08fcbc2 ,
atribuit acestuia în mod arbitrar.
https:// licenta. eusebiu.me /wp-admin/post.php?post=50&action=trash&
wpnonce=83a08fcbc2

Pentru eficiență maximă, e bine ca nonce -urile să fie folosite concomitant cu verificarea
drepturilor de acces , folosind funcția current_user_can() .
În WordPress, există 2 tip -uri de nonce -uri:
– vizibile, folosite în structura URL -urilor
– invizibile, folosite în structura formularelor sub form ă de câmpuri ascunse
Pentru utilizarea nonce -urilor există mai multe funcții, dintre care amintim:
– wp_nonce_url()
– wp_nonce_field()
– wp_create_nonce()
– wp_verify_nonce()
– check_ajax_referer()
– check_admin_referer()
Pentru dezvoltarea plugin -ului nostru nu a fost nevoie să folosim nonce -uri în mod explicit
deoarece am folosit Settings API la crearea panoului de control. Unul din marile avantaje ale
acestui API este că nu trebuie să adăugam noi partea de securizare pe bază de nonce -uri. Aceasta
este făcută în mod automat de WordPress .
Dacă s -ar fi ales implementarea panoului de control folosind o altă metodă, responsabilitatea
securizării la atacurile de tip CSRF ar fi căzut în sarcina noastră.
Existența nonce -ului pentru formularul cu setările plugin -ului poate fi observată în imagin ea de
mai jos.

Figura 4.2. Securizarea plugin -ului – Cheia nonce a paginii de setări

4.4. Validarea , sanitizarea și escaparea opțiunilor

Validarea și sanitizarea opțiunilor este una din cele mai importante acțiuni pe care le poate
efectua un programator. Aceste două acțiuni trebuie effectuate întotdeauna înainte de
prelucrarea sau salvarea datelor în baza de date.
Datele pot veni din multe surse: din propria bază de date, de la utilizator sau chiar din surse
externe. Posibilitățile de atac su nt nenumerate și de aceea este esențial să facem verificări peste
tot, chiar cu riscul de a f ace verificări redundante.
Sanitizarea datelor presupune o verificare înainte de stocarea acestora în baza de date și
eliminarea elementelor periculoase. De exempl u, într-un câmp de tip text în care e nevoie de

introducerea unui titlu nu trebuie permisă introducerea de elemente HTML , inclusiv tag-uri
JavaScript, foarte des utilizat e la atacurile de tip XSS.
Validarea datelor presupune o altă verificare a datelor înainte de st ocarea acestora în baza de
date cu scopul de a verifica formatul corect al acestora. De exemplu, într -un câmp de tip text
pentru un număr de telefon nu trebuie să apară alte caractere în afară de caracterul + și cifrele
0-9.
Tot la validare, c ând datele introduse sunt invalide, se poate încerca o corecție a acestora. Sau,
de ce nu , se poate face o conversi e a datelor într-un format mai ușor de prelucrat pentru a evita
acest tip de conversie la fiecare preluare a acestora din baza de date.
Este recomandată u tilizarea simultană a funcțiilor de sanitizare și validare la fiecare procesare
a datelor. Acest lucru este valabil și pentru înformațiile preluate din variabilele superglobale
$_SERVER , $_REQUEST , $_COOKIE , $_SESSION , $_POST , $_GET , etc
Escaparea datelor presupune securizarea datelor existente în baza de date sau preluate în mod
direct din alte surse înainte de afișarea acestora . Acest lucru poate presupune encodarea
anumitor caractere sau chiar eliminarea anumitor porțiuni din datele pre lucrate.

Sanitizarea datelor
Pentru sanitizarea datelor, WordPress ne pune la dispoziție o serie de funcții, dintre care
amintim:
– wp_kses()
– wp_kses_post()
– wp_kses_data()
– wp_kses_allowed_html ()
– wp_filter_post_kses()
– wp_filter_nohtml_kses()
– sanitize_ key()
– sanitize_meta()
– sanitize_option()
– sanitize_file_name()
– sanitize_mime_type()
– sanitize_html_class()
– sanitize_ text_field ()
– sanitize_ email()
– sanitize_title()
– sanitize_title_for_query()
– sanitize_title_with_dashes()
– wp_strip_all_tags()
– etc
Pentru dezvoltarea plugin -ului nostru s -au folosit foarte multe funcții de sanitizare. Chiar dacă
utilizarea Settings API la dezvoltarea panoului de control oferă un nivel acceptabil de
sanitizare automată , pentru majoritatea câmpurilor a fost nevoie de crearea unor funcții de
sanitizare mai complexe .

De exemplu, pentru sanitizarea setărilor de culoare s -a folosit formatul următor:

// Sanitize Design Settings > Colors > Main Color.
if ( isset( $input['design-settings' ]['main-color'] ) ) {
if ( preg_match( '/^#[a-f0-9]{6}$/i' , $input['design-settings' ]['main-color'] ) ) {
$output[ 'design-settings' ]['main-color'] = $input['design-settings' ]['main-color'];
} else {
$output[ 'design-settings' ]['main-color'] = null;
}
} else {
$output[ 'design-settings' ]['main-color'] = $raindrop[ 'design-settings' ]['main-color'];
}

Validarea datelor
Pentru validarea datelor, se pot folosi funcții simple PHP precum is_int() , is_float() ,
zeroized() , etc, sau funcții puse la dispoziție de WordPress, precum intval() , absint() ,
is_email() , etc
În cele mai multe cazuri, însă, pentru validare a datelor e nevoie sa ne cream propriile funcții.
De aceea, pentru dezvoltarea plugin -ului nostru am folosit aproape în exclusivitate funcții
custom.
Un astfel de exemplu e ste funcția de sanitizare și validare a IP-urilor introduse de utilizator.

// Sanitize General Settings > Access Control > Access by IP Address.
if ( isset( $input['general -settings' ]['access-by-ip'] ) ) {
$output[ 'general -settings' ]['access-by-ip'] =
wp_strip_all_tags( $input['general -settings' ]['access-by-ip'] );
$output[ 'general -settings' ]['access-by-ip'] =
strtolower( $output[ 'general -settings' ]['access-by-ip'] );
$output[ 'general -settings' ]['access-by-ip'] =
str_replace( array( "\t", ' ' ), '', $output[ 'general -settings' ]['access-by-ip'] );

// Build the IP whitelist.
$output[ 'general -settings' ]['ip-whitelist' ] =
array_filter( explode( PHP_EOL, $output[ 'general -settings' ]['access-by-ip'] ) );

// Make range easier to read.
$output[ 'general -settings' ]['access-by-ip'] =
str_replace( array( '-' ), ' – ', $output[ 'general -settings' ]['access-by-ip'] );
} else {
$output[ 'general -settings' ]['access-by-ip'] = $raindrop[ 'general -settings' ]['access-by-ip'];
}

După cum se poate vedea în cadrul acestei secțiuni de cod,
– am eliminat toate tag -urile HTML, păstrând doar elementele text
– am transformat toate caracterele rămase în minuscule
– am eliminat toate spațiile și tab -urile
– am transformat valorile rămase într -un array, folosind caracterul invizibil de linie nouă
ca delimitator
– am adăugat câte un spațiu în ainte și după caracterul “ –“ folosit la declararea intervalelor
de IP -uri pentru ușurință la citire

Escaparea datelor
Pentru escaparea datelor, WordPress ne pune la dispoziție o serie de funcții, dintre care
amintim:
– esc_html()
– esc_url()
– esc_js()
– esc_sql()
– esc_attr()
– esc_textarea()
– urlencode()
– urlencode_deep()
– wp_redirect()
– wp_safe_redirect()
– etc
În afară de acestea, pentru escaparea datelor se pot folosi și funcțiile de sanitizare pentru
situațiile în care simpla encodare a datelor nu este de ajuns.
Pentru dezvoltarea plugin -ului nostru s -au folosit foarte multe funcții de sanitizare. Pe scurt,
orice informație preluată din baza de da te a fost prelucrată fie cu o funcție de escapare, fie cu
una de sanitizare înainte de a fi afișată.
Câteva exemple de escapare pot fi observate în următoarea secțiune de cod extrasă din fișierul
responsabil cu interfața paginii de întâmpinare:

<div class="content" >
<h1><?php echo esc_html( $title ); ?></h1>
<p><?php echo esc_html( $description ); ?></p>
</div>

<div class="cover-image">
<?php if ( is_int( $image ) ) { ?>
<?php echo wp_get_attachment_image( $image, 'full' ); ?>
<?php } else { ?>
<img src="<?php echo esc_attr( $image ); ?>"
alt="<?php echo esc_html( $title ); ?>"
width="100%">
<?php } ?>

<div class="clock">
<!– Markup eliminat pentru relevanță –>
</div>
</div>

Notă: Alte exemple de funcții personalizate de validare și sanitizare pot fi văzute în Anexa 2.

Capitolul 5: Interna ționalizarea plugin -ului

Internaționalizarea este procesul prin care un pachet software este pregătit pentru traducerea
în alte limbi. WordPress folosește ca limbă implicită limba engleza însă acest lucru nu ne oprește
să îl folosim în orice altă limbă existent ă. Datorită unui sistem foarte bine pus la punct de
localizare a codului , traducerea interfeței este posibilă prin simpla încărcare a unor fișiere de
limbă.
Același principiu poate fi aplicat și pentru teme sau plugin -uri. De aceea este foarte important
ca la dezvoltarea codului sursă să respectăm cu strictețe pașii detaliați în subcapitolul următor.

5.1. Internațion alizarea codului

Internaționalizarea codului constă în 4 pași simpli , dar foarte importanți:
1. Folosirea unui Text Domain (identificator unic folosit la traducere)
2. Specificarea directorului în care sunt stocate fișierele de traducere
3. Încărcarea fișierelor d e limbă fie din directorul global, fie din cel local
4. Identificarea textelor de tradus prin folosirea unor funcții speciale
Pentru indicarea Text Domain -ului și a directorului cu fișierele de limbă local se folosește File
Header API . Aceste instrucțiuni sunt adăugate sub formă de comentarii în antetul fișierului
principal al plugin -ului. (comentariile Text Domain și Domain Path )

/**
* Plugin Name: Raindrop: Under Construction
* Plugin URI: https://wordpress.org/plugins/raindrop
* Description: Raindrop is a simple plugin for making private websites with modes like
* under construction, under maintenance and private website.
* Version: 1.1.1
* Author: Eusebiu Oprinoiu
* Author URI: https:// eusebiu.me
* Text Domain: raindrop
* Domain Path: /languages/
* License: GNU General Public License version 3.0
*
* @since 1.0.0
* @package Raindrop
*/

Pentru încărcarea fișierelor de limbă se pot utiliza 3 funcții:
– load_textdomain()
– load_theme_textdomain()
– load_plugin_textdomain()
În cazul nostru, folosim funcți a load_textdomain() pentru încărcarea fișierelor de limbă
aflate în directorul de limbă global WordPress și funcția load_plugin_textdomain()
pentru fișierele de limbă aflate în directorul local de lim bă al plugin -ului.

/**
* Define the internationalization functionality.
*
* Load and define the internationalization files making the plugin ready for
* translation.
*
* @since 1.0.0
*/
class Raindrop_i18n {

/**
* Load plugin text-domain.
*
* Load the plugin text-domain for translation from:
*
* – Global languages folder: wp-content/languages/plugins/raindrop/raindrop -en_US.mo
* – Local languages folder: wp-content/plugins/raindrop/languages/raindrop -en_US.mo
*
* If no files are found in the global languages folder the plugin uses the files available
* in the local folder.
*
* @since 1.0.0
*/
public function load_plugin_textdomain() {
$locale = apply_filters( 'locale' , get_locale(), RAINDROP_NAME );

// Load textdomain from the global languages folder.
load_textdomain( RAINDROP_NAME, trailingslashit( WP_LANG_DIR ) . 'plugins/' .
RAINDROP_NAME . '/' . RAINDROP_NAME . '-' . $locale . '.mo' );

// Load textdomain from the local languages folder.
load_plugin_textdomain( RAINDROP_NAME, false, plugin_basename( RAINDROP_DIR_PATH ) .
'/languages/' );
}
}

Fișierele de traducere din directorul local al plugin -ului nu trebuie modificate niciodată. Orice
modificări aduse acestora sunt pierdute în urma update -urilor. Acest director conține doar
fișierele d e limbă suportate oficial de dezvoltatorul plugin -ului.
Pentru modificări ale fișierelor de limbă sau adăugarea de limbi noi, nesuportate oficial se
folosește directorul de limbă global al WordPress . Fișierele adăugate aici nu sunt suprascrise la
actualiz ări.
Foarte important ă este și ordinea în care se încarcă aceste fișiere de limbă. După cum se poate
observa în extrasul de cod de mai sus, primul fișier încărcat este cel din directorul global. Astfel,
dacă utilizatorul a adăugat noi fișiere de limbă, sau a modificat unul deja existent în plugin,
acestea au prioritate.
Dacă nu se găsește un fișier de limbă în directorul global pentru limba utilizatorului se încearcă
încărcarea acestuia din fișierul de traducere local al plugin -ului. Dacă nici aici nu se gă sește un
fișier, se folosește textul original folosit în codul sursă al plugin -ului. În cazul nostru, textul va
apărea în limba engleză.
Notă: Codul sursă al plugin -ului a fost scris în limba engleză pentru a permite o integrare bună cu
core -ul WordPress (scris în totalitate în limba engleză ).

Pentru identificarea textelor de tradus se folosesc o serie de funcții gettext puse la dispoziție
de WordPress. Aceste funcții acceptă de obicei 2 sau mai mulți parametrii, dintre care cei mai
importanți sunt textul de tradus, si identificatorul text-domain .

Funcțiile principale puse la dispoziție de WordPress sunt:
– __()
– _e()
– _x()
– _n()
– _ex()
– _nx()
– _n_noop()
– _nx_noop()
– date_i18n()
– number_format_i18n()
Funcția __() este funcția de bază care doar marchează textul pentru traducere. Funcțiile cu e
în denumire afișează textul , fiind echivalentul unei funcții echo() . Funcțiile cu n în denumire
permit adăugarea de traduceri individuale pentru formatele singular/plural , iar cele cu noop
în denumire înregistrează formatu l plural în fișierul de traducere, fără însă a face și traducerea
acestuia.
Complementar acestor funcții există și un alt set de funcții de traducere care fac în același timp
și escaparea textelor traduse. Acestea au fost adăugate pentru conveniență , pentru simplificarea
instrucțiunilor necesare la scrierea codului sursă . Aceste funcții sunt:
– esc_html__()
– esc_html_e()
– esc_html_x()
– esc_attr__()
– esc_attr_e()
– esc_attr_x()
Pentru dezvoltarea plugin -ului nostru s -au folosit aproape în exclusivitate funcțiil e cu escapare.
Ca un scurt exemplu, în extrasul de cod de mai jos se poate observa utilizarea funcțiil or de
traducere pentru valorile <meta> ale site -ului.

<head>
<!– Alte instrucțiuni eliminate pentru relevanță. –>

<title><?php echo esc_html( $seo_title ); ?></title>
<meta name="description" content= "<?php echo esc_attr( $seo_description ); ?>">
<meta name="robots" content= "<?php echo esc_attr( $seo_robots ); ?>">

<!– Alte instrucțiuni eliminate pentru relevanță. –>
</head>

5.2. Crearea fișierului de traducere pentru limba română

În momentul în care toți pașii prezentați mai sus sunt implementați, se pot genera fișiere le de
limbă. Pentru generarea acestora este nevoie de un program numit Poedit .
Pentru crearea unui astfel de fișier se crează un nou proiect , se alege directorul rădăcină al
plugin -ului ca locație pentru scanat și se definesc funcțiile folosite pentru localizare.

Figura 5.1. Internaționalizarea plugin -ului – Setarea inițială a fișierului de limbă

După finalizarea setărilor inițiale ale proiectului, se apasă pe butonul de scanare al codului. Dacă
totul a fost configurat cum trebuie, programul ar trebui să detecteze toate string -urile de tradus.
Salvarea fișierului de limbă șablo n pe baza căruia se generează toate fișierele de limbă se face
cu denumirea slug -ului (numele directorului principal al plugin -ului) și extensia .pot . În cazul
nostru, această denumire este raindrop.pot .
Pentru generarea fișierelor de limbă propriu -zise se folosește acest fișier șablon în care se
adaugă traducer ea textelor . Salvarea fișierelor se face în formatul slug-locale.po . Pentru
limba română, fișierul nostru are denumirea raindrop -ro_RO.po . La salvarea acestui fișier
se generează în mod automat și un fișier binar raindrop -ro_RO.mo .
Toate cele 3 fișiere se salvează în directorul declarat în antetul plugin -ului. În cazul nostru,
directorul /languages/ .

Figura 5.2. Internaționalizarea plugin -ului – Traducerea textelor

Capitolul 6: Formatarea codului surs ă și documentarea acestuia

Formatarea codului sursă și organizarea acestuia este foarte importantă, indiferent dacă lucrăm
singuri sau în echipă, la proiecte personale sau la proiecte distribuite global. O bună organizare
a codului ajută la citira ușoară a acestuia , facilitează colaborare a între programatori și ajută la
identificarea rapidă a problemelor în procesul de depanare.
WordPress este un pachet software pentru Web și de aceea are standard e de codare pentru
toate tehnologiile folosite în mod u zual în internet :
– Standarde de codare PHP
– Standarde de codare JavaScript
– Standarde de codare HTML
– Standarde de codare CSS
Notă: Respectarea acestor standarde este obligatorie pentru temele și plugin -urile distribuite prin
intermediul WordPress.

6.1. Formatarea codului sursă folosind standardele de codare WordPress

Deoarece lista de reguli pentru fiecare standard este foarte lungă nu voi intra în detalii privind
reguli specifice. În schimb, voi prezenta pe scurt tipurile de reguli acoperite de ace stea și voi
oferi câte un exemplu aleator ce poate fi observant în codul sursă al plugin -ului.

Pentru standardele de codare PHP sunt definite reguli pentru:
– Utilizarea ghilimelelor simple sau duble
– Indentarea , alinierea și spațierea codului
– Poziționarea p arantezelor și a acoladelor
– Închiderea, deschiderea și poziționarea tag -urior PHP
– Denumirea claselor , funcțiilor și a variabilelor
– Denumirea cârligelor WordPress dinamice prin interpolare
– Formatarea query -urilor SQL
– Formatarea comentariilor cu una sau mai multe linii
– Funcții PHP de evitat
– Etc
Exemplu aleator: Convenții de denumire
– Denumirea funcțiilor se face întotdeauna folosind litere mici și separarea cuvintelor din
denumire folosind caracterul underscores
– Denumirea claselor se face întotdeauna folosind cuvinte capitalizate și separarea
cuvintelor din denumire folosind caracte rul underscores
– Denumirea constantelor se face intotdeauna folosind litere mari și separarea cuvintelor
din denumire folosind caracterul underscores
– Denumirea fi șierelor se face întotdeauna folosind litere mici și separarea cuvintelor din
denumire folosind caracterul liniuță

// Denumirea corectă a unei clase.
class Raindrop_Options {
// Denumirea corectă a unei funcții.
public function add_options_page() {
}
}

// Denumirea corectă a unei constante.
define( 'RAINDROP_VERSION' , '1.1.1' );

// Denumirea corectă a unui fișier.
class-raindrop -options.php

Pentru standardele de codare JavaScript sunt definite reguli pentru:
– Utilizarea ghilimelelor (doar cele simple sunt acceptate)
– Lungimea liniilor de cod (recomandată la 100 de caractere)
– Indentarea, alinierea și spațierea codului
– Poziționarea parantezelor și a acoladelor
– Denumirea claselor, funcțiilor și a variabilelor
– Formatarea funcțiilor și obiectelor
– Formatarea pe mai multe linii a instrucțiunilor lungi
– Formatarea comentariilor cu una sau mai multe linii
– Metode preferate de verificare a tipurilor de variabile
– Recomandări pentru folosirea obiectelor, array -urilor, string -urilor
– Etc
Exemplu aleator: Conv enții de spațiere
– Indentarea se face folosind tab -uri
– Nu se lasă spații la sfârșit de linie sau pe linii goale
– Se recomandă ca lungimea liniilor să nu depășească 100 de caractere
– Caracterele ; și , nu trebuie să fie precedate de spații libere
– Instrucțiunil e if, else , for , etc folosesc întotdeauna acolade și se scriu întotdeauna pe
mai multe linii
– Etc

// Exemple de indentare și spațiere între acolade, paranteze, variabile și operanzi.
jQuery( document ).ready( function ( $ ) {
var margin = jQuery( '.content' ).height() – 24; // Remove the negative margins.

jQuery( function () {
jQuery( '.cover-image' ).attr( 'style', 'margin-bottom: ' + margin + 'px;' );
jQuery( '.clock' ).attr( 'style', 'margin-top: -' + ( margin ) + 'px;' );
} );
} );

Pentru standardele de codare HTML sunt definite reguli pentru:
– Indentarea, alinierea și spațierea codului
– Utilizarea tag -urilor și atributelor HTML
– Utilizarea elementelor self -closing
– Etc

Exemplu aleator: Formatarea tag -urilor și atributelor HTML
– Denumirile tag -urilor și atributelor se scriu întotdeauna cu litere mici
– Valorile atributelor se scriu întotdeauna cu litere mici (excepție fac atributele afișate
utili zatorilor umani )

<!– Formatarea corectă a tag-urilor și atributelor HTML –>
<html class="no-js" <?php language_attributes(); ?>>
<head>
<meta charset= "<?php bloginfo( 'charset' ); ?>">
<meta http-equiv="x-ua-compatible" content= "ie=edge" >
<meta name="viewport" content= "width=device -width, initial-scale=1.0" >
</head>

<!– Alte instrucțiuni eliminate pentru relevanță –>
</html>

Pentru standardele de codare CSS sunt definite reguli pentru:
– Inden tarea, alinierea și spațierea codului
– Poziționarea selectorilor ( fiecare pe câte o linie )
– Denumirea selectorilor (denumire propriu -zisă și specificitate )
– Formatarea și ordinea listării proprietăților
– Formatarea și gruparea media -query -urilor
– Utilizarea prefixelor specific e fiecărui browser
– Etc
Exemplu aleator: Utilizarea prefixelor specific e fiecărui browser
– Listarea instrucțiunilor cu prefixe se face de la cel mai lung la cel mai scurt
– Alinierea instrucțiunilor cu prefixe se face la caracteru l :

/* Formatarea corectă a instrucțiunilor cu prefixe */
@keyframes rotate {
100% {
-webkit-transform: rotateZ(360deg); /* Instrucțiune pentru Google Chrome */
-moz-transform: rotateZ(360deg); /* Instrucțiune pentru Mozilla Firefox */
-ms-transform: rotateZ(360deg); /* Instrucțiune pentru Microsoft Edge sau IE */
transform: rotateZ(360deg); /* Instrucțiune generală */
}
}

Notă: Formatarea codului sursă folosind standardele de codare Word Press poate fi observată în
detaliu în oricare din cele 4 anexe atașate acestei lucrări.

6.2. Documentarea codului sursă folosind standard ele DocBlock

Documentarea codului sursă este un alt aspect important în procesul de creare software.
Împreună cu scr ierea de cod auto -descriptiv , documentarea inline a acestuia facilitează o
colaborare ușoară între programatori și permite o mentenanță pe termen lung a proiectului
fără nevoia de refamiliarizare în mod constant cu codul sursă , rând cu rând.

Pentru documentarea codului sursă, WordPress a ales standardele DocBlock. Acestea definesc
felul în care sunt organizate și formatate comentariile .
Pentru proiectele complexe , pe baza comentariilor se pot genera în mod automat site -uri cu
documentație tehnică fol osind programe precum phpDocumentor sau DocBlox. Documentația
oficială a Word Press este generat ă aproape în totalitate pe baza acestui sistem.

La fel ca și în cazul standardelor de codare, nu voi intra în detalii privind reguli specific e de
formatare a co mentariilor . În schimb, voi prezenta pe scurt tipurile de informații utilizate în
aceste comentarii și voi oferi câteva exemple aleatoare din codul sursă al plugin -ului.
În mod usual, comentariile se folosesc pentru adăugarea de informații pentru :
– Clase, f uncții și metode
– Constante și variabile
– Antete de fișiere
– Cârlige WordPress (de tip acțiune sau filtru)
– Etc
Comentariile sunt de touă tipuri:
– Comentarii pe o singură linie
– Comentarii pe mai multe linii

// Acesta este un comentariu pe o singură linie.

/*
* Acesta este un comentariu pe mai multe linii.
* – Informație 01
* – Informație 02
*/

Indiferent de tipul lor, se recomandă ca formulările să fie scurte și la obiect, fără a se face
compromisuri privind calitatea explicațiilor.
Comentariile pe mai multe rânduri conțin de obicei o descriere scurtă , o descriere detaliată și o
serie de informații a diționale indicate cu ajutorul unor cuvinte cheie precum:
– @since – oferă informații despre data adăugării sub formă de versiune software
– @global – oferă informații despre variabilele globale
– @param – oferă informații despre parametrii de intrare ai unei funcții
– @return – oferă informații despre datele returnate de o funcție
– @access – oferă informații privind drepturile de acces
– @package – indică pach etul software din care codul face parte
– @deprecated – indică o funcție sau clasă scoasă din uz s
– @var – oferă informații despre variabilele claselor
– @see – oferă informații adiționale prin trimiterea la alte funcții sau clase
– @link – oferă informații adiționale sub form ă de link -uri externe
– @todo – oferă informații despre elementele propuse pentru implementare
– Etc

Ca un exemplu concret de implementare a acestor principii în codul plugin -ului, iată un extras
din fișierul responsabil gestionarea instrucțiunilor de administrare .
În cadrul acestui extras de cod se pot observa comentarii pentru aproape toate scenariile de
utilizare:
– Comentarii de antet
– Comentarii de clase, funcții și metode
– Comentarii de variabile
Notă: Mai m ulte e xemple de implementare a standardelor DocBlock pot fi văzute în oric are din
cele 4 anexe atașate acestei lucrări.

/**
* The file that contains the class with the admin functionality
*
* @since 1.0.0
* @package Raindrop
*/

/**
* The admin-specific functiona lity of the plugin.
*
* This class is responsable for maintaining all functions with admin functionality.
*
* @since 1.0.0
*/
class Raindrop_Admin {

/**
* Initialize the class and get things started.
*
* @since 1.0.0
*/
public function __construct() {
// Instrucțiuni eliminate pentru relevanță.
}

/**
* Migrate and update options on plugin updates.
*
* Compare the current plugin version with the one stored in the options table
* and migrate recursively if needed after a plugin update. The migration code for each
* version is stored in individual files and it's triggered only if the 'last-updated-version'
* parameter is older than versions where changes have been made.
*
* @since 1.0.0
*/
public function maybe_update() {
// Instrucțiuni eliminate pentru relevanță.
}

/**
* Run activation script for new sites.
*
* If we are running WordPress Multisite and our plugin is network activated,
* run the activation script every time a new site is created.
*
* @since 1.0.0
* @param int $blog_id Blog ID of the created blog. Optional.
* @param int $user_id User ID of the user creating the blog. Required.
* @param string $domain Domain used for the new blog. Optional.
* @param string $path Path to the new blog. Optional.
* @param int $site_id Site ID. Only relevant on multi-network installs. Optional.

* @param array $meta Meta data. Used to set initial site options. Optional.
*/
public function maybe_activate( $blog_id , $user_id , $domain, $path, $site_id , $meta ) {
// Instrucțiuni eliminate pentru relevanță.
}
}

6.3. Verificarea automată a forma tării cu ajutorul unui sniffer de cod

Verificarea manuală a formatării codului este un process anevoios , mai ales pentru
programatorii începători care nu cunosc foarte bine regulile de formatare. De aceea este
indicat ă folosirea unui sniffer de cod care să facă scanarea acestuia automat și în mod constant .
Pentru utilizarea unui astfel de sniffer avem nevoie de un server web, de preferat Apache,
instalat pe mașina local ă. În cazul nostru, vom folosi XAMPP pentru instala rea acestuia.
Pașii de instalare sunt următorii:
1. Instalează XAMPP pe ma șina locală (locatia implicită este C: /XAMPP )
2. Deschide terminalul “Command Prompt ” al sistemului de operare ca Administrator
3. Navighează în directorul C:/XAMPP/php
4. Execută comanda pear install PHP_CodeSniffer
5. Mergi la My Computer > Properties > Advanced System Setting
6. Apasă pe butonul Environment Variables
7. Adaugă calea C:\XAMPP \php în variabila Path
După finalizarea acestor pași avem instala te pe mașina locală mai multe standarde de cod
precum PEAR, PSR -1, PSR -2, Squiz, Zend, etc.
Pentru a folosi standardele de codare WordPress este nevoie să facem adăugarea manual ă a
acestora în directorul C:/XAMPP/php/pear/PHP/CodeSniffer/src/Standards .
Fișierele cu standardele de codare WordPress se p ot descărca d e pe contul oficial de Github al
WordPress.
Ca un ultim pas, în editorul de cod preferat , la secțiunea de sniffer, trebuie adăugată calea
C:\XAMPP \php \phpcs în câmpul pentru executabilul phpcs și indicat standardul dorit pentru
verificări.

Figura 6.1. Formatarea codului – Configurații pentru utilizarea unui sniffer de cod

Acum, cu toate configurațiile puse la punct, sniffer -ul de cod rulează în fundal, avertizându -ne
de fiecare data când avem erori de formatare și în unele cazuri, chiar imp lementări greșite ce
pot expune utilizatorul final la atacuri de tip XSS sau CSRF .
În imaginea de mai jos se poate observa o astfel de eroare care ne anunță că la sfârșitul unei linii
nu se acceptă spații .

Figura 6.2. Formatarea codului – Eroare de for matare detectată automat

Concluzii

[se va completa]

Provocări la dezvoltarea plugin -ului și soluțiile găsite

– Dezvoltarea algoritmului de calcul cu IP -uri
– Organizarea codului pentru pagina cu setări a plugin -ului
– Crearea ceasului în frontend
– Modelul de update -uri în baza de date de la o versiune mai veche la una mai nouă

Opinia personal ă despre procesul de dezvoltare al plugin -ului

Posibile î mbunătățiri viitoare

– Autentificarea prin oAuth2 a contului de Google Analytics
– Adăugarea de noi template -uri de design pentru pagina de întâmpinare și a unei secțiuni
dedicate în panoul de control de unde se poate selecta template -ul favorit
– Adăugarea de fișiere de traducere pentru alte limbi precum german ă, franceză, spaniolă,
italiană, rusă, etc

Bibliografie

[se va complete la final]

Anexa 1: Determinarea acces ului în funcție de rol și adresă IP

Notă: Codul prezentat în cadrul acestei anexe este un fragment . În codul sursă al plugin -ului,
clase le prezentate mai jos conțin mai multe funcții . Pentru relevan ță s-au păstrat doar secțiunile
ce țin de determinarea accesului și a modului în care plugin -ul preia controlul asupra interfeței
WordPress când un utilizator nu are dreptul de acces .

Extras din fișierul /includes/general/class -raindrop -public.php
În cadr ul acestei clase se face verificarea propriu -zisă a accesului și se preia controlul asupra
interfeței WordPress dacă utilizatorul nu are dreptul de acces .

/**
* The public-facing functionality of the plugin.
*
* This class is responsable for maintaining all functions with public-facing functionality.
*
* @since 1.0.0
*/
class Raindrop_Public {

/**
* Overwrite WordPress.
*
* If the visitor is not allowed to view the website display a landing page.
*
* @since 1.0.0
*/
public function overwrite_wp() {
// Global variables.
global $pagenow;

// Variables.
$raindrop = get_option( 'raindrop' );
$status = $raindrop[ 'general -settings' ]['status' ];
$mode = $raindrop[ 'general -settings' ]['mode'];
$custom_mode_html = $raindrop[ 'layout-settings' ]['custom-mode-html'];

if ( $status == 'enabled' ) {
if ( ! $this->has_access() && $pagenow != 'wp-login.php' ) {
$this->http_headers();

if ( $mode == 'custom-mode' ) {
if ( $custom_mode_html ) {
echo Raindrop_Helper::decode_escaped_h tml( $custom_mode_html );
} else {
require( RAINDROP_DIR_PATH .
'includes/general/partials/custom -html-default.php' );
}
} else {
require( RAINDROP_DIR_PATH . 'includes/general/partials/overwrite -wp.php' );
}

die();
}
}
}

/**
* Visitor has access.
*
* Check if the visitor is allowed to access the website based on its role or IP.
*
* @since 1.0.0
* @access protected
* @return bool Whether the visitor is allowed to access the website.
*/
protected function has_access( ) {
$raindrop = get_option( 'raindrop' );
$allowed_roles = $raindrop[ 'general -settings' ]['access-by-role'];
$ip = new Raindrop_IP();
$ip_access = $ip->has_access();

if ( is_user_logged_in() ) {
global $current_user;

$user_roles = $current_user ->roles;
$role_access = false;

foreach ( $user_roles as $user_role ) {
if ( in_array( $user_role, $allowed_roles ) ) {
$role_access = true;
}
}

if ( ! $role_access ) {
wp_logout();
wp_redirect( home_url() );
exit;
}
} else {
$role_access = false;
}

return ( $ip_access || $role_access );
}

// Alte funcții și variabile interne eliminate pentru relevanță în cadrul acestei anexe.
}

Codul prezentat până acum se rulează definind în clasa principală a plugin -ului ca momente de
execuție următoarele cârlige de tip acțiune pentru funcția overwrite_wp ():
– template_redirect – pentru rularea codului în frontend
– admin_init – pentru rularea codului în panoul de control
Execuția corectă a acestuia e condiționată de existența clasei Raindrop_IP prezentată pe
pagina următoare .

class Raindrop {
private function define_hooks() {
$public = new Raindrop_Public();

$this->loader->add_action( 'template_redirect' , $public, 'overwrite_wp' );
$this->loader->add_action( 'admin_init' , $public, 'overwrite_wp' );
}
}

Extras din fișierul /includes/general/ class-raindrop -ip.php
Cu ajutorul acestei clase se determină IP-ului utilizatorului și se verifică dacă acesta
îndeplinește cel puțin o regulă de acces pe bază de IP definit ă de administrator.
Tot aici se fac conversiile de IP-uri și calculele necesare pentru verificarea regulilor IP definite
în panoul de control.

/**
* Class with IP functions.
*
* Defines all IP functions required for building, checking and enforcing the IP rules.
*
* @since 1.0.0
*/
class Raindrop_IP {

/**
* IP has access.
*
* Check if the visitor's IP can get unrestricted access.
*
* @since 1.0.0
* @return bool Whether the IP is allowed to access the website.
*/
public function has_access() {
// Variables.
$raindrop = get_option( 'raindrop' );
$whitelist = $raindrop[ 'general -settings' ]['ip-whitelist' ];
$ip = $this->get_ip();
$access = false;

// Detect IP type.
if ( strpos( $ip, ':' ) !== false ) {
$ip_type = 'IPv6';
} else {
$ip_type = 'IPv4';
}

// Check if IP is in range.
if ( $ip_type == 'IPv6' ) {
foreach ( $whitelist as $rule => $range ) {
if ( strpos( $range, ':' ) !== false ) {
if ( $this->ipv6_in_range( $ip, $range ) ) {
$access = true;
}
}
}
} else {
foreach ( $whitelist as $rule => $range ) {
if ( strpos( $range, '.' ) !== false ) {
if ( $this->ipv4_in_range( $ip, $range ) ) {
$access = true;
}
}
}
}

return $access;
}

/**
* Get visitor's IP.
*
* Get the IP address of the visitor's machine. If it's not available, use as fallback
* options HTTP_X_FORWARDED_FOR and REMOTE_ADDR.
*
* @since 1.0.0
* @access protected
* @return string Visitor's IP.
*/
protected function get_ip() {
if ( ! empty( $_SERVER[ 'HTTP_CLIENT_IP' ] ) ) {
$ip = $_SERVER[ 'HTTP_CLIENT_IP' ];
} elseif ( ! empty( $_SERVER[ 'HTTP_X_FORWARDED_FOR' ] ) ) {
$ip = $_SERVER[ 'HTTP_X_FORWARDED_FOR' ];
} else {
$ip = $_SERVER[ 'REMOTE_ADDR' ];
}

return $ip;
}

/**
* Check if IPv4 address is in range.
*
* This function takes 2 arguments: an IPv4 address and a range in several
* different formats. Network ranges can be specified as:
* – Singular IPs: 1.2.3.4
* – Wildcard IPs: 1.2.3.*
* – CIDR Subnets: 1.2.3/24
* – IP Ranges: 1.2.3.10 -1.2.3.20
* The function will return true if the supplied IP is within the range.
* Note little validation is done on the range inputs. The values are expected
* în one of the formats above.
*
* @since 1.0.0
* @access protected
* @param string $ip IP to check.
* @param string $range IP range to check against.
* @return bool Whether the IP is in range or not.
*/
protected function ipv4_in_range( $ip, $range ) {
// Return false for IPv6.
if ( ( strpos( $ip, ':' ) !== false ) || ( strpos( $range, ':' ) !== false ) ) {
return false;
}

// Range is a singular IP.
if ( ( strpos( $range, '/' ) === false ) && ( strpos( $range, '*' ) === false ) &&
( strpos( $range, '-' ) === false ) ) {
$range .= '/32';
}

if ( strpos( $range, '/' ) !== false ) {
// Range format is IP/Netmask.
// Get the subnet address and the subnet mask.
list( $range, $netmask ) = explode( '/', $range, 2 );

// Add missing range pieces.
$pieces = explode( '.', $range );
while ( count( $pieces ) < 4 ) {
$pieces[] = '0';
}
list( $a, $b, $c, $d ) = $pieces;
$range = sprintf(
'%u.%u.%u.%u' ,
empty( $a ) ? '0' : $a,
empty( $b ) ? '0' : $b,
empty( $c ) ? '0' : $c,
empty( $d ) ? '0' : $d
);

// Convert readable IPs into strings of bits.
$ip_bits = $this->ip_to_bits( $ip );
$range_bits = $this->ip_to_bits( $range );

// Get the network bits of the given IP address and the subnet address.
$ip_net_bits = substr( $ip_bits, 0, $netmask );
$range_net_bits = substr( $range_bits, 0, $netmask );

// If the network bits are identical, then the IP is in range.
return ( $ip_net_bits == $range_net_bits );
} else {
// Range format is a.b.*.*
if ( strpos( $range, '*' ) !== false ) {
// Convert to A-B format.
$lower_ip = str_replace( '*', '0', $range );
$upper_ip = str_replace( '*', '255', $range );
$range = $lower_ip . '-' . $upper_ip;
}

// Range format is in A-B.
if ( strpos( $range, '-' ) !== false ) {
list( $lower_ip, $upper_ip ) = explode( '-', $range, 2 );

$lower_ip_dec = (float) sprintf( '%u', ip2long( $lower_ip ) );
$upper_ip_dec = (float) sprintf( '%u', ip2long( $upper_ip ) );
$ip_dec = (float) sprintf( '%u', ip2long( $ip ) );

return ( ( $ip_dec >= $lower_ip_dec ) && ( $ip_dec <= $upper_ip_dec ) );
}

// Return false for invalid entries.
return false;
}
}

/**
* Check if IPv6 address is in range.
*
* This function takes 2 arguments: an IPv6 address and a range in several
* different formats. Network ranges can be specified as:
* – Singular IPs: 2001:db8::1: 2
* – Wildcard IPs: 2001:db8::1:*
* – CIDR Subnets: 2001:db8::/120
* – IP Ranges: 2001:db8::1:10 -2001:db8::1:20
* The function will return true if the supplied IP is within the range.
* Note little validation is done on the range inputs. The values are expected
* în one of the formats above.
*
* @since 1.0.0
* @access protected
* @param string $ip IP to check.
* @param string $range IP range to check against.
* @return bool Whether the IP is in range or not.
*/
protected function ipv6_in_range( $ip, $range ) {
// Return false for IPv4.
if ( ( strpos( $ip, '.' ) !== false ) || ( strpos( $range, '.' ) !== false ) ) {
return false;
}

// Range is a singular IP.
if ( ( strpos( $range, '/' ) === false ) && ( strpos( $range, '*' ) === false ) &&
( strpos( $range, '-' ) === false ) ) {
$range .= '/128';
}

if ( strpos( $range, '/' ) !== false ) {
// Range format is IP/Netmask .
// Expand shorthand IP.
$ip = $this->get_ipv6_full( $ip );
$range = $this->get_ipv6_full( $range );

// Get the subnet address and the subnet mask.
list( $range, $netmask ) = explode( '/', $range, 2 );

// Convert the readable IPs into strings of bits.
$ip_bits = $this->ip_to_bits( $ip );
$range_bit s = $this->ip_to_bits( $range );

// Get the network bits of the given IP address and the subnet address.
$ip_net_bits = substr( $ip_bits, 0, $netmask );
$range_net_bits = substr( $range_bits, 0, $netmask );

// If the network bits are identical, then the IP is in range.
return ( $ip_net_bits == $range_net_bits );
} else {
// Range format is a:b::*:*
if ( strpos( $range, '*' ) !== false ) {
// Convert to A-B format.
$lower_ip = str_replace( '*', '0', $range );
$upper_ip = str_replace( '*', 'ffff', $range );
$lower_ip = $this->get_ipv6_full( $lower_ip );
$upper_ip = $this->get_ipv6_full( $upper_ip );
$range = $lower_ip . '-' . $upper_ip;
}

// Range format is A-B.
if ( strpos( $range, '-' ) !== false ) {
list( $lower_ip, $upper_ip ) = explode( '-', $range, 2 );

$lower_ip = $this->get_ipv6_full( $lower_ip );
$upper_ip = $this->get_ipv6_full( $upper_ip );
$lower_ip_bits = $this->ip_to_bits( $lower_ip );
$upper_ip_bits = $this->ip_to_bits( $upper_ip );
$ip_bits = $this->ip_to_bits( $ip );

return ( ( $ip_bits >= $lower_ip_bits ) && ( $ip_bits <= $upper_ip_bits ) );
}

// Return false for invalid entries.
return false;
}
}

/**
* Convert IPv6 from short to long format.
*
* Expand the short notation of IPv6 to the full format and return it.
*
* @since 1.0.0
* @access protected
* @param string $ip IP to convert.
* @return string Converted IP.
*/
protected function get_ipv6_full( $ip ) {
// Remove netmask if necessary.
if ( strpos( $ip, '/' ) !== false ) {
$blocks = explode( '/', $ip, 2 );
$ip = $blocks[0];
$netmask = $blocks[1];
} else {
$ip = $ip;
$netmask = null;
}

if ( strpos( $ip, '::' ) === false ) {
// We already have a full IP.
if ( strlen( $ip ) == 39 ) {
if ( $netmask ) {
$full_ip = $ip . '/' . $netmask;
} else {
$full_ip = $ip;
}
} else {

// We have a long IP with missing zeros.
// Extract the pieces.
$pieces = explode( ':', $ip );

// Rebuild the pieces.
$count = 0;
foreach ( $pieces as $key => $value ) {
$pieces[ $key ] = str_pad( $pieces[ $key ], 4, '0', STR_PAD_LEFT );
$count++;
}

// Rebuild the IP using the pieces.
$full_ip = implode( ':', $pieces );

if ( $netmask ) {
$full_ip = $full_ip . '/' . $netmask;
}
}
} else {
// We have a short IP.
// Extract the sections.
$sections = explode( '::', $ip, 2 );
$section_before = $sections[0];
$section_after = $sections[1];

// Extract the pieces.
$pieces_before = explode( ':', $section_before );
$pieces_after = explode( ':', $section_after );

// Rebuild the pieces.
$count_before = 0;
foreach ( $pieces_before as $key => $value ) {
$pieces_before[ $key ] = str_pad( $pieces_before[ $key ], 4, '0', STR_PAD_LEFT );
$count_before++;
}

$count_after = 0;
foreach ( $pieces_after as $key => $value ) {
$pieces_after[ $key ] = str_pad( $pieces_after[ $key ], 4, '0', STR_PAD_LEFT );
$count_after++;
}

// Rebuild the IP using the pieces.
for ( $i = 0; $i < 8; $i++ ) {
$ip_pieces[ $i ] = '0000';
}

for ( $i = 0; $i < $count_before; $i++ ) {
$ip_pieces[ $i ] = $pieces_before[ $i ];
}

if ( trim( $section_after ) ) {
$count = 0;
for ( $i = 8 – $count_after; $i < 8; $i++ ) {
$ip_pieces[ $i ] = $pieces_after[ $count ];
$count++;
}
}

$full_ip = implode( ':', $ip_pieces );

if ( $netmask ) {
$full_ip = $full_ip . '/' . $netmask;
}
}

// Return the full IP.
return $full_ip;
}

/**
* Convert IP to string of bits.
*
* Takes an IP address ( IPv4 or IPv6 ) and converts it into a long string of bits
* of ones and zeros.
*
* @since 1.0.0
* @access protected
* @param string $ip IP to convert.
* @return string Converted IP.
*/
protected function ip_to_bits( $ip ) {
// Convert IP to a binary string.
$binary_ip = inet_pton( $ip );

// Unpack from a binary string into an array.
$unpacked = unpack( 'a*', $binary_ip );
$unpacked = str_split( $unpacked[1] );

// Build the string of bits.
$ip_bits = '';
foreach ( $unpacked as $byte ) {
$ip_bits .= str_pad( decbin( ord( $byte ) ), 8, '0', STR_PAD_LEFT );
}

// Return converted IP.
return $ip_bits;
}

// Alte funcții și variabile interne eliminate pentru relevanță în cadrul acestei anexe.
}

Anexa 2: Panoul de control și sanitizarea op țiunilor

Notă: Codul prezentat în cadrul acestei anexe este un fragment. În codul sursă al plugin -ului,
funcții le prezentate mai jos conțin mai multe instrucțiuni . Pentru relevanță și ușurință la
parcurgere s-au păstrat doar secțiunile ce țin de crearea și sanitizarea o pțiunilor din subtab-
urile “Panou de control” și “Controlul accesului” .
Funcția de sanitizare conține mai multe exemple, nu doar cele pentru subtab -urile “Panou de
control” și “Controlul accesului”. (exemple adiționale: sanitizare pentru setări culoare, setări
font, imagine, text )
Extras din fișierul /includes/general/class -raindrop -options.php
Cu ajutorul acestei clase este creată pagina de setări a plugin -ului și se face sanitizarea
opțiunilor înainte de salvarea acestora în baza de date.

/**
* Create the options page.
*
* This class is responsable for maintaining all the functions needed to create
* the options page.
*
* @since 1.0.0
*/
class Raindrop_Options {

/**
* Create the options page.
*
* Create an option page under the Settings menu from where we will be able
* to control how our plugin behaves.
*
* @since 1.0.0
*/
public function add_options_page() {
$raindrop = get_option( 'raindrop' );
switch ( $raindrop[ 'general -settings' ]['mode'] ) {
case 'under-construction' :
$menu_title = esc_html__( 'Under Construction' , 'raindrop' );
break;
case 'under-maintenance' :
$menu_title = esc_html__( 'Under Maintenance' , 'raindrop' );
break;
case 'private -website' :
$menu_title = esc_html__( 'Private Website' , 'raindrop' );
break;
default:
$menu_title = esc_html__( 'Raindrop' , 'raindrop' );
}

add_submenu_page(
'options -general.php' , // Parent menu slug.
esc_html__( 'Raindrop' , 'raindrop' ), // Page title.
$menu_title, // Menu title.
'manage_options' , // Capability.
'raindrop' , // Options page slug.
array( $this, 'options_page_content' ) // Callback function to output the content.
);
}

/**
* Display content for the options page.
*
* Render a simple page to display for the options menu defined above.
*
* @since 1.0.0
*/
public function options_page_content() {
// Variables.
$active_tab = $this->get_active_tab();
$active_section = $this->get_active_section();
?>

<div class="wrap">
<h1><?php echo esc_html__( 'Raindrop' , 'raindrop' ); ?></h1>

<?php $this->tabs_and_sections(); ?>

<form method="post" action="options.php" >
<input type="hidden" name="raindrop[active -tab]"
value="<?php echo esc_attr( $active_tab ); ?>">
<input type="hidden" name="raindrop[active -section]"
value="<?php echo esc_attr( $active_section ); ?>">

<?php
settings_fields( 'raindrop' ); // Options group.
do_settings_sections( 'raindrop' ); // Options page slug.
submit_button(); // Submit button.
?>
</form>
</div>

<?php
}

/**
* Initialize the options page.
*
* Initialize the options page and add sections and fields on it.
*
* @since 1.0.0
*/
public function options_page_init() {
// Variables.
$active_tab = $this->get_active_tab();
$active_section = $this->get_active_section();

// Register settings.
register_setting(
'raindrop' , // Options group.
'raindrop' , // Options name.
array( $this, 'sanitize_options' ) // Sanitization function.
);

// General Settings tab.
if ( $active_tab == 'general -settings' ) {
// Dashboard section.
if ( $active_section == 'dashboard' ) {
add_settings_section(
'raindrop -dashboard -section' , // Section ID.
esc_html__( 'Dashboard' , 'raindrop' ), // Section title.
null, // Callback function.
'raindrop' // Options page slug.
);

add_settings_field(
'raindrop -status', // Setting ID.
esc_html__( 'Status' , 'raindrop' ), // Setting title.
array( $this, 'display_options' ), // Callback function.

'raindrop' , // Options page slug.
'raindrop -dashboard -section' , // Section ID.
array( 'id' => 'raindrop -status' ) // Extra arguments for the callback
function.
);

add_settings_field(
'raindrop -mode', // Setting ID.
esc_html__( 'Mode', 'raindrop' ), // Setting title.
array( $this, 'display_options' ), // Callback function.
'raindrop' , // Options page slug.
'raindrop -dashboard -section' , // Section ID.
array( 'id' => 'raindrop -mode' ) // Extra arguments for the callback
function.
);
}

// Access Control section.
if ( $active_section == 'access-control' ) {
add_settings_section(
'raindrop -access-control-section' , // Section ID.
esc_html__( 'Access Control' , 'raindrop' ), // Section title.
null, // Callback function .
'raindrop' // Options page slug.
);

add_settings_field(
'raindrop -access-by-role', // Setting ID.
esc_html__( 'Access by User Role', 'raindrop' ), // Setting title.
array( $this, 'display_options' ), // Callback function.
'raindrop' , // Options page slug.
'raindrop -access-control-section' , // Section ID.
array( 'id' => 'raindrop -access-by-role' ) // Extra arguments for the
callback function.
);

add_settings_field(
'raindrop -access-by-ip', // Setting ID.
esc_html__( 'Access by IP Address' , 'raindrop' ), // Setting title.
array( $this, 'display_options' ), // Callback function.
'raindrop' , // Options page slug.
'raindrop -access-control-section' , // Section ID.
array( 'id' => 'raindrop -access-by-ip' ) // Extra arguments for the
callback function .
);
}

// Alte instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.
}

// Design Settings tab.
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.

// Layout Settings tab.
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.
}

/**
* Display options.
*
* Cicle through all options and display the HTML markup for the current one.
*
* @since 1.0.0
* @param array $args Array with additional arguments.
*/
public function display_options( $args ) {
// Variables.
$raindrop = get_option( 'raindrop' );
$id = $args['id'];

switch ( $id ) {
// General Settings tab.
case 'raindrop -status':
?>
<label class="raindrop -label">
<input type="radio" value="enabled"
name="raindrop[general -settings][status]"
<?php checked( $raindrop[ 'general -settings' ]['status' ],
'enabled' ); ?> />
<?php echo esc_html__( 'Enabled' , 'raindrop' ); ?>
</label><br>

<label class="raindrop -label">
<input type="radio" value="disabled"
name="raindrop[general -settings][status]"
<?php checked( $raindrop[ 'general -settings' ]['status' ],
'disabled' ); ?> />
<?php echo esc_html__( 'Disabled' , 'raindrop' ); ?>
</label><br>

<p class="description" >
<?php echo esc_html__( 'Enable this option to prevent public access to your
website.' , 'raindrop' ); ?>
</p>
<?php
break;

case 'raindrop -mode':
?>
<label class="raindrop -label">
<input type="radio" value="under-construction"
name="raindrop[general -settings][mode]"
<?php checked( $raindrop[ 'general -settings' ]['mode'],
'under-construction' ); ?> />
<?php echo esc_html__( 'Under Construction' , 'raindrop' ); ?>
</label><br>

<label class="raindrop -label">
<input type="radio" value="under-maintenance"
name="raindrop[general -settings][mode]"
<?php checked( $raindrop[ 'general -settings' ]['mode'],
'under-maintenance' ); ?> />
<?php echo esc_html__( 'Under Maintenance' , 'raindrop' ); ?>
</label><br>

<label class="raindrop -label">
<input type="radio" value="private -website"
name="raindrop[general -settings][mode]"
<?php checked( $raindrop[ 'general -settings' ]['mode'],
'private -website' ); ?> />
<?php echo esc_html__( 'Private Website' , 'raindrop' ); ?>
</label><br>

<label class="raindrop -label">
<input type="radio" value="custom-mode"
name="raindrop[general -settings][mode]"
<?php checked( $raindrop[ 'general -settings' ]['mode'],
'custom-mode' ); ?> />
<?php echo esc_html__( 'Custom Mode', 'raindrop' ); ?>
</label><br>

<p class="description" >
<?php echo esc_html__( 'What mode should we use when preventing access to
your website?' , 'raindrop' ); ?>
</p>
<?php
break;

case 'raindrop -access-by-role':
$access_roles = isset( $raindrop[ 'general -settings' ]['access-by-role'] ) ?
$raindrop[ 'general -settings' ]['access-by-role'] : array();
$editable_roles = get_editable_roles();

?>
<?php foreach ( $editable_roles as $role => $details ) { ?>
<?php if ( ! in_array( $role, array( 'wpseo_editor' ,
'wpseo_manager' ) ) ) { ?>
<label class="raindrop -label">
<input type="checkbox" value="<?php echo esc_attr( $role ); ?>"
name="raindrop[general -settings][access -by-role][<?php
echo esc_attr( $role ); ?>]"
<?php checked( in_array( $role, $access_roles ) ); ?> />
<?php echo esc_html( $details[ 'name'] ); ?>
</label><br>
<?php } ?>
<?php } ?>

<p class="description" >
<?php echo esc_html__( 'What user roles should have unrestricted access to
your website?' , 'raindrop' ); ?>
</p>
<?php
break;

case 'raindrop -access-by-ip':
?>
<textarea class="raindrop -textarea"
name="raindrop[general -settings][access -by-ip]">
<?php echo esc_html( $raindrop[ 'general -settings' ]['access-by-ip'] ); ?>
</textarea>

<p class="description" >
<?php echo esc_html__( 'What IPs should have unrestricted access to your
website? Add one rule per line.', 'raindrop' ); ?>
</p>

<p class="description" >
<?php echo esc_html__( 'Note: Little validation is done on your input.
It expects you to write valid IP rules.',
'raindrop' ); ?>
</p>

<table class="raindrop -ip-rules">
<tr>
<th><?php echo esc_html__( 'Rule Type', 'raindrop' ); ?></th>
<th><?php echo esc_html__( 'IPv4 Example' , 'raindrop' ); ?></th>
<th><?php echo esc_html__( 'IPv6 Example' , 'raindrop' ); ?></th>
</tr>
<tr>
<td><?php echo esc_html__( 'Singular IP', 'raindrop' ); ?></td>
<td>1.2.3.4</td>
<td>2001:db8::1:2</td>
</tr>
<tr>
<td><?php echo esc_html__( 'Wildcard IP', 'raindrop' ); ?></td>
<td>1.2.3.*</td>
<td>2001:db8::1:*</td>
</tr>
<tr>
<td><?php echo esc_html__( 'CIDR Subnet', 'raindrop' ); ?></td>
<td>1.2.3/24</td>
<td>2001:db8::/120</td>
</tr>
<tr>
<td><?php echo esc_html__( 'Range of IPs', 'raindrop' ); ?></td>
<td>1.2.3.100 – 1.2.3.200</td>
<td>2001:db8::1:100 – 2001:db8::1:200</td>
</tr>
</table>

<?php
break;

// Alte instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.

// Design Settings tab.
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.

// Layout Settings tab.
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.

// Default value for debugging.
default:
echo esc_html__( 'Wrong option ID.', 'raindrop' );
break;
}
}

/**
* Sanitize options.
*
* Validate and sanitize options upon saving.
* Rebuild missing keys when options are stored as an array. WordPress removes empty
* keys so we need to recreate them in order to avoid any possible issues that might appear.
*
* @since 1.0.0
* @param array $input Array with all options.
* @return array $output Array with the new options.
*/
public function sanitize_options( $input ) {
// Variables.
$active_tab = $input['active-tab'];
$active_sec tion = $input['active-section' ];
$raindrop = get_option( 'raindrop' );
$output = get_option( 'raindrop' );

// Sanitize General Settings > Dashboard > Status.
if ( isset( $input['general -settings' ]['status' ] ) ) {
if ( in_array( $output[ 'general -settings' ]['status' ],
array( 'enabled' , 'disabled' ) ) ) {
$output[ 'general -settings' ]['status' ] = $input['general -settings' ]['status' ];
} else {
$output[ 'general -settings' ]['status' ] = 'disabled' ;
}
} else {
$output[ 'general -settings' ]['status' ] = $raindrop[ 'general -settings' ]['status' ];
}

// Sanitize General Settings > Dashboard > Mode.
if ( isset( $input['general -settings' ]['mode'] ) ) {
if ( in_array( $output[ 'general -settings' ]['mode'],
array( 'under-construction' , 'under-maintenance' , 'private -website' ,
'custom-mode' ) ) ) {
$output[ 'general -settings' ]['mode'] = $input['general -settings' ]['mode'];
} else {
$output[ 'general -settings' ]['mode'] = 'under-construction' ;
}
} else {
$output[ 'general -settings' ]['mode'] = $raindrop[ 'general -settings' ]['mode'];
}

// Sanitize General Settings > Access Control > Access by User Role.
if ( isset( $input['general -settings' ]['access-by-role'] ) ) {
$choices = $input['general -settings' ]['access-by-role'];
foreach ( $choices as $key => $value ) {
$choices[ $key ] = wp_strip_all_tags( $choices[ $value ] );
}
$output[ 'general -settings' ]['access-by-role'] = $choices;
$output[ 'general -settings' ]['access-by-role']['administrator' ] = 'administrator' ;
} else {
if ( ( $active_tab == 'general -settings' ) &&
( $active_section == 'access-control' ) ) {
$output[ 'general -settings' ]['access-by-role'] = array();

$output[ 'general -settings' ]['access-by-role']['administrator' ] = 'administrator' ;
} else {
$output[ 'general -settings' ]['access-by-role'] =
$raindrop[ 'general -settings' ]['access-by-role'];
}
}

// Sanitize General Settings > Access Control > Access by IP Address.
if ( isset( $input['general -settings' ]['access-by-ip'] ) ) {
$output[ 'general -settings' ]['access-by-ip'] = wp_strip_all_tags(
$input['general -settings' ]['access-by-ip'] );
$output[ 'general -settings' ]['access-by-ip'] = strtolower(
$output[ 'general -settings' ]['access-by-ip'] );
$output[ 'general -settings' ]['access-by-ip'] = str_replace(
array( "\t", ' ' ), '', $output[ 'general -settings' ]['access-by-ip'] );

// Build the IP whitelist.
$output[ 'general -settings' ]['ip-whitelist' ] = array_filter(
explode( PHP_EOL, $output[ 'general -settings' ]['access-by-ip'] ) );

// Make range easier to read.
$output[ 'general -settings' ]['access-by-ip'] = str_replace(
array( '-' ), ' – ', $output[ 'general -settings' ]['access-by-ip'] );
} else {
$output[ 'general -settings' ]['access-by-ip'] =
$raindrop[ 'general -settings' ]['access-by-ip'];
}

/**
* Deasupra acestui comentariu sunt prezentate instrucțiunile de sanitizare
* pentru subtab-urile "Panou de control" și "Controlul accesului".
*
* Sub acest comentariu s-a ales câte un exemplu de:
* – Sanitizare setari culoare
* – Sanitizare setari font
* – Sanitizare imagine
* – Sanitizare text
*/

// Sanitize Design Settings > Colors > Main Color.
if ( isset( $input['design-settings' ]['main-color'] ) ) {
if ( preg_match( '/^#[a-f0-9]{6}$/i' , $input['design-settings' ]['main-color'] ) ) {
$output[ 'design-settings' ]['main-color'] = $input['design-settings' ]['main-color'];
} else {
$output[ 'design-settings' ]['main-color'] = null;
}
} else {
$output[ 'design-settings' ]['main-color'] = $raindrop[ 'design-settings' ]['main-color'];
}

// Sanitize Design Settings > Typography > Title Font.
if ( isset( $input['design-settings' ]['title-font'] ) ) {
$output[ 'design-settings' ]['title-font'] = wp_strip_all_tags(
$input['design-settings' ]['title-font'] );
} else {
$output[ 'design-settings' ]['title-font'] = $raindrop[ 'design-settings' ]['title-font'];
}

if ( isset( $input['design-settings' ]['title-font-weight'] ) ) {
if ( in_array( $output[ 'design-settings' ]['title-font-weight'],
array( '400', '700' ) ) ) {
$output[ 'design-settings' ]['title-font-weight'] =
$input['design-settings' ]['title-font-weight'];
} else {
$output[ 'design-settings' ]['title-font-weight'] = '400';
}
} else {

$output[ 'design-settings' ]['title-font-weight'] =
$raindrop[ 'design-settings' ]['title-font-weight'];
}

if ( isset( $input['design-settings' ]['title-text-transform' ] ) ) {
if ( in_array( $output[ 'design-settings' ]['title-text-transform' ],
array( 'none', 'lowercase' , 'uppercase' ) ) ) {
$output[ 'design-settings' ]['title-text-transform' ] =
$input['design-settings' ]['title-text-transform' ];
} else {
$output[ 'design-settings' ]['title-text-transform' ] = 'none';
}
} else {
$output[ 'design-settings' ]['title-text-transform' ] =
$raindrop[ 'design-settings' ]['title-text-transform' ];
}

// Sanitize Layout Settings > Under Construction > Title.
if ( isset( $input['layout-settings' ]['under-construction -title'] ) ) {
$output[ 'layout-settings' ]['under-construction -title'] = wp_strip_all_tags(
$input['layout-settings' ]['under-construction -title'] );
} else {
$output[ 'layout-settings' ]['under-construction -title'] =
$raindrop[ 'layout-settings' ]['under-construction -title'];
}

// Sanitize Layout Settings > Under Construction > Cover Image.
if ( isset( $input['layout-settings' ]['under-construction -image'] ) ) {
if ( ( intval( $input['layout-settings' ]['under-construction -image'] ) != 0 ) &&
( wp_get_attachment_url( $input['layout-settings' ]['under-construction –
image'] ) ) ) {
$output[ 'layout-settings' ]['under-construction -image'] =
intval( $input['layout-settings' ]['under-construction -image'] );
} else {
$output[ 'layout-settings' ]['under-construction -image'] = null;
}
} else {
$output[ 'layout-settings' ]['under-construction -image'] =
$raindrop[ 'layout-settings' ]['under-construction -image'];
}

// Alte instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.

// Return.
return $output;
}

/**
* Get active tab.
*
* Get the active tab and sanitize it before returning its value.
*
* @since 1.0.0
* @access protected
* @return string $active_tab Active tab.
*/
protected function get_active_tab() {
// Get active tab.
if ( isset( $_GET['tab'] ) ) {
$active_tab = sanitize_title_with_dashes( $_GET['tab'] );
} else {
$active_tab = 'general -settings' ;
}

// Return the active tab.
return $active_tab;
}

/**
* Get active section.
*
* Get the active section for the current tab and sanitize it before
* returning its value.
*
* @since 1.0.0
* @access protected
* @return string $active_section Active section for the current tab.
*/
protected function get_active_section() {
// Get active tab.
$active_tab = $this->get_active_tab();

// Get active section.
if ( $active_tab == 'general -settings' ) {
if ( isset( $_GET['section' ] ) ) {
$active_section = sanitize_title_with_dashes( $_GET['section' ] );
} else {
$active_section = 'dashboard' ;
}
}

if ( $active_tab == 'design-settings' ) {
if ( isset( $_GET['section' ] ) ) {
$active_section = sanitize_title_with_dashes( $_GET['section' ] );
} else {
$active_section = 'colors' ;
}
}

if ( $active_tab == 'layout-settings' ) {
if ( isset( $_GET['section' ] ) ) {
$active_section = sanitize_title_with_dashes( $_GET['section' ] );
} else {
$active_section = 'under-construction' ;
}
}

// Return the active section.
return $active_section;
}

/**
* Display tabs and sections.
*
* Display the options page navigation through tabs and sections.
*
* @since 1.0.0
* @access protected
*/
protected function tabs_and_sections() {
// Variables.
$active_tab = $this->get_active_tab();
$active_section = $this->get_active_section ();

// Display tabs.
?>
<h2 class="nav-tab-wrapper" >
<a href="?page=raindrop&tab=general -settings"
class="nav-tab <?php echo $active_tab == 'general -settings' ?
'nav-tab-active' : ''; ?>">
<?php echo esc_html__( 'General Settings' , 'raindrop' ); ?>
</a>

<a href="?page=raindrop&tab=design -settings"
class="nav-tab <?php echo $active_tab == 'design-settings' ?
'nav-tab-active' : ''; ?>">
<?php echo esc_html__( 'Design Settings' , 'raindrop' ); ?>

</a>

<a href="?page=raindrop&tab=la yout-settings"
class="nav-tab <?php echo $active_tab == 'layout-settings' ?
'nav-tab-active' : ''; ?>">
<?php echo esc_html__( 'Layout Settings' , 'raindrop' ); ?>
</a>
</h2>
<?php

// Display sections for the General Settings tab.
if ( $active_tab == 'general -settings' ) {
?>
<p class="nav-section-wrapper" >
<a href="?page=raindrop&tab=ge neral-settings &section =dashboard"
class="nav-section <?php echo $active_section == 'dashboard' ?
'nav-section-active' : ''; ?>">
<?php echo esc_html__( 'Dashboard' , 'raindrop' ); ?>
</a>

<span class="nav-section-separator" ></span>

<a href="?page=raindrop&tab=general -settings &section =access-control"
class="nav-section <?php echo $active_section == 'access-control' ?
'nav-section-active' : ''; ?>">
<?php echo esc_html__( 'Access Control' , 'raindrop' ); ?>
</a>

<span class="nav-section-separator" ></span>

<a href="?page=raindrop&tab=general -settings &section =search-engines"
class="nav-section <?php echo $active_section == 'search-engines' ?
'nav-section-active' : ''; ?>">
<?php echo esc_html__( 'Search Engines' , 'raindrop' ); ?>
</a>

<span class="nav-section-separator" ></span>

<a href="?page=raindrop&tab=gen eral-settings &section =google-analytics"
class="nav-section <?php echo $active_section == 'google-analytics' ?
'nav-section-active' : ''; ?>">
<?php echo esc_html__( 'Google Analytics' , 'raindrop' ); ?>
</a>
</p>
<?php
}

// Display sections for the Design Settings tab.
if ( $active_tab == 'design-settings' ) {
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.
}

// Display sections for the Layout Settings tab.
if ( $active_tab == 'layout-settings' ) {
// Instrucțiuni eliminate pentru relevanță în cadrul acestei anexe.
}
}

// Alte funcții și variabile interne eliminate pentru relevanță în cadrul acestei anexe.
}

Codul prezentat până acum se rulează definind în clasa principală a plugi n-ului ca momente de
execuție următoarele cârlige de tip acțiune pentru funcți ile add_options_page () și
options_page_init() :
– admin_menu – pentru integrarea paginii în meniul WordPress
– admin_init – pentru rularea codului în panoul de control

class Raindrop {
private function define_hooks() {
$options = new Raindrop_ Options();

$this->loader->add_action( 'admin_menu ', $options, 'add_options_page ' );
$this->loader->add_action( 'admin_init' , $options, 'options_page_init ' );
}
}

Anexa 3: Popularea dinamica a librariei de fonturi folosind
Google Fonts API

Notă: Codul prezentat în cadrul acestei anexe este un fragment. În codul sursă al plugin -ului,
clasa prezentat ă mai jos conțin mai multe funcții. Pentru relevanță s -au păstrat doar secțiunile
ce țin de popularea dinamică a librăriei de fonturi folosind Google Fonts API .

Extras din fișierul /includes/general/class -raindrop -options.php
Cu ajutorul acestei clase este creată pagina de setări a plugin -ului . Secțiunea de cod prezentată
mai jos este folosită pentru popularea listei de fonturi în cadrul subtab -ului “Tipografie”.

/**
* Create the options page.
*
* This class is responsable for maintaining all the functions needed to create
* the options page.
*
* @since 1.0.0
*/
class Raindrop_Options {

/**
* API Key for Google Fonts.
*
* @since 1.0.0
* @access private
* @var string
*/
private $api_key = null;

/**
* Number of fonts to retreive.
*
* @since 1.0.0
* @access private
* @var int|string
*/
private $amount = 'all';

/**
* Cache time in days.
*
* @since 1.0.0
* @access private
* @var int
*/
private $cache_time = 365;

/**
* Initialize the class and get things started.
*
* @since 1.0.0
*/
public function __construct() {
$this->api_key = 'AIzaSyB QCExP4SYnuBQxYoS4DNbM9WgK93YJ 2FRk';
$this->amount = 'all';
$this->cache_time = 30;
}

/**
* Get Google Fonts from the API or cache.
*
* Get the list of Google Fonts using the API or from saved transients.
*
* @since 1.0.0
* @access protected
* @return array Array of Google Fonts.
*/
protected function get_fonts() {
$api_key = $this->api_key;
$cache_time = DAY_IN_SECONDS * $this->cache_time;

if ( ! $api_key ) {
return null;
}

// Set the 'raindrop_google_fonts' transient if not available.
if ( ! get_transient( 'raindrop_google_fonts' ) ) {
$cloud_url = 'https://www.googleapis.com/webfonts/v1/webfonts?key=' . $api_key;
$local_url = RAINDROP_DIR_URL .
'assets/fonts/google -fonts/google -fonts.json?ver=' . RAINDROP_VERSION;
$cloud_data = wp_remote_get( $cloud_url, array( 'sslverify' => false ) );
$local_data = wp_remote_get( $local_url, array( 'sslverify' => false ) );

if ( ! is_wp_error( $cloud_data ) ) {
$content = json_decode( $cloud_data[ 'body'] );

if ( isset( $content ->error ) ) {
if ( ! is_wp_error( $local_data ) ) {
$content = json_decode( $local_data[ 'body'] );
} else {
return null;
}
}
} else {
if ( ! is_wp_error( $local_data ) ) {
$content = json_decode( $local_data[ 'body'] );
} else {
return null;
}
}

set_transient( 'raindrop_google_fonts' , $content, $cache_time );
}

// Set the 'raindrop_popular_google_fonts' transient if not available.
if ( ! get_transient( 'raindrop_popular_google_fonts' ) ) {
$cloud_url = 'https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=' .
$api_key;
$local_url = RAINDROP_DIR_URL .
'assets/fonts/google -fonts/google -fonts-popular.json?ver=' .
RAINDROP_VERSION;
$cloud_data = wp_remote_get( $cloud_url, array( 'sslverify' => false ) );
$local_data = wp_remote_get( $local_url, array( 'sslverify' => false ) );

if ( ! is_wp_error( $cloud_data ) ) {
$content = json_decode( $cloud_data[ 'body'] );

if ( isset( $content ->error ) ) {
if ( ! is_wp_error( $local_data ) ) {
$content = json_decode( $local_data[ 'body'] );
} else {
return null;
}
}
} else {
if ( ! is_wp_error( $local_data ) ) {
$content = json_decode( $local_data[ 'body'] );
} else {
return null;
}

}

set_transient( 'raindrop_popular_google_fonts' , $content, $cache_time );
}

// Get fonts data from transients.
if ( 'all' == $this->amount ) {
$content = get_transient( 'raindrop_google_fonts' );
return $content ->items;
} else {
$content = get_transient( 'raindrop_popular_google_fonts' );
return array_slice( $content ->items, 0, $this->amount );
}
}

// Alte funcții și variabile interne eliminate pentru relevanță în cadrul acestei anexe.
}

Similar Posts