Model activați php. Cum se creează o pagină de înregistrare personalizată în WordPress Multisite

Astăzi ne vom uita la exploatarea unei vulnerabilități critice de 1 zi în popularul CMS Joomla, care a explodat pe Internet la sfârșitul lunii octombrie. Vom vorbi despre vulnerabilități cu cifre CVE-2016-8869, CVE-2016-8870Și CVE-2016-9081. Toți trei provin dintr-o singură bucată de cod care a lânceit în adâncurile cadrului timp de cinci ani lungi, așteptând în aripi, pentru ca apoi să se elibereze și să aducă cu el haos, site-uri piratate și lacrimile utilizatorilor nevinovați ai acestui Joomla. Doar cei mai curajoși și curajoși dezvoltatori, ai căror ochi sunt roșii de la lumina monitoarelor și ale căror tastaturi sunt pline de firimituri de pâine, au reușit să provoace spiritele rele furioase și să-și pună capetele pe altarul reparațiilor.

AVERTIZARE

Toate informațiile sunt furnizate doar în scop informativ. Nici editorii, nici autorul nu sunt responsabili pentru eventualele daune cauzate de materialele acestui articol.

De unde a început totul

Pe 6 octombrie 2016, Demis Palma a creat un subiect pe Stack Exchange în care întreba: de ce, de fapt, în Joomla versiunea 3.6 există două metode de înregistrare a utilizatorilor cu același nume register()? Primul se află în controlerul UsersControllerRegistration, iar al doilea este în controlerul UsersControllerUser. Damis a vrut să știe dacă metoda UsersControllerUser::register() a fost folosită undeva sau dacă a fost doar un anacronism evolutiv rămas din vechea logică. Preocuparea lui a fost că, chiar dacă această metodă nu este folosită de nicio vizualizare, poate fi apelată printr-o interogare elaborată. La care am primit un răspuns de la un dezvoltator sub porecla itoctopus, care a confirmat: problema chiar există. Și a trimis un raport dezvoltatorilor Joomla.

Apoi evenimentele s-au dezvoltat cel mai rapid. Pe 18 octombrie, dezvoltatorii Joomla au acceptat raportul lui Damis, care până la acel moment elaborase un PoC care să permită înregistrarea utilizatorilor. El a publicat o notă pe site-ul său, unde a vorbit în termeni generali despre problema pe care a găsit-o și gândurile sale cu privire la această chestiune. În aceeași zi, este lansată o nouă versiune de Joomla 3.6.3, care conține încă cod vulnerabil.

După aceasta, Davide Tampellini învârte bug-ul până la punctul de a înregistra nu un simplu utilizator, ci un administrator. Și pe 21 octombrie, un nou caz ajunge la echipa de securitate Joomla. Se vorbește deja despre creșterea privilegiilor. În aceeași zi, pe site-ul Joomla apare un anunț că marți, 25 octombrie, va fi lansată următoarea versiune cu număr de serie 3.6.3, care corectează o vulnerabilitate critică din nucleul sistemului.

25 octombrie Echipa Joomla Security Strike găsește cea mai recentă problemă creată de fragmentul de cod descoperit de Damis. Apoi, un commit din 21 octombrie cu numele discret Prepare 3.6.4 Stable Release este împins în ramura principală a depozitului oficial Joomla, care remediază eroarea nefericită.

După această ieșire, numeroase persoane interesate se alătură comunității de dezvoltatori - încep să promoveze vulnerabilitatea și să pregătească exploit-uri.

Pe 27 octombrie, cercetătorul Harry Roberts încarcă un exploit gata făcut în depozitul Xiphos Research, care poate încărca un fișier PHP pe un server cu un CMS vulnerabil.

Detalii

Ei bine, fundalul s-a terminat, să trecem la partea cea mai interesantă - analiza vulnerabilității. Am instalat Joomla 3.6.3 ca versiune de testare, astfel încât toate numerele de linii vor fi relevante pentru această versiune. Și toate căile către fișierele pe care le veți vedea mai jos vor fi indicate în raport cu rădăcina CMS-ului instalat.

Datorită descoperirii lui Damis Palma, știm că există două metode care realizează înregistrarea utilizatorilor în sistem. Primul este folosit de CMS și se află în fișierul /components/com_users/controllers/registration.php:108. Al doilea (cel pe care va trebui să-l apelăm) locuiește în /components/com_users/controllers/user.php:293. Să aruncăm o privire mai atentă.

