Lydia: Spara allt, filtrera och formattera vid utskrift

  • Författare
  • Meddelande
Användarvisningsbild

mos

dbwebb

  • Inlägg: 11180
  • Blev medlem: 10 nov 2011, 09:52
  • Ort: Ronneby / Bankeryd

Lydia: Spara allt, filtrera och formattera vid utskrift

Inlägg13 apr 2012, 10:11

Detta är en del i tutorialen Lydia: ett PHP-baserat, MVC-inspirerat CMF. Senast uppdaterad 2012-04-13.

Vi väljer en taktik att spara allt innehåll som användaren skriver in och vi filtrerar och formatterar innehållet när det presenteras för användaren. Vi väljer ett par olika sätt att behandla innehållet, vi börjar med "plain", "html", och "php".


Vad kan användaren skriva in för innehåll?

Text, ja, självklart. Men hur skall vi betrakta texten? Om vi tjuvkikar på Drupals hantering av innehåll så kan användaren välja att varje innehåll skall filtreras och formatteras på ett visst sätt. De vanliga sätten är "Ren text" och då betraktas texten som skrivs in som vanligt text och inga HTML-element tillåts. Hanteringen kan innehålla att radbrytningar ersätts med <br> och länkar ersätts till <a>. Det blir alltså enkel formattering av sådan text, men det kan vara bra för de användare som inte kan skriva HTML så bra. Sedan finns det formatteringssätt för "Filtrerad HTML" där det tillåts ett begränsat antal HTML-element och det finns "Full HTML" där alla HTML-element tillåts.

Detta ger en flexibel möjlighet till hur innehållet skall formatteras när det presenteras för besökaren. Det finns även möjlighet att tillåta ren PHP-kod i innehållet, att användaren kan skriva PHP-kod i innehållet som lagras i databasen och sedan exekveras när det presenteras för besökaren.

Ett sådant här stöd vill jag ha i ramverket. Då sätter vi igång.


Uppdatera databastabellen Content

Databastabellen Content får en ny kolumn, filter, som innehåller en sträng som anger vilken filtrering/formattering som skall ske. Jag håller det enkelt och lagrar strängen som anger vilket filter som skall användas.


Uppdatera modellen CMContent

I och med att tabellen ändrades så ändrades även flera av SQL-satserna i CMContent::SQL() och även de metoder som använder sig av SQL-satserna, i detta fallet var det CMContent::Init() och CMContent::Save().

Tills vidare läggs allt innehåll in som "plain", det finns säkerhetaspekter på att låta användaren skriva in innehåll som rent HTML (injektioner av JavaScript) och PHP (*farligt* hela servern tillgänglig för användaren) så även om jag bygger ut det stödet så vill jag inte tillåta det i en standardinstallation. Jag väljer att bygga in stödet men kommenterar bort det i källkoden tills vidare.


Hämta filtrerat och formatterat innehåll

Jag lägger till ett par metoder för att hämta det filtrerade och formatterade innehållet, datat i CMContent, $content['data']. Dels lägger jag till en statisk metod CMContent::Filter() som kan filtrera data enligt ett filter och dels lägger jag till en publik metod CMContent::GetFilteredData() som kan filtrera ett objekts data. Dessutom blir det en tema funktion i theme/functions.php, filter_data() som går att använda i vyerna och i temat.

Anledningen till att det blev så många metoder var att jag i CCPage-vyn jobbar med objekt av typen CMContent, där kan jag använda metoden CMContent::GetFilteredData(). Men i CCBlog-vyn jobbar jag med en array som kommer direkt från en databasfråga och då det inte finns ett objekt så får jag använda tema funktionen filter_data() istället.

Här kan det finnas läge för reenigineering framöver. Det går säkert att strukturera lite annorlunda för att få en mer enhetlig hantering. Men det lägger vi på hyllan tillsvidare. Så farligt var det inte.


Uppdatera kontrollern CCContent

Jag tittade igenom kontrollern CCContent men där finns inget behov av förändringar.


Uppdaterade formuläret CFormContent

