Dezvoltarea unui plugin WordPress pentru protejarea conținutului [632289]
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ă interacț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 plu gin-ului ………………………….. ………………………….. .. 19
2.1. Instalarea plugin -ului ………………………….. ………………………….. ………………………….. …………………………. 19
2.2. Panoul de control principal ………………………….. ………………………….. ………………………….. ………………. 22
2.3. Setările de acce s î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 culoare ………………………….. ………………………….. ………………………….. ………………………….. ….. 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: Securizarea 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 ………………………….. …………… 47
6.3. Verificarea automată a formatării cu ajutorul unui sniffer de cod ………………………….. …… 50
Capitolul 7: Provocări la dezvoltarea plugin -ului și soluțiile găsite ………………………….. …………………. 52
Concluzii ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. ……….. 53
Bibliografie ………………………….. ………………………….. ………………………….. ………………………….. ………………………….. ….. 54
Anexa 1: Determinarea accesului în funcție de rol și adresă IP ………………………….. …………………………. 55
Anexa 2: Panoul de control și sanitizarea opțiunilor ………………………….. ………………………….. ………………. 63
Anexa 3: Popularea dinamica a librariei de fonturi folosind Google Fonts API ………………………….. 74
Anexa 4: Crearea și animarea ceasului ………………………….. ………………………….. ………………………….. …………… 77
Lista figurilor
[se va complete la final]
Lista tabelelor
[se va complete la final]
Lista acronimelor
[se va complete la fin al]
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ținutu l 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 plugin , 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 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 ordPress 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țele gerea
diferitelor 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 u ltimul 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 plugin
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 pu r ș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 u rma 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 panoul de control
– posib ilităț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, up sell-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 permis 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ă et apă 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 definirea foarte clară a funcțional ităț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 riguroase
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 WordPress, cât și externe: Plug in 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 co nstat î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 modului de
funcționare și a aspectului interfeței afișate vizitatorilor fără drepturi de acces
– crearea unei pagini de întâmpinare cu un design plăcut afișată utilizatori lori fără acces
în cadrul căreia se afișează un ceas func țional (animat cu ajutorul CSS și JavaScript )
– implementar ea 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 va lidarea, 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 mod ifică 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 mportame ntul 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 ext indere 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 pachetulu i 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 avantaj î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 ajuta să reducem în mod
considerab il 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 software 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 functionalitate și prezentare.
WordPress este const ruit 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 repository -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 actualiza te
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 între 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 dezactiv area 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 important ă 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 est e î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. Aceste 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.php – 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 ina ctivarea 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 dez voltarea 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 c are 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 atacuri lor 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 actualiza opțiunile ex istente, 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țiunile 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 despre obiectul părin te. Î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 personalizat e.
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.
Short code API
Acest API permite adaugarea de suport 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 f olosirea shortcode -urilor se permite utilizatorilor 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 elementele 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 configuraț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 este un format de date standard deschis , ușor de interpretat , ce permite citirea, creare a ș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 automata 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ă folos ească 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 ef ectele
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 implicit . 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 pluggabl e 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 nostru, 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 s ite-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 folosit 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 antetului 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 externe 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 ofic ial 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. Pentru 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ă urmarim 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 cur entă 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
nimic. 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 es te 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ă trea că 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ție 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 pr in intermediul tab -urilor
și subtaburilor , sunt opționale .
Acest mod de funcționarea al plugin -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 sit e-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ă directive 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 p rivate, etc) Pe lângă un design dedicat cu setări separate de prezentare ș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 general e 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 bit 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 accesului ș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 spec ifice 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ă utilizarea 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 An alytics .
Pentru a evita atacurile de tip 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 profi lului definit în Google Analytics. ( în present
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 inamic 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 s unt 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,
controalele 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 ă tipografiei. 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 prim eș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 conectare î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 foloseș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 un ei imagini controlul acestei setări se schimbă în mod dinamic 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.
Design -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 implicite pentru cele 3 moduri predefinite
Notă: Pagina de întâmpinare conține un ceas functional, animat cu ajutorul CSS și JavaScript .
Crearea propriu -zisă a acestuia și script-ul de animare al limbilor pot fi văzut e în Anexa 4 .
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 poate 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 po t 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 di rect la fișiere.
Cu toate acestea, e bine 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ă acestea 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ă utilizarea 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 pentru
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 implementare 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_o ptions', // 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 pent ru 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 folo site 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 n once -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 imagine a 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 de 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 limbă 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/raindr op/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 de 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 fo loseș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 identifi catorul 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ă șablon pe baza căruia se genere ază 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ă , indif erent 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 tehn ologiile folosite în mod uzual în internet :
– Standarde de codare PHP
– Standarde de codare JavaScript
– Standarde de codare HTML
– Standarde de codare CSS
Notă: Respectarea ace stor standard e este obligatorie pentru temele și plugin -urile distribuite prin
intermediu l WordPress.
6.1. Formatarea codului sursă folosind standardele de codare WordPress
Deoarece lista de reguli pentru fiecare standard este f oarte lungă nu voi intra în det alii privind
reguli specifice . În schimb, voi prezenta pe scurt tipurile de reguli acoperite de acestea și voi
oferi câte un exemplu aleator ce poate fi observant în codul sursă al plugin -ului.
Pent ru standardele de codare PHP sunt definite reguli pentru:
– Utilizare a ghilimelelor simple sau duble
– Indentarea , alinierea și spațierea codului
– Poziționarea parantezelor și a acoladelor
– Închiderea , deschiderea și pozi ționarea tag -urior PHP
– Denumire a claselor , funcțiilor și a variabilelor
– Denumire a cârligelor WordPress dinamice prin interpolare
– Format area 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 folosin d caracterul underscores
– Denumirea claselor se face întotdeauna folosind cuvinte capitalizate și separarea
cuvintelor din denumire folosind caracte rul underscores
– Denumirea c onstantelor 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 caract erul 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: Convenții de spațiere
– Indentarea se face fol osind 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țiunile if, else , for , etc folosesc întotdeauna acolade și se scriu întotdeauna pe
mai multe linii
– Etc
// Denumirea corectă a funcției folosind camelCase .
function moveSecondsHand() {
var containers = document.querySelectorAll( '.seconds -container' );
// Exemple de indentare și spațiere între acolade, paranteze, variabile și operanzi.
setInterval( function () {
for ( var i = 0; i < containers.length; i++ ) {
if ( containers[i].angle === undefined ) {
containers[i].angle = 6;
} else {
containers[i].angle += 6;
}
containers[i].style.webkitTransform = 'rotateZ(' + containers[i].angle + 'deg)';
containers[i].style.transform = 'rotateZ(' + containers[i].angle + 'deg)';
}
}, 1000 );
}
Pentru standardele de codare HTML sunt defini te 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 HTM L
– 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
utilizatorilor 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 pen tru:
– Indentarea , 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 caracterul :
/* 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ă fol osind standardele de codare WordPress 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 scrierea 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ă folosind programe precum phpDocumentor sau DocBlox . Documentația
oficială a Word Press este generat ă aproape în totalitate pe baza acestui siste m.
La fel ca și în cazul standardelor de codare, nu voi intra în detalii priv ind reguli specific e de
formatare a comentariilor . Î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 , funcții și metode
– Constante și variabile
– Antete de fișiere
– Cârlige WordPress ( de tip acțiune sau filtr u)
– 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 f ormulă 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ă pachetul 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 de spre 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 cu înregistrarea și mentenanța cârligelor WordPress . Acestă secțiune a
În cadrul acestui extras de cod se po t observa come ntarii pentru aproape toate scenar iile de
utilizare :
– Comentarii de antet
– Comentarii de clase, funcții și metode
– Comentarii de varia bile
Notă: Mai m ulte exemple 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 loader class that registers all hooks
*
* @since 1.0.0
* @package Raindrop
*/
/**
* Register all actions and filters for the plugin.
*
* Maintain a list of all hooks that are registered throughout
* the plugin and register them with the WordPress API. Call the
* run function to execute the list of actions and filters.
*
* @since 1.0.0
*/
class Raindrop_Loader {
/**
* The array of actions registered with WordPress.
*
* @since 1.0.0
* @access protected
* @var array
*/
protected $actions ;
/**
* The array of filters registered with WordPress.
*
* @since 1.0.0
* @access protected
* @var array
*/
protected $filters ;
/**
* Initialize the class and get things started.
*
* @since 1.0.0
*/
public function __construct() {
$this->actions = array();
$this->filters = array();
}
/**
* Add a new action.
*
* Add a new action to the collection to be registered with WordPress.
*
* @since 1.0.0
* @param string $hook The name of the WordPress action that is being registered.
* @param object $component Instance of the object on which the action is defined.
* @param string $callback The name of the function definition on the $component.
* @param int $priority Optional. The priority at which the function should be fired.
* @param int $accepted_args Optional. The number of accepted arguments passed to $callback.
*/
public function add_action( $hook, $component , $callback , $priority = 10, $accepted_args = 1 ) {
$this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority,
$accepted_args );
}
/**
* Add a new filter.
*
* Add a new filter to the collection to be registered with WordPress.
*
* @since 1.0.0
* @param string $hook The name of the WordPress filter that is being registered.
* @param object $component Instance of the object on which the filter is defined.
* @param string $callback The name of the function definition on the $component.
* @param int $priority Optional. The priority at which the function should be fired.
* @param int $accepted_args Optional. The number of arguments passed to $callback.
*/
public function add_filter( $hook, $component , $callback , $priority = 10, $accepted_args = 1 ) {
$this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority,
$accepted_args );
}
/**
* Register all hooks into a single collection.
*
* A helper function that is used to register all actions and filters into a single
* collection.
*
* @since 1.0.0
* @access private
* @param array $hooks The collection of hooks that is being registered .
* @param string $hook The name of the WordPress filter that is being registered.
* @param object $component Instance of the object on which the filter is defined.
* @param string $callback The name of the function definition on the $component.
* @param int $priority Optional. The priority at which the function should be fired.
* @param int $accepted_args Optional. The number of arguments passed to $callback.
* @return array The collection of actions and filters registered with WordPress
*/
private function add( $hooks, $hook, $component , $callback , $priority , $accepted_args ) {
$hooks[] = array(
'hook' => $hook,
'component' => $component ,
'callback' => $callback ,
'priority' => $priority ,
'accepted_args' => $accepted_args ,
);
return $hooks;
}
/**
* Register hooks
*
* Register all our actions and filters with WordPress.
*
* @since 1.0.0
*/
public function run() {
foreach ( $this->actions as $hook ) {
add_action( $hook['hook'], array( $hook['component' ], $hook['callback' ] ),
$hook['priority' ], $hook['accepted_args' ] );
}
foreach ( $this->filters as $hook ) {
add_filter( $hook['hook'], array( $hook['component' ], $hook['callback' ] ),
$hook['priority' ], $hook['accepted_args' ] );
}
}
}
6.3. Verificarea automată a formată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 ac estuia 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 instalarea 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 Admini strator
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 pot descărca d e pe contul oficial de Git hub al
WordPress.
Ca un ultim pas, în editorul de cod preferat , la secțiunea de sniffer, trebuie adăug ată calea
C:\XAMPP \php \phpcs în câmpul pentru executabilul phpcs și indicat standardul dorit pentru
verificări.
Figura 6.1. Formatarea codului – Configu raț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 implementă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 formatare detectată automat
Capitolul 7: Provocări la dezvoltarea plugin -ului și soluțiile găsite
[se va completa]
Concluzii
[se va completa]
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 cadrul 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_Helpe r::decode_escaped_html( $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, try for
* forwarded IP address using HTTP_X_FORWARDED_FOR. If this is also not available,
* get the IP address using REMOTE_ADDR.
*
* @since 1.0.0
* @access protected
* @return string Visitor's IP.
*/
protected function get_ip() {
if ( ! emptyempty( $_SERVER[ 'HTTP_CLIENT_IP' ] ) ) {
$ip = $_SERVER[ 'HTTP_CLIENT_IP' ];
} elseif ( ! emptyempty( $_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. It expects you to
* use 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 if we have 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 is in IP/Netmask format.
// 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 the human-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 is in a.b.*.* format.
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 is in A-B format.
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. It expects you to
* use 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 if we have 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 is in IP/Netmask format.
// 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 human-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 is in a:b::*:* format.
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 is in A-B format.
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 acest ora î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.20 0</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_section = $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=layout -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=general -settings §ion =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 §ion =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 §ion =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=general -settings §ion =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 plugin -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.
}
Anexa 4: Crearea și a nimarea ceasului
Notă: Codul prezentat în cadrul acestei anexe este un fragment. În codul sursă al plugin -ului,
fișierele prezentate conțin mai multe instrucțiuni . Pentru relevanță s -au păstrat doar secțiunile
ce țin de crearea și animarea ceasului .
Extras din fișierul /includes /general/partials/overwrite -wp.php
Acesta este fișierul PHP ce generează codul HTML al interfeței afișate utilizatorilor fără drepturi
de acces. Secțiunea de cod prezentată mai jos este structura generală a ceasului.
<div class="clock">
<i class="hour-number hour-3"></i>
<i class="hour-number hour-6"></i>
<i class="hour-number hour-9"></i>
<i class="hour-number hour-12"></i>
<div class="hours-container" >
<div class="hours"></div>
</div>
<div class="minutes -container" >
<div class="minutes" ></div>
</div>
<div class="seconds -container" >
<div class="seconds" ></div>
</div>
</div>
Extras din fișierul /assets/stylesheets/raindrop.css
Acesta este fișierul CSS principal al plugin -ului. Secțiunea de cod prezentată mai jos este
responsabilă cu animarea limbii orar și adăugarea efectelor de tip bounce pentru limbile
minutar și secundar. Poziționarea în pagină și stilizarea elementelor ceasului au fost excluse din
prezentare.
@keyframes rotate {
100% {
-webkit-transform: rotateZ(360deg);
-moz-transform: rotateZ(360deg);
-ms-transform: rotateZ(360deg);
transform: rotateZ(360deg);
}
}
.hours-container {
animation: rotate 43200s infinite linear;
}
.minutes -container {
/* animation: rotate 3600s infinite steps(60); */
transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44);
}
.seconds -container {
/* animation: rotate 60s infinite steps(60) ; */
transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44);
}
Extras din fișierul /assets/javascript /raindrop.js
Acest a este fișier ul JavaScript principal al plugin -ului. Secțiunea de cod prezentată mai jos este
responsabilă cu setarea corectă a orei și animarea limbilor minutar și secundar ale ceasului.
// Animate clock.
jQuery( document ).ready( function ( $ ) {
initLocalClock();
setUpMinutesHand();
moveSecondsHand();
// Start clock using the local time.
function initLocalClock() {
// Get the local time using JS.
var date = new Date;
var seconds = date.getSeconds();
var minutes = date.getMinutes();
var hours = date.getHours();
// Create an object with each hand and it's angle in degrees.
var hands = [
{
hand: 'hours',
angle: ( hours * 30 ) + ( minutes / 2 )
},
{
hand: 'minutes' ,
angle: ( minutes * 6 )
},
{
hand: 'seconds' ,
angle: ( seconds * 6 )
}
];
// Loop through each of these hands to set their angle.
for ( var j = 0; j < hands.length; j++ ) {
var elements = document.querySelectorAll( '.' + hands[j].hand );
for ( var k = 0; k < elements.length; k++ ) {
elements[k].style.webkitTransform = 'rotateZ(' + hands[j].angle + 'deg)';
elements[k].style.transform = 'rotateZ(' + hands[j].angle + 'deg)';
// If this is a minute hand, note the seconds position (to calculate minute position
).
if ( hands[j].hand === 'minutes' ) {
elements[k].parentNode.setAttribute( 'data-second-angle', hands[j + 1].angle );
}
}
}
}
// Set a timeout for the first minute hand movement (less than 1 minute), then rotate it every
// minute after that.
function setUpMinutesHand() {
// Find out how far into the minute we are.
var containers = document.querySelectorAll( '.minutes -container' );
var secondAngle = containers[0].getAttribute( "data-second-angle" );
if ( secondAngle > 0 ) {
// Set a timeout until the end of the current minute, to move the hand.
var delay = ( ( ( 360 – secondAngle ) / 6 ) + 0.1 ) * 1000;
setTimeout( function () {
moveMinutesHand( containers );
}, delay );
}
}
// Do the first minute's rotation.
function moveMinutesHand( containers ) {
for ( var i = 0; i < containers.length; i++ ) {
containers[i].style.webkitTransfo rm = 'rotateZ(6deg)' ;
containers[i].style.transform = 'rotateZ(6deg)' ;
}
// Then continue with a 60 second interval.
setInterval( function () {
for ( var i = 0; i < containers.length; i++ ) {
if ( containers[i].angle === undefined ) {
containers[i].angle = 12;
} else {
containers[i].angle += 6;
}
containers[i].style.webkitTransform = 'rotateZ(' + containers[i].angle + 'deg)';
containers[i].style.transform = 'rotateZ(' + containers[i].angle + 'deg)';
}
}, 60000 );
}
// Move the second containers .
function moveSecondsHand() {
var containers = document.querySelectorAll( '.seconds -container' );
setInterval( function () {
for ( var i = 0; i < containers.length; i++ ) {
if ( containers[i].angle === undefined ) {
containers[i].ang le = 6;
} else {
containers[i].angle += 6;
}
containers[i].style.webkitTransform = 'rotateZ(' + containers[i].angle + 'deg)';
containers[i].style.transform = 'rotateZ(' + containers[i].angle + 'deg)';
}
}, 1000 );
}
} );
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Dezvoltarea unui plugin WordPress pentru protejarea conținutului [632289] (ID: 632289)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