286: /** 287: * Metodă de înregistrare a unui utilizator. 288: * 289: * @return boolean 290: * 291: * @din 1.6 292: */ 293: public function register() 294: ( 295: JSession::checkToken("post") sau jexit(JText::_ ("JINVALID_TOKEN"); ... 300: // Obține datele din formular 301: $data = $this->input->get("user", array(), "array"); 315: $retur = $model->validate($form, $date 316: 317: // Verificați erorile 318: if ($return === false) 319: ( ... 345: / /). Finalizați înregistrarea 346: $retur = $model->register($date);

Aici am lăsat doar rânduri interesante. Versiunea completă a metodei vulnerabile poate fi vizualizată în depozitul Joomla.

Să ne dăm seama ce se întâmplă în timpul înregistrării normale a utilizatorului: ce date sunt trimise și cum sunt procesate. Dacă înregistrarea utilizatorului este activată în setări, formularul poate fi găsit la http://joomla.local/index.php/component/users/?view=registration.


O solicitare legitimă de înregistrare a unui utilizator arată ca următoarea captură de ecran.


Componenta com_users este responsabilă pentru lucrul cu utilizatorii. Acordați atenție parametrului sarcinii din cerere. Are formatul $controller.$method . Să ne uităm la structura fișierului.

Numele scripturilor din folder controlorii corespund denumirilor controlorilor apelați. Deoarece cererea noastră conține acum $controller = "registration" , fișierul va fi apelat înregistrare.phpși metoda sa register().

Atenție, întrebare: cum se transferă procesarea înregistrării într-un loc vulnerabil din cod? Probabil ai ghicit deja. Numele metodelor vulnerabile și reale sunt aceleași (înregistrare), așa că trebuie doar să schimbăm numele controlerului apelat. Unde este localizat controlerul nostru vulnerabil? Așa este, în dosar utilizator.php. Se dovedește $controller = "utilizator" . Punând totul împreună, obținem task = user.register . Acum cererea de înregistrare este procesată prin metoda de care avem nevoie.


Al doilea lucru pe care trebuie să-l facem este să trimitem datele în formatul corect. Totul este simplu aici. Legitimate register() așteaptă de la noi o matrice numită jform , în care trecem date de înregistrare - nume, autentificare, parolă, e-mail (vezi captura de ecran cu cererea).

  • /components/com_users/controllers/registration.php: 124: // Obțineți datele utilizatorului. 125: $requestData = $this->input->post->get("jform", array(), "array");

Clientul nostru primește aceste date de la o matrice numită utilizator.

  • /components/com_users/controllers/user.php: 301: // Obține datele formularului. 302: $date = $this->input->post->get("utilizator", array(), "array");

Prin urmare, schimbăm numele tuturor parametrilor din cerere de la jfrom la user .

Al treilea pas este să găsim un token CSRF valid, deoarece fără el nu va exista nicio înregistrare.

  • /components/com_users/controllers/user.php: 296: JSession::checkToken("post") sau jexit(JText::_("JINVALID_TOKEN"));

Arată ca un hash MD5, și îl poți lua, de exemplu, din formularul de autorizare de pe site-ul /index.php/component/users/?view=login.


Acum puteți crea utilizatori folosind metoda dorită. Dacă totul a funcționat, atunci felicitări - tocmai ai exploatat o vulnerabilitate CVE-2016-8870„Lipsește verificarea permisiunii pentru înregistrarea de noi utilizatori.”

Iată cum arată în metoda register() „de lucru” din controlerul UsersControllerRegistration:

  • /components/com_users/controllers/registration.php: 113: // Dacă înregistrarea este dezactivată - Redirecționează către pagina de conectare. 114: if (JComponentHelper::getParams("com_users")->get("allowUserRegistration") == 0) 115: ( 116: $this->setRedirect(JRoute::_("index.php?option=com_users&view=) login", false)); 117: 118: return false; 119: )

Și așa în vulnerabil:

  • /components/com_users/controllers/user.php:

Da, în nici un caz.

Pentru a înțelege a doua problemă, mult mai serioasă, să trimitem cererea pe care am creat-o și să vedem cum se execută în diferite părți ale codului. Iată piesa care este responsabilă pentru validarea datelor trimise de utilizator în metoda lucrătorului:

Continuarea este disponibilă numai pentru membri

Opțiunea 1. Alăturați-vă comunității „site” pentru a citi toate materialele de pe site

Calitatea de membru al comunității în perioada specificată vă va oferi acces la TOATE materialele Hacker, vă va crește reducerea cumulativă personală și vă va permite să acumulați un rating profesional Xakep Score!

Vă permite să utilizați o singură instalare WordPress pentru mai multe site-uri în același timp. În acest caz, fiecare site primește propriile tabele în baza de date cu un prefix unic.

Tabelele cu datele utilizatorilor înregistrați sunt comune tuturor site-urilor din rețea. Acesta este un plus sigur și prin înregistrare odată puteți obține acces la mai multe site-uri. Mai mult, pe fiecare site același cont poate avea drepturi diferite. De exemplu, pe un site un utilizator poate fi editor, iar pe altul administrator.

Într-o instalare tipică WordPress, pagina de înregistrare, autentificare și resetare a parolei este scoasă din fișierul wp-login.php.

  • wp-login.php - autorizare
  • wp-login.php?action=register - înregistrare
  • wp-login.php?action=lostpassword - resetarea parolei

În modul Multisite, nucleul WordPress începe să se comporte ușor diferit și atunci când urmați linkul wp-login.php?action=register, va avea loc o redirecționare către wp-signup.php. Aceasta este pagina dvs. de înregistrare în rețea care vine implicit cu WordPress.

Pe lângă înregistrarea conturilor de utilizator obișnuite, puteți crea și un site web nou pe acesta dacă super-administratorul a activat această funcție în setările rețelei (Administrator rețea → Setări → Setări rețea).

În majoritatea temelor, pagina de înregistrare nu arată foarte bine. Multe teme folosesc cadre CSS precum Bootstrap și propriile lor clase personalizate pentru a stila diferite elemente de pe pagini, așa că este greu să scrii un HTML care să se potrivească tuturor.

Dar nu disperați dacă pagina pare neîngrijită. Fișierul wp-signup.php este un lucru grozav la început, când nu aveți timp să lucrați prin fiecare detaliu al site-ului - vă puteți concentra pe alte pagini și conținut mai importante.

Când sunteți gata să vă creați propria pagină de înscriere, wp-signup.php este un exemplu bun pentru a vă ajuta să înțelegeți gama de funcții oferite de WordPress pentru procesarea și validarea intrărilor utilizatorilor și crearea de noi conturi.

Site-ul principal al rețelei

Implicit, WordPress deschide pagina de înregistrare (wp-signup.php) pe domeniul (site-ul) principal al rețelei. Cu toate acestea, puteți crea pagini de înregistrare pentru fiecare site din rețeaua dvs., chiar dacă acestea au teme.

Vom lua în considerare cazul când toate site-urile din rețea folosesc aceeași temă, dar fiecare dintre ele are o pagină de înregistrare. Site-urile diferă ca limbă (engleză și rusă), astfel încât pagina de înregistrare va fi afișată în limba „nativă” a site-ului. Dacă site-urile folosesc teme diferite, totul va depinde de ce teme sunt, dacă li se va potrivi același aspect (o situație excelentă care vă poate împinge să vă unificați toate temele) sau dacă merită să dezvoltați paginile individual.

Alternativă la functions.php

Ordinea fișierelor

Pluginurile MU pot conține orice număr de fișiere și o structură care vi se pare logică. Mă țin de ceva ca această ierarhie:

| mu-plugins | | încărcare.php | | selena-network | | | înscriere | | | | plugin.php | | | ... | | | jetpack | | | | plugin.php

Fișierul load.php include traduceri și toate „pluginurile” necesare:

// Se încarcă traduceri pentru pluginurile MU load_muplugin_textdomain("selena_network", "/selena-network/languages/"); // Funcționalitatea pentru pagina de înregistrare necesită WPMU_PLUGIN_DIR . „/selena-network/signup/plugin.php”; // Un alt plugin // necesită WPMU_PLUGIN_DIR ...

Folderele pluginurilor sunt stocate în directorul selena-network. Fiecare are propriul plugin.php, pe care îl includem în load.php. Acest lucru vă oferă flexibilitatea și capacitatea de a opri și a porni instantaneu componentele individuale ale unui proiect de lucru în caz de urgență.

Pagina de înregistrare

După ce ne-am dat seama unde și cum vom scrie codul, putem trece la crearea unei pagini de înregistrare.

Să creăm o pagină cu adresa example.org/signup/ prin interfața normală. Puteți utiliza orice adresă URL care vi se pare potrivită pentru proiectul dvs.

Redirecționați către pagina de înregistrare dorită

Pentru ca WordPress să învețe despre noua noastră pagină de înregistrare și să redirecționeze exact către ea, atunci când dați clic pe linkul „Înregistrare”, se folosește filtrul wp_signup_location. Poate fi găsit în interiorul wp-login.php și este responsabil pentru redirecționarea către wp-signup.php în mod implicit.

Caz „înregistrare”: if (is_multisite()) ( wp_redirect(apply_filters("wp_signup_location", network_site_url("wp-signup.php")))); ieșire; //...

După cum vă amintiți, în mod implicit, pagina de înregistrare se deschide pe domeniul principal al rețelei. Acesta este motivul pentru care network_site_url() este folosit aici.

Să adăugăm handlerul nostru la filtrul din mu-plugins/selena-network/signup/plugin.php, care va returna adresa paginii de înregistrare pe site-ul curent:

Funcția selena_network_signup_page($url) ( return home_url("înregistrare"); ) add_filter("wp_signup_location", "selena_network_signup_page", 99);

selena_network este prefixul pe care îl folosesc în numele tuturor funcțiilor din interiorul pluginurilor MU de pe site-ul meu pentru a evita coliziunile, ar trebui să fie înlocuit cu propriul prefix unic. Prioritatea de adăugare a unui filtru este 99, deoarece unele plugin-uri, de exemplu, bbPress și BuddyPress pot suprascrie această adresă cu propria lor (plugin-urile MU se încarcă mai devreme decât pluginurile obișnuite, vezi mai sus).

Vă rugăm să rețineți că este folosit home_url(), care, spre deosebire de network_site_url(), returnează adresa site-ului curent, și nu site-ul principal al rețelei.

Funcționalitatea wp-signup.php

Fișierul wp-signup.php conține un număr mare de funcții și cod. Pentru a vedea imaginea de ansamblu, puteți folosi codul de pliere. De regulă, în engleză acest lucru se numește „code pliere”.

La începutul fișierului, de la rândurile 1 la 80 (în versiunea 4.1.1), sunt efectuate diverse verificări și „start” paginii este scos folosind get_header() .

În continuare, sunt declarate multe metode și înainte de a începe să lucrăm cu ele, merită să înțelegem ce face fiecare funcție. Multe dintre ele folosesc adesea alte funcții cu prefixul wpmu_, toate acestea fiind declarate în fișierul wp-includes/ms-functions.php. Această secțiune este greu de înțeles fără să vezi singur codul. Mai jos este o scurtă descriere a principalelor funcții în cazul în care aveți dificultăți.

  • wpmu_signup_stylesheet() - Afișează CSS suplimentar pe pagina de înregistrare.
  • show_blog_form() - câmpuri pentru înregistrarea site-ului (adresă, nume, vizibilitate pentru motoarele de căutare).
  • validate_blog_form() - validează adresa și titlul site-ului introduse folosind wpmu_validate_blog_signup() .
  • show_user_form() - câmpuri pentru înregistrarea utilizatorului (login și adresa de e-mail).
  • validate_user_form() — verificarea autentificarii și a adresei de e-mail introduse. e-mail folosind wpmu_validate_user_signup() .
  • signup_another_blog() - câmpuri pentru înregistrarea site-urilor noi folosind show_blog_form() pentru utilizatorii care sunt deja înregistrați pe site.
  • validate_another_blog_signup() - verifică adresa și titlul site-ului folosind validate_blog_form() .
  • signup_user() este funcția principală de afișare a câmpurilor paginii de înregistrare.
  • validate_user_signup() - verifică autentificarea și adresa de e-mail. mail folosind validate_user_form() .
  • signup_blog() - câmpuri pentru introducerea adresei, numelui și vizibilității site-ului (al doilea pas de înregistrare) folosind show_blog_form() .
  • validate_blog_signup() - verifică autentificare, adresa de e-mail. e-mail, adresa și numele site-ului web.

În partea de jos a fișierului wp-signup.php (de la linia 646 în versiunea 4.1.1) se află logica principală a paginii de înregistrare, care folosește toate metodele descrise mai sus. Această parte a codului nu este inclusă în funcție. La sfârșit, get_footer() este apelat.

Copiați funcționalitatea wp-signup.php

Următoarele vor descrie procedura de copiere a wp-signup.php în pluginurile MU și de a face modificări la „furcătură”. Aceasta poate să nu pară cea mai bună cale de a merge. În schimb, puteți scrie propriile funcții de la zero pentru a valida și afișa formulare folosind clase, mai degrabă decât funcții obișnuite. După părerea mea, wp-signup.php are deja toată logica necesară pentru pagina noastră, rămâne doar să facem câteva mici modificări.

Când WordPress este actualizat, wp-signup.php se schimbă și el din când în când, dar asta nu înseamnă că va trebui să sincronizezi „furcătura” cu fiecare lansare. Funcțiile din wp-signup.php se ocupă, în esență, doar de ieșirea HTML, verificarea datelor, crearea de conturi și site-uri, iar metodele cu prefixul wpmu_, declarat în ms-functions.php, sunt tratate.

Să creăm o funcție care va afișa formularul de înregistrare pe pagină. Pentru a face acest lucru, copiați wp-signup.php din rădăcina WordPress în mu-plugings/selena-network/signup/ . Să-l conectăm în mu-plugins/selena-network/signup/plugin.php).

Necesită WPMU_PLUGIN_DIR . „/selena-network/signup/wp-signup.php”;

Să eliminăm toate verificările necesare și inutile încă de la începutul fișierului copiat. În versiunea 4.1.1, acesta este tot codul de la rândurile 1 până la 80.

Acum suntem gata să creăm funcția principală pentru afișarea formularului de înregistrare. Pentru a face acest lucru, vom transfera toată logica de la linia 646 până la sfârșitul fișierului într-o funcție numită selena_network_signup_main. La final, vom elimina două închideri suplimentare

(liniile 722 și 723), precum și apelul get_footer().

În nou-creatul selena_network_signup_main(), la început vom declara variabila globală active_signup, care este folosită de toate celelalte metode din acest fișier. Și să adăugăm un apel la evenimentul before_signup_form, pe care l-am eliminat chiar de la începutul fișierului.

Funcția selena_network_signup_main() ( global $active_signup; do_action("before_signup_form"); // ... )

Acum nu mai rămâne decât să schimbați aspectul în toate locurile unde este necesar și pagina de înregistrare este gata.

Ieșirea formularului de înregistrare

Există cel puțin două opțiuni aici. O modalitate mai convenabilă este să creați un shortcode și să-l plasați pe pagină printr-un editor obișnuit.

// Creați un shortcode network_signup add_shortcode("network_signup", "selena_network_signup_main");

A doua opțiune este să creați un șablon de pagină page-signup.php în folderul cu tema copilului. În loc de cuvântul „înregistrare”, puteți folosi ID-ul unic alocat paginii. În interiorul șablonului, adăugați aspectul necesar și apelați selena_network_signup_main() în locul potrivit.

Drept urmare, pagina mea de înregistrare arăta mult mai bine și mai curată.

Pagina de activare

În mod implicit, WordPress împarte procesul de înregistrare în Multisite în doi pași - completarea unui formular pe site și activarea contului dvs. făcând clic pe linkul trimis în e-mail. După ce completați formularul creat în secțiunea anterioară, WordPress trimite un e-mail cu instrucțiuni scurte și un link pentru a vă activa contul.

Fișierul wp-activate.php aflat în directorul rădăcină WordPress este responsabil pentru afișarea paginii de activare. wp-activate.php poate fi de asemenea schimbat complet. Procesul este similar cu ceea ce am făcut deja pentru wp-signup.php.

Să creăm pagina example.org/activate/ prin interfața normală. Pentru adresă, utilizați orice adresă URL care vi se pare potrivită.

Să copiem fișierul wp-activate.php în pluginurile noastre MU și să-l conectăm la mu-plugins/selena-network/signup/plugin.php.

Necesită WPMU_PLUGIN_DIR . „/selena-network/signup/wp-activate.php”;

Nu există prea mult conținut în interior, spre deosebire de wp-signup.php. Fișierul efectuează o singură operațiune - activează contul dacă este primită cheia corectă și afișează un mesaj despre eroarea sau finalizarea cu succes a operațiunii.

Să eliminăm toate verificările inutile și să solicităm - de la rândurile 1 la 69 în WordPress 4.1.1. La final, vom elimina apelul get_footer(). Vom transfera conținutul rămas în funcția selena_network_activate_main().

Este interesant de observat că aici, înainte de a încărca WordPress (wp-load.php), a fost declarată constanta WP_INSTALLING. Prezența sa face ca WordPress să nu încarce pluginuri.

Ca și în cazul paginii de înregistrare, nu mai rămâne decât să corectăm layout-ul acolo unde este necesar. De asemenea, puteți modifica textul mesajelor afișate (în acest caz, nu uitați să adăugați domeniul text al pluginurilor dumneavoastră MU la toate funcțiile de traducător; implicit, acesta nu este instalat nicăieri).

Funcția gata făcută poate fi utilizată pe o pagină pre-creată printr-un shortcode sau un șablon separat într-o temă copil.

Scrisori de activare cu linkuri corecte

Pagina de activare este gata de funcționare, dar WordPress nu știe despre asta și va trimite în continuare e-mailuri de activare cu un link către wp-activate.php. Spre deosebire de wp-signup.php, nu există niciun filtru care să vă permită să schimbați adresa. În schimb, trebuie să scrieți propria funcție care va trimite scrisori cu linkurile corecte.

Când completați și trimiteți formularul de pe pagina de înregistrare, WordPress sună wpmu_signup_ utilizator() sau wpmu_signup_ blog() în funcție de tipul de înregistrare. Ambele funcții creează o nouă intrare în tabelul wp_signups, completând-o cu conținutul necesar, care include cheia de activare a contului.

Ulterior, în funcție de funcție, se apelează wpmu_signup_ utilizator _notification() sau wpmu_signup_ blog _notificare() . Ambele funcții au funcționalități similare - generează și trimit un e-mail cu un link de activare, dar iau argumente diferite. Ambele au filtre pentru a „intercepta” evenimentul.

Dacă (! apply_filters("wpmu_signup_user_notification", $user, $user_email, $key, $meta)) returnează false;

Pentru a activa conturi cu crearea blogului:

Dacă (! apply_filters("wpmu_signup_blog_notification", $domain, $path, $title, $user, $user_email, $key, $meta)) ( returnează false; )

Tot ce rămâne este să-ți scrii propriile handlere, în interiorul cărora să trimiți scrisori prin wp_mail() , iar la sfârșit, asigură-te că returnezi false, astfel încât WordPress să nu trimită o scrisoare de activare de două ori - una este a ta, cealaltă este cea implicită scrisoare cu un link către wp-activate.php .

Funcția selena_network_wpmu_signup_user_notification($user, $user_email, $key, $meta = array()) ( // Generați antetul, textul și anteturile scrisorii // ... // Trimiteți scrisoarea sau adăugați o sarcină Cron pentru a trimite scrisoare wp_mail($user_email , wp_specialchars_decode($subject), $message, $message_headers // Dă false pentru ca WordPress să nu trimită de două ori e-mailul de activare return false("wpmu_signup_user_notification", "selena_network_wpmu_notification),"; ;

Dacă trimiteți e-mailuri prin intermediul unui server SMTP sau dacă numărul de înregistrări este foarte mare, ar trebui să vă gândiți să nu trimiteți e-mailuri instantaneu. În schimb, puteți adăuga sarcini Cron folosind WordPress Cron.

Închidem accesul la wp-signup.php și wp-activate.php

După ce v-ați creat propriile pagini de înregistrare și activare, este posibil să doriți să închideți „originalele”. De exemplu, dacă există câmpuri suplimentare pe pagina de înregistrare care trebuie completate. De asemenea, multe site-uri WordPress fac obiectul înregistrărilor de spam.

Puteți rezolva două probleme într-o singură acțiune, cerând Apache să returneze un 404 dacă încercați să deschideți aceste pagini. Pentru a face acest lucru, trebuie doar să adăugați câteva RewriteRules suplimentare la fișierul de configurare sau .htaccess .

RewriteEngine On RewriteBase / # Cunoașterea expresiilor regulate nu va fi niciodată de prisos :) RewriteRule ^wp-signup\.php - RewriteRule ^wp-activate\.php - # BEGIN WordPress # Nu atingem regulile WordPress în mod implicit :) # .. . # END WordPress

Concluzie

Există multe soluții pentru aceasta și multe alte „probleme” WordPress pe Internet. De exemplu, pentru a crea pagini de înregistrare și activare, unii sugerează să rescrieți wp-signup.php și wp-activate.php original. Acest lucru nu trebuie făcut, deoarece dacă actualizați WordPress, veți pierde toate modificările aduse fișierelor și, de asemenea, nu veți putea verifica integritatea nucleului folosind .

Când dezvoltați orice supliment, temă sau soluție, ar trebui să petreceți puțin timp pentru a înțelege ce se întâmplă în interiorul WordPress. Există multe instrumente utile de depanare pentru aceasta.

P.S.

Pentru a atribui automat diferite roluri utilizatorilor noi, puteți utiliza pluginul Multisite User Management.

Dacă aveți întrebări sau dificultăți în timpul creării paginilor de înregistrare și activare după citirea articolului, lăsați un comentariu și vă vom răspunde cu siguranță.

27.03.2015 27.03.2015

Dezvoltator WordPress. Îi place ordinea în toate și înțelegerea noilor instrumente. Inspirat de arhitectura componentelor Symfony.

  • Temele nu sunt de obicei funcționale, dar uneori noi dezvoltatorii trebuie să implementăm unele caracteristici în tema noastră pentru a o face puțin mai bună și mai convenabilă.

    În acest tutorial, vom explora termenul „teritoriu plugin” și, de asemenea, vom învăța cum să folosim un instrument fantastic scris de Thomas Griffin: biblioteca TGM Plugin Activation.

    Funcționalitatea temei: invadarea teritoriului pluginului

    Temele sunt concepute pentru a schimba designul unui site web WordPress. În mod ideal, tema ar trebui să abordeze doar aspectul vizual. Cu toate acestea, în această epocă de aur a WordPress, dezvoltatorii de plugin-uri includ adesea funcții funcționale în temele lor care îi ajută să rămână competitivi pe piață.

    Aceasta este o incursiune în teritoriul pluginului. Ne putem gândi la „teritoriu plugin” ca la niște secțiuni funcționale de cod. Orice fragment de cod care modifică funcționalitatea site-ului dvs. ar trebui să fie sub forma unui plugin, cu excepția cazului în care codul respectiv este încorporat în nucleul WordPress.

    Am formulat deja mai devreme într-unul dintre articolele mele o regulă generală pentru „teritoriu plugin:

    Dacă o caracteristică este legată de prezentarea vizuală a site-ului, atunci ar trebui inclusă în temă; dacă este legat de funcționalitate, atunci ar trebui să fie prezentat ca un plugin separat.

    O regulă destul de simplă. Oamenii încă încearcă să codifice fragmente funcționale în temele lor, dar directoarele de teme (cum ar fi WordPress.org sau ThemeForest) nu acceptă teme care se aventurează în „teritoriul pluginurilor”. Astfel, oferirea de funcționalități în teme a devenit o mică provocare.

    Din fericire, există o soluție simplă care nu contravine regulii teritoriului pluginului.

    Introducere în biblioteca de activare a pluginurilor TGM

    Configurarea activării pluginului TGM

    Observați funcția tgmpa() cu doi parametri la sfârșitul codului. Al doilea parametru este variabila $config, care este, de asemenea, o matrice, ca $plugins. După cum sugerează și numele, puteți personaliza biblioteca TGM Plugin Activation folosind această matrice. De asemenea, variabila acceptă propriul set de opțiuni:

    • id (șir) – id unic pentru biblioteca de activare a pluginului TGM din tema dvs. Acest lucru este foarte important: dacă și alte plugin-uri folosesc activarea pluginului TGM, ID-uri diferite vor preveni posibilele conflicte.
    • default_path (șir) – calea absolută implicită pentru pluginurile din tema dvs. Odată ce îl instalați, puteți utiliza numele fișierului zip ca valoare a parametrului sursă pentru pluginul dvs.
    • meniu (șir) – meniu slug pentru pagina de instalare a pluginului.
    • has_notices (boolean) – dacă se setează la true, vor fi emise notificări de administrator pentru pluginurile necesare/recomandate.
    • respins (boolean) – dacă se setează la adevărat, utilizatorul poate „închide” notificările.
    • dismiss_msg (șir) – dacă opțiunea de respins este setată la false, acest mesaj va fi afișat deasupra notificării de administrator.
    • is_automatic (boolean) – dacă este setat la true, pluginurile vor fi activate după ce utilizatorul este de acord să le instaleze.
    • mesaj (șir) – ieșire HTML suplimentară înainte de tabelul de pluginuri.
    • șiruri de caractere (array) – o matrice care include mesajele care urmează să fie afișate. Le puteți specifica ca șiruri traduse. Priviți fișierul example.php pentru a vedea o listă completă a tuturor mesajelor.
    "mytheme-tgmpa", // ID-ul dvs. unic TGMPA "default_path" => get_stylesheet_directory() . "/lib/plugins/", // calea absolută implicită "menu" => "mytheme-install-required-plugins", // meniu slug "has_notices" => true, // Afișează notificările de administrator "dismissable" => false , // notificările NU sunt respinse "dismiss_msg" => "Într-adevăr, chiar am nevoie de tine să instalezi aceste plugin-uri, bine?", // acest mesaj va fi afișat în partea de sus a mesajului "is_automatic" => adevărat, // automat activați pluginurile după instalare "message" => "", // mesajul de afișat chiar înainte de tabelul de pluginuri "strings" => array(); // Matricea de șiruri de mesaje pe care le folosește TGM Plugin Activation); ?>

    Concluzie

    După cum puteți vedea, este posibilă oferirea de funcționalități în temele WordPress - trebuie doar să vă gândiți mai întâi la utilizatorii care ar putea trece de la o temă la alta. Biblioteca TGM Plugin Activation oferă o modalitate foarte inteligentă de a face acest lucru.

    Ce părere aveți despre acest instrument? L-ați folosit vreodată, intenționați să îl folosiți în viitor? Impartaseste-ti gandurile!

    Creăm propria noastră pagină de înregistrare pentru un multisite în locul standardului wp-signup.php.

    Într-o instalare tipică WordPress, pagina de înregistrare (autentificare, resetare parolă) este scoasă din fișierul wp-login.php.

    • /wp-login.php - autorizare
    • /wp-login.php?action=register - înregistrare
    • /wp-login.php?action=lostpassword - resetarea parolei

    Există condiții separate pentru un multisite în wp-login.php. Deci, atunci când urmați linkul /wp-login.php?action=register pe un multisite, WordPress va redirecționa către pagina /wp-signup.php. Multe teme nu fac pagina să arate foarte atractivă, așa că o vom crea pe a noastră.

    Site-ul principal al rețelei

    Implicit, WordPress deschide pagina de înregistrare (wp-signup.php) pe domeniul (site-ul) principal al rețelei. Cu toate acestea, puteți face o pagină de înregistrare separată pentru fiecare site din rețeaua dvs., chiar dacă acestea au teme diferite. Vom lua în considerare cazul în care toate site-urile din rețea au propria lor pagină de înregistrare, dar se folosește aceeași temă și site-urile diferă doar prin limbă. Dacă utilizați teme diferite, va trebui să scrieți mai mult cod.

    functions.php?

    Nu. Acest nume de fișier pare să fie menționat în fiecare articol despre WordPress. În cazul nostru, având în vedere că funcționalitatea de înregistrare este concepută pentru mai multe site-uri, are sens să o includem în plugin-urile MU, care se încarcă la deschiderea oricărui site.

    Digresiune lirică

    Este de remarcat faptul că pluginurile MU sunt încărcate înaintea pluginurilor obișnuite și înainte ca nucleul WordPress să fie complet încărcat, astfel încât apelarea unor funcții poate duce la erori fatale în PHP. O astfel de încărcare „devreme” are și avantajele sale. Să presupunem că în interiorul oricărei teme nu puteți atașa la unele acțiuni care sunt declanșate chiar înainte ca fișierul functions.php să fie încărcat din temă. Un exemplu în acest sens sunt acțiunile din pluginul Jetpack de forma jetpack_module_loaded_related-posts (related-posts este numele modulului), cu ajutorul cărora este posibilă monitorizarea activității modulelor din Jetpack. Este imposibil să „atașați” la această acțiune din fișierul temă, deoarece acțiunea s-a declanșat deja înainte ca tema să fie încărcată - pluginurile sunt încărcate înaintea temelor. Puteți arunca o privire la imaginea generală a ordinii de încărcare WordPress pe pagina Action Reference din codex.

    Ordinea fișierelor

    Pluginurile MU pot conține orice număr de fișiere și orice structură care vi se pare logică. Mă țin de ceva ca această ierarhie:

    |-mu-plugins |-|-load.php |-|-|-selena-network |-|-|-|-signup |-|-|-|-|-plugin.php |-|-|-| -|-... |-|-|-|-jetpack |-|-|-|-|-plugin.php

    Fișierul load.php conține toate „pluginurile” necesare pentru rețeaua noastră:

    // Încărcați traducerile pentru toate suplimentele load_muplugin_textdomain ("selena_network", "/selena-network/languages/"); // Înregistrarea în rețea necesită WPMU_PLUGIN_DIR . „/selena-network/signup/plugin.php”; // Alte pluginuri // necesită WPMU_PLUGIN_DIR ...

    În interiorul folderului selena-network sunt stocate foldere cu pluginuri, fiecare cu propriul plugin.php, pe care îl includem în load.php. Acest lucru vă oferă flexibilitate și abilitatea de a opri și a porni rapid lucrurile.

    Adresa paginii de înregistrare

    Pentru a specifica adresa paginii de înregistrare, utilizați filtrul wp_signup_location. Acesta poate fi găsit în interiorul fișierului wp-login.php și este responsabil pentru redirecționarea către wp-signup.php.

    Caz „înregistrare”: if (is_multisite()) ( wp_redirect(apply_filters("wp_signup_location", network_site_url("wp-signup.php"))); ieșire;

    Să adăugăm funcția noastră la mu-plugins/selena-network/signup/plugin.php, care va returna adresa paginii de înregistrare de pe site-ul curent:

    Funcția selena_network_signup_page ($url) ( return home_url () . "/signup/"; ) add_filter ( "wp_signup_location", "selena_network_signup_page", 99);

    selena_network este prefixul pe care îl folosesc în numele tuturor funcțiilor din interiorul pluginurilor MU de pe site-ul meu pentru a evita coliziunile, ar trebui să fie înlocuit cu propriul prefix unic. Prioritatea de adăugare a unui filtru este 99, deoarece unele plugin-uri, de exemplu bbPress și BuddyPress, pot suprascrie această adresă cu propria lor (plugin-urile MU se încarcă mai devreme decât pluginurile obișnuite, vezi mai sus). Rețineți că home_url() este folosit în loc de network_site_url() pentru a menține vizitatorul pe același domeniu. Orice adresă URL poate fi folosită ca adresă.

    Crearea unei pagini

    Acum haideți să creăm o pagină cu adresa site.com/signup/ prin interfața normală, iar în folderul cu tema copil șablonul pentru noua noastră pagină este page-signup.php. În loc de cuvântul „înregistrare”, puteți folosi un ID unic.

    În noul șablon, trebuie să apelați funcția selena_network_signup_main(), care va afișa formularul de înregistrare.

    Merită remarcat faptul că întregul proces de șablon este opțional și, în schimb, puteți crea propriul cod scurt care va apela și funcția selena_network_signup_main().

    wp-signup.php și wp-activate.php

    Acum să creăm o funcție care va afișa formularul de înregistrare. Pentru a face acest lucru, copiați fișierele wp-signup.php și wp-activate.php din rădăcina WordPress în mu-plugings/selena-network/signup/ (și nu uitați să le conectați în mu-plugins/selena-network /signup/plugin.php) . Manipulările ulterioare cu fișiere sunt extrem de dificile și lungi de descris, așa că va trebui să le faceți singur. Voi descrie exact ce trebuie făcut și voi publica fișierele sursă ale proiectului meu:

    1. La începutul fișierului, eliminați toate apelurile require , funcții și alt cod din afara funcțiilor.
    2. Redenumiți toate funcțiile adăugând prefixe unice la nume.
    3. Înfășurați partea inferioară a codului wp-signup.php în funcția selena_network_signup_main și la început scrieți global $active_signup; .
    4. Înlocuiți aspectul cu al dvs. în locurile potrivite.

    În interiorul wp-activate.php trebuie să faceți aproximativ același lucru:

    1. Eliminați tot codul din afara funcțiilor, înglobați aspectul într-o funcție separată.
    2. Schimbați aspectul în locurile unde este necesar.

    Fișierul wp-activate.php este responsabil pentru pagina de activare a contului. Ca și în cazul paginii de înregistrare, trebuie să creați un șablon separat pentru aceasta, în interiorul căruia trebuie să apelați funcția din fișierul wp-activate.php.

    Trimiterea scrisorilor de activare

    Pagina de înregistrare trimite vizitatorului un e-mail cu un link pentru a-și activa contul. În mod implicit, acest lucru este realizat de funcția wpmu_signup_user_notification() din fișierul ms-functions.php. Puteți împrumuta funcționalitatea acestuia pentru propria dvs. funcție. Motivul pentru a evita utilizarea acestei funcții este că trimite linkul de activare a contului de la wp-activate.php. Puteți „dezactiva” această funcție folosind filtrul wpmu_signup_user_notification, returnându-l fals (dacă nu se face acest lucru, scrisoarea de activare va fi trimisă de două ori, bine, de fapt două litere diferite).

    Funcția armyofselenagomez_wpmu_signup_user_notification($user, $user_email, $key, $meta = array()) ( // ... // Cod din funcția wpmu_signup_user_notification() wp_mail($user_email, wp_specialchars_decode($subject), $message_headers) ; return false ) add_filter("wpmu_signup_user_notification", "armyofselenagomez_wpmu_signup_user_notification", 10, 4);

    Drept urmare, pagina de înregistrare în tema Selena a început să arate mult mai curată și mai îngrijită.

    Concluzie

    Există multe alte modalități nu tocmai corecte pe Internet de a face același lucru - redirecționări Apache, formulare AJAX care nu vor funcționa fără Java Script etc. Nu mi-au plăcut foarte mult toate acestea, așa că am încercat să o fac la fel de corect ca posibil pe propriul meu site.

    Remarc că ar trebui să editați fișierele cu atenție și să încercați să nu vă abateți prea mult de la cele originale, astfel încât pe viitor, dacă WordPress va modifica fișierele wp-signup.php și wp-activate.php, să fie mai ușor de comparat ei unul cu altul pentru a găsi schimbări.

    Nu uitați să vă uitați la codul sursă al tuturor funcțiilor descrise mai sus pentru a înțelege pe deplin ce și cum se întâmplă în interiorul codului.

    Primă. Protecție împotriva spammerilor

    Chiar și cele mai mici site-uri WordPress sunt adesea afectate de înregistrări spam. Poți scrie condiții nesfârșite pentru filtrarea boților, de multe ori mai degrabă o încercare de a crea inteligență artificială :) În cazul unui multisite, m-a ajutat foarte mult o redirecționare obișnuită în Apache, cu ajutorul căreia am cerut un 404 la deschidere / wp-signup.php și /wp-acitvate.php (nu sunt un expert în configurarea Apache, așa că regulile mele pot să nu fie foarte corecte).

    RewriteEngine On RewriteBase / RewriteRule ^wp-signup\.php - RewriteRule ^wp-activate\.php - # BEGIN WordPress # Nu atingem regulile de la WordPress în mod implicit :) # ... # END WordPress

    P.S. Încerc să descriu cât mai detaliat unele lucruri de la terți, pentru că când am început, uneori nu era nimeni să sugereze și să explice multe lucruri. De asemenea, cred că astfel de sfaturi mici despre alte materiale vor încuraja pe cineva să învețe ceva nou și să-și extindă aria de cunoștințe. Intrările RewriteRule folosesc expresii regulate, nu sunt deloc complicate, de exemplu, simbolul ^ înseamnă începutul unei linii.

    Înregistrează o funcție care se va declanșa când pluginul este activat.

    Această funcție atașează funcția de apel invers specificată la cârligul activate_(plugin) și este un înveliș pentru acel cârlig.

    (plugin) din hook activate_(plugin) este înlocuit cu numele căii relative către fișierul plugin principal. De exemplu, dacă pluginul este localizat: wp-content/plugins/sampleplugin/sample.php, atunci numele hook-ului va fi: activate_sampleplugin/sample.php.

    Din versiunea 3.1. Cârligul se declanșează numai în timpul activării pluginului și nu se declanșează în timpul actualizărilor automate ale pluginului.

    Cum functioneaza

    Pluginul este activat de funcția activate_plugin(), în care este declanșat hook-ul activate_(plugin).

    Funcția activate_plugin() din nucleu este apelată după încărcarea mediului VI. Această funcție conectează fișierul plugin principal (și tot ceea ce este specificat în el), apoi activează funcția de apel invers specificată printr-un cârlig. Din acest motiv, toate funcțiile și clasele pluginului sunt disponibile în funcția noastră de apel invers. Dar, din moment ce toate cârligele WP principale s-au declanșat deja când mediul VI este încărcat, niciun eveniment plugin care este agățat pe hook, de exemplu plugins_loaded , nu se va mai declanșa atunci când fișierul plugin principal este conectat. Aceasta înseamnă că pluginul nostru va fi conectat, dar nu complet: nu așa cum ar trebui să fie conectat atunci când este deja activat.

    Deci, de exemplu, dacă un plugin face ceva în timpul evenimentului plugins_loaded, atunci toate acele acțiuni pur și simplu nu se vor întâmpla atunci când pluginul este activat. De exemplu, dacă include un fișier de traducere, atunci fișierul de traducere nu va fi inclus în momentul în care funcția de apel invers specificată pentru register_activation_hook() este declanșată.

    De regulă, după declanșarea unei funcții de apel invers, există 2 evenimente la care pot fi atașate funcții: activated_plugin și shutdown .

    Pentru a face ceva fantezist atunci când activați un plugin, vedeți Exemplul 5.

    Termeni de utilizare

    Funcția nu va funcționa dacă este apelată când este declanșat un cârlig, de exemplu plugins_loaded , init . Funcția trebuie apelată direct din fișierul plugin principal. Reguli de activare:

      register_activation_hook() ar trebui să fie apelat din fișierul plugin principal, oriunde se află directiva Plugin Name: ... și nu ar trebui să fie apelat de la niciun hook precum plugins_loaded sau init .

      Funcția de cârlig trebuie să fie în același fișier cu cârligul sau conectată în prealabil dintr-un alt fișier.

      Ieșirea ecranului (eco) nu funcționează în funcția de cârlig. Pentru că are loc o redirecționare și nu veți vedea ecou. Dar puteți folosi die() .

    1. Variabilele globale (dacă există) trebuie definite în mod explicit pentru a fi accesate din funcția hook.

    O notă despre zona variabilă

    La activarea unui plugin, fișierul plugin principal nu este inclus în domeniul global, ci în cadrul funcției activate_plugin(). Prin urmare, variabilele care sunt considerate globale în funcționarea normală a pluginului nu vor fi globale.

    Deci, funcția care este utilizată în register_activation_hook() poate să nu vadă variabile globale, chiar dacă le-ați declarat ca fiind globale în cadrul acestei funcție. Exemplu:

    $myvar = „ceva”; register_activation_hook(__FILE__, "myplugin_activate"); function myplugin_activate())( global $myvar; echo $myvar; // Variabila nu este egala cu "ceva" )

    Din cauza acestei caracteristici, variabilele globale trebuie întotdeauna specificate în mod explicit. Toate variabilele globale trebuie definite ca fiind globale, chiar dacă variabila este specificată în corpul pluginului. Numai în acest caz veți avea acces la ele oriunde. Exemplu:

    Global $myvar; // indică în mod explicit că aceasta este o variabilă globală $myvar = "ceva"; register_activation_hook(__FILE__, "myplugin_activate"); function myplugin_activate())( global $myvar; echo $myvar; //> ceva)

    Nu există cârlige.

    Se intoarce

    nul. Nu returnează nimic.

    Utilizare

    register_activation_hook($fișier, $funcție); $file (șir) (obligatoriu) Calea către fișierul PHP principal al pluginului, inclusiv numele pluginului în sine. De obicei este folosită constanta magică PHP __FILE__. $funcție (șir/matrice/lambda) (obligatoriu)

    Numele funcției de apel invers. Pentru clase, utilizați o matrice: array($this, "function_name"); .

    Funcția va primi variabila logică $network_wide - dacă pluginul este activat pentru întreaga rețea de site-uri, în cazul unui multisite.

    Exemple

    #1. Rularea unei funcții atunci când este activat un plugin

    Să presupunem că avem o funcție my_plugin_activate() în fișierul principal de plugin: wp-content/plugins/myplugin/myplugin.php , apoi pentru a rula această funcție în timpul activării pluginului utilizați următorul cod:

    Register_activation_hook(__FILE__, "my_plugin_activate"); funcția my_plugin_activate() ( // Cod de activare... )

    #2. Rularea unei metode de clasă

    Dacă pluginul folosește o clasă PHP, codul de activare este adăugat astfel:

    Register_activation_hook(__FILE__, array("My_Plugin", "install")); clasa My_Plugin (funcția statică install() ( // Nu creați nicio ieșire aici... ) )

    #3. Rularea unei metode de clasă dintr-un fișier separat

    Dacă clasa care conține funcția de activare se află într-un fișier separat, atunci înregistrați funcția de activare astfel:

    Include_once __DIR__ . „/class-My_Plugin.php”; register_activation_hook(__FILE__, array("My_Plugin", "on_activate_function"));

    #4. Rularea unei metode de clasă din interiorul clasei în sine

    Dacă vă aflați în __construct() . Important, __FILE__ trebuie să „se uite” la fișierul principal de plugin:

    Register_activation_hook(__FILE__, array($this, "YOUR_METHOD_NAME"));

    # 5 Faceți ceva imediat după activarea pluginului

    După activarea unui plugin, sunt declanșate doar două cârlige: activated_plugin și shutdown .

    Când trebuie să faceți ceva imediat după activarea unui plugin, le puteți atașa o funcție.

    Când această soluție nu este potrivită, puteți utiliza opțiunile WP: salvați datele în opțiune și apoi verificați prezența opțiunii și faceți ceva dacă opțiunea este acolo:

    // Fișierul principal de plugin. ... function my_plugin_activate() ( // adauga o optiune pentru ca mai tarziu, daca este acolo, sa putem face ceva. add_option("Activated_Plugin", "Plugin-Slug"); // Aici este codul de activare... ) register_activation_hook(__FILE__, "my_plugin_activate"); funcția load_plugin() ( if (is_admin() && get_option("Activated_Plugin") == "Plugin-Slug") ( // ștergeți opțiunea adăugată astfel încât să nu mai funcționeze // și faceți ceea ce trebuie făcut... delete_option("Activated_Plugin "); // Faceți ceva o dată, după activarea pluginului // De exemplu: add_action("init", "my_init_function" ) ) add_action("admin_init", "load_plugin");

    O altă opțiune de a face ceva în timpul activării pluginului este să vă creați evenimentul astfel:

    Funcția my_plugin_activate())( // Configurați hook-ul astfel încât să vă puteți atașa la el din fișierele pluginului însuși do_action("my_plugin_activate"); ) register_activation_hook(__FILE__, "my_plugin_activate");

    # 6 O altă demonstrație a utilizării funcției

    Un mic plugin care demonstrează cum se utilizează funcția:

    /* Nume plugin: A Test Descriere: A Test */ require_once dirname(__FILE__) . „/my_other_file.php”; /* Acest cod nu va funcționa. Cârligul de activare trebuie apelat din fișierul principal. register_activation_hook (dirname(__FILE__) . "/my_other_file.php", "my_other_function"); */ // Acesta este un cod de lucru. register_activation_hook(__FILE__, "test_activated"); /* Acesta este modul corect de a declara și de a accesa variabilele globale. Variabilele globale trebuie declarate clar. Fără aceasta, nu veți avea acces la ele. */ global $some_var; $some_var = "hei"; // Funcția de activare a funcției test_activated())( // aici $some_var nu va fi egal cu hey global $some_var; // Și aici $some_var va fi egal cu hey // Această funcție este definită în fișierul "my_other_file.php" my_other_function(); /* Această opțiune nu va funcționa dacă trebuie să scrieți jurnalele într-un fișier temporar, utilizați fopen/fwrite. echo "test_activated numit!";