Formuläret för att editera innehållet uppdaterades med det nya fältet för filtret. Jag lägger dit det som ett vanligt text-fält tills vidare.


Uppdatera vyerna för CCBlog och CCPage

De båda vyerna som används i CCBlog och CCPage fick uppdateras för att hämta det filtrerade och formatterade fältet.


Stöd för olika filtreringar och formatteringar

V kan kika lite på metoden som sköter filtreringen och formatteringen i CMContent.

CMContent::Filter()
Kod: Markera allt
  /**
   * Filter content according to a filter.
   *
   * @param $data string of text to filter and format according its filter settings.
   * @returns string with the filtered data.
   */
  public static function Filter($data, $filter) {
    switch($filter) {
      case 'php': $data = nl2br(makeClickable(eval('?>'.$data))); break;
      case 'html': $data = nl2br(makeClickable($data)); break;
      case 'plain':
      default: $data = nl2br(makeClickable(htmlEnt($data))); break;
    }
    return $data;
  }


I detta läget stöds alltså "plain", "html" och "php". I samtliga fall används PHP-metoden nl2br() för att ersätta radbrytningar till HTML-elementet <br>. Dessutom används funktionen makeClickable() (se src/bootstrap.php) för att göra alla länkar klickbara. Det finns ett eget foruminlägg om den funktionen.

När det filtreras enligt "plain" så används även PHP-metoden htmlentities() (jag har "wrappat" denna funktionen i min htmlEnt()) för att formattera om alla tecken till sina HTML-entiteter. Det gör att om användaren skrivit in HTML-kod så kommer den att synas, men inte att betraktas som HTML. Studera följande två bilder för att se skillnaden mellan samma innehåll formatterat enligt "plain" och "html".

Först som "plain", HTML-elementen syns men betraktas inte som HTML element eftersom metodenhtmlentities() använts.

Bild

Sedan samma innehåll men nu presenterat enligt filtret "html", nu tolkas innehållet som HTML.

Bild


För PHP-läget, "php", så används PHP-funktionen eval() för att evaluera och köra de PHP-instruktioner som finns i koden. Detta är självklart känsligt och vanskligt eftersom användaren i princip får tillgång till hela webbservern via PHP. Men om man begränsar åtkomsten till de betrodda användarna så är det ett mäktigt verktyg som gör att man snabbt kan skapa specialicet innehåll på en webbplats utan att behöva koda i ramverket. Bra, men använd med förstånd. Tänk på att hela $ly är tillgängligt från sådan PHP-kod, bara deklarera den som global $ly och använd den. Farligt och mäktigt, som sagt var.

I mitt standardläge så har jag kommenterat bort stödet för "html" och "php".


Säkerhet och hantering av innehållet

Nu behandlar vi data som lagrats i databasen av webbplatsens användare, vi sparar allt data som vi får den men vi filtrerar och formatterar när den används. Det är naturligtvis viktigt att vi inte glömmer att filtrera och formattera, annars öppnar vi säkerhetshål i webbplatsen. Det är alltså extra viktigt att alltid se till att det innehåll som användaren skrivit filtreras och formattera och att tema funktionen esc() används för att göra om "farliga" tecken till dess HTML entiteter.

Här behövs ordning och reda och koll på vilket läge en textsträng befinner sig i, är den säker eller ej, behövs den filtreras, formatteras eller escapas? Detta är en viktig aspekt i säker PHP-programmering av webbapplikationer.


Sammanfattningsvis

Nu hanterar vi innehållet på ett mer och mer flexibelt sätt.

Du kan testa min kod och se källkoden som vanligt via följande länkar. Du loggar in med root:root eller doe:doe.

http://dbwebb.se/lydia/tags/v0.2.13/
http://dbwebb.se/lydia/tags/v0.2.13/content
https://github.com/mosbth/lydia/tree/v0.2.13

Fortsätt med nästa tutorial.
...
..:
.... /mos

Vilka är online

Användare som besöker denna kategori: Google [Bot] och 31 gäster