28.07.2013 Views

Hent teknisk dokumentation - Go Basic

Hent teknisk dokumentation - Go Basic

Hent teknisk dokumentation - Go Basic

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Dato<br />

28.01.2012<br />

<strong>Go</strong> <strong>Basic</strong> udvikler<strong>dokumentation</strong><br />

<strong>Go</strong> <strong>Basic</strong> er et asp.net projekt, som skal fungere som et startsite for nye webprojekter.<br />

I nuværende form er <strong>Go</strong> <strong>Basic</strong> implementeret i Umbraco, men i virkeligheden har vi<br />

lavet en arkitektur, som er minimalt koblet til Umbracos API for at opnå følgende<br />

fordele:<br />

Cms uafhængighed.<br />

Automatiseret kodegenerering<br />

Typestærkt datalag med auto completion og compile time-fejlhåndtering.<br />

Cms-uafhængighed<br />

Selve projektets formål er, at skabe basiskoden for en standard multisiteløsning til<br />

brug for offentlige myndigheder i Danmark. Vi har i første omgang valgt at benytte<br />

Umbraco, fordi det er gratis, men der er som sådan ikke nogen krav derud over, hvor<br />

man kan sige at det ene CMS system egner sig mere end de andre. Derfor har vi<br />

forsøgt at lave en minimal kobling til Umbraco i koden.<br />

Konceptuelt er arkitekturen derfor bygget op som illustreret i følgende diagram:<br />

<strong>Go</strong><strong>Basic</strong><br />

Cms Abstraktion<br />

Umbraco<br />

Adapter<br />

Sitecore<br />

Adapter<br />

EpiServer<br />

Adapter<br />

Umbraco<br />

Sitecore<br />

EpiServer<br />

I diagrammet repræsenterer <strong>Go</strong> <strong>Basic</strong> præsentations- og forretningslogikken for vores<br />

standardløsning. CMS-abstraktionen symboliserer en service, der kan kaldes for at<br />

tilgå de nødvendige data og operationer. <strong>Go</strong> <strong>Basic</strong>-koden bør dog ideelt set være<br />

skærmet af for, om det er det ene CMS eller det andet der ligger bag skyen. Den kode<br />

som oversætter instruktioner og data til det ene eller andet CMS kalder vi her for


adapters, og ideelt set er det altså kun denne del som skal udskiftes for at understøtte<br />

et andet system.<br />

CMS-abstraktionen har til opgave, at stille en lang række funktioner til rådighed. Også<br />

selv om de nævnte CMS systemer har relativt forskellige API’er.<br />

Items (objects)<br />

Templates (classes)<br />

Select<br />

Delete<br />

Fields (properties)<br />

Create<br />

Update<br />

Diagrammet her viser nogle af de overordnede elementer og operationer som CMSabstraktionen<br />

arbejder med.<br />

I forhold til navngivning har vi valgt at lade os inspirere Sitecore’s navngivning fordi<br />

den virker mest gennemtænkt. Det kan muligvis forvirre en smule eftersom en<br />

Template i Umbraco betyder en layoutfil, mens den i Sitecore svarer til Umbracos<br />

Document Type. For at gøre forvirringen mindre, viser vi her følgende<br />

navngivningstabel, som er oversat til tilsvarende objektorienteret terminologi i c#:<br />

<strong>Go</strong> <strong>Basic</strong> Sitecore Umbraco c#<br />

Template Template DocumentType class<br />

Entity Item Node / Document object<br />

Field Field PropertyType property<br />

BaseTemplate BaseTemplate Master base class<br />

Overordnet Arkitektur – Lagopdeling<br />

2


LinqIt.Cms <strong>Go</strong><strong>Basic</strong>.UmbracoServices<br />

<strong>Go</strong><strong>Basic</strong>.Logic<br />

<strong>Go</strong><strong>Basic</strong>.CustomFieldTypes<br />

<strong>Go</strong><strong>Basic</strong>.Web<br />

Umbraco Api<br />

Størstedelen af abstraktionen ligger i at benytte LinqIt.Cms abstraktionslaget til at<br />

oprette og udtrække data i mange forskellige sammenhænge. I LinqIT.Cms biblioteket<br />

findes CmsService klassen, som er den primære adgang til dataoprettelse og udtræk.<br />

Selve CmsService klassen er abstrakt, og den specifikke implementation, som taler<br />

Umbraco’s sprog er implementeret i <strong>Go</strong><strong>Basic</strong>.UmbracoServices biblioteket. Der bør<br />

ideelt set ikke være referencer til hverken UmbracoServices eller umbracos api fra<br />

andre steder i løsningen for at bibeholde den lave kobling, men af forskellige grunde<br />

optræder de dog i praksis enkelte steder i løsningen alligevel.<br />

I Umbraco findes to forskellige begreber af data, nemlig Documents ( som er data fra<br />

databasen ) og Nodes som er publiceret data.<br />

I LinqIt.Cms abstraktionen findes der derimod kun ét begreb, nemlig Entities som altså<br />

abstraherer forskellene væk mellem de to Umbraco Typer, hvilket også er den måde<br />

som eksempelvis Sitecore og EpiServer fungerer. Det er conteksten i hvilken koden<br />

eksekveres, som bestemmer hvilke Umbraco-typer der benyttes internt.<br />

Den reelle model, som er implementeret i <strong>Go</strong><strong>Basic</strong>.UmbracoServices, er et wrapper<br />

pattern, ser sådan her ud:<br />

3


LinqIt.Cms<br />

<strong>Go</strong><strong>Basic</strong>.UmbracoServices<br />

umbraco<br />

UmbracoNode<br />

-_node<br />

Entity<br />

-WrappedItem : object<br />

UmbracoItem<br />

cms<br />

UmbracoDocument<br />

-_document<br />

NodeFactory.Node Document<br />

Som det fremgår af modellen så er de to klasser UmbracoNode og<br />

UmbracoDocument, som wrapper funktionaliteten, af Umbracos interne typer, og som<br />

ensretter tilgangen til dataene.<br />

I praksis beskæftiger man sig imidlertid kun med Entity klassen, og specialiseringer af<br />

denne. Selve de specialiserede entiteter vi arbejder med i løsningen er deklareret i<br />

<strong>Go</strong><strong>Basic</strong>.Logic biblioteket.<br />

Entiteterne er så vidt muligt en repræsentation af nedarvningsforholdene af<br />

dokumenttyper i Umbraco. F.eks. ses her et udsnit af klasserne, som benyttes til<br />

Forms modulet.<br />

FormsAction<br />

+Execute()<br />

FormsSendMailAction<br />

+Execute()<br />

*<br />

1<br />

Entity<br />

GridModule<br />

-highlight : bool<br />

FormsModule<br />

-Fields : FormsField<br />

-Actions : FormsAction<br />

1 *<br />

FormsField<br />

FormsCheckBoxField FormsTextBoxField<br />

4


Som det ses i diagrammet, nedarver alle klasserne direkte, eller indirekte fra Entity<br />

klassen, som gør at CmsService klassen kan arbejde med dem.<br />

Eksempler på brugen af API-abstraktionen kommer her:<br />

var service = CmsService.Instance; // Returnerer via provider pattern en request<br />

scope instans af <strong>Go</strong><strong>Basic</strong>.UmbracoServices.UmbracoService.<br />

var module = service.GetItem(new Id(12)); Returnerer et typestærkt<br />

FormsModule objekt. Den tilsvarende kode i Umbraco ville være :<br />

var node = new NodeFactory.Node(12);<br />

eller<br />

var document = new Document(12);<br />

Modulsystem<br />

<strong>Go</strong> <strong>Basic</strong>s modulsystem er udviklet for at give redaktører en større fleksibilitet i forhold<br />

til opbygning af indhold. Et modul er basalt set en lille byggeklods som kan inkluderes<br />

på en eller flere sider. Der findes en række forskellige modultyper i <strong>Go</strong> <strong>Basic</strong>, bl.a. en<br />

nyhedsliste, en call-to-action box, et billedegalleri og en videoboks. Hver modultype<br />

har en række felter som gør, at det enkelte modul kan konfigureres på forskellige<br />

måder.<br />

Rent <strong>teknisk</strong> set, består et modul af en dokumenttype, en entitets klasse og en<br />

præsentationsklasse.<br />

Dokumenttypen skal nedarve fra GridModule dokumenttypen og skal navngives med<br />

postfix ”Module”, f.eks. ”MultiBox Module”. Det giver et dokumenttype alias som<br />

hedder følgende ”MultiBoxModule”. Der skal ikke specificeres en template ved<br />

oprettelsen. Derudover tilføjer man de felter man har behov for, for at konfigurere<br />

modulet.<br />

Entitetsklassen skal nedarve fra GridModule entiteten, og navnet skal svare til<br />

dokumenttypens alias. Dette sker helt automatisk ved at køre kodegenereringen. For<br />

at tilgå de enkelte felter på modulet oprettes en partiel klasse, hvorpå felterne oprettes<br />

som properties.<br />

Præsentationsklassen kan være en CustomControl eller en UserControl. Der er pt.<br />

ikke understøttelse af xslt renderinger. Præsentationsklassen skal hedde<br />

entitetsklassens navn plus postfikset ”Rendering”. Dvs. MultiBoxModuleRendering.<br />

Når et modul skal renderes leder systemet altså efter en præsentationsklasse som<br />

hedder modulets navn + ”Rendering”.<br />

Bemærk! Hvis man benytter sig af user controls til at implementere<br />

præsentationsklassen, skal den oprettes under /Modules folderen i roden af website<br />

projektet.<br />

Eksempel på tilføjelse af et modul<br />

I det følgende eksempel tager vi udgangspunkt i at benytte custom controls til at<br />

implementere et ”Hello World” modul.<br />

Opret dokumenttypen ”Hello World Module”.<br />

Log ind i Umbraco som administrator.<br />

Vælg ”settings” sektionen i venstre side i bunden.<br />

5


Højreklik på Grid Module dokumenttypen og vælg ”Create”<br />

Indtast navnet ”Hello World Module”, fjern krydset i ”Create matching template”, og klik<br />

Create.<br />

På infofanen kan du vælge et ikon til modulet, som gør det nemmere at lokalisere<br />

moduler i grid editoren. Nye ikoner kan tilføjes til Umbraco ved at tilføje et 16x16 .png<br />

billede til ~/umbraco/images/umbraco folderen. Benyt et af de ikoner som er<br />

depricated.<br />

6


På tabs fanen tilføjes en ny tab ”Content”.<br />

7


På Generic properties fanen tilføjes et felt ”Message” af typen textstring som placeres<br />

under ”Content”. Her kan man angive en hjælpetekst til redaktøren. Bemærk at<br />

Umbraco automatisk laver et alias kaldet ”message”.<br />

I Visual Studio åbnes filen ”Autogenerated.tt” i <strong>Go</strong><strong>Basic</strong>.Logic projektet under Entities<br />

folderen. Tryk ctrl+s for at aktivere autogenereringen af koden. Åbn Autogenerated.cs<br />

(under .tt filen), og verificer at der er dannet en ”HelloWorldModule” klasse. Dette er<br />

entitets-klassen.<br />

For at tilføje vores Message property, oprettes en ny klasse under Entities-folderen<br />

kaldet ”HelloWorldModule” med følgende kode ( bemærk at klassen skal være public<br />

partial ):<br />

using System;<br />

using System.Collections.Generic;<br />

using System.Linq;<br />

using System.Text;<br />

namespace <strong>Go</strong><strong>Basic</strong>.Logic.Entities<br />

{<br />

/// <br />

/// This is the entity class for the HelloWorld module<br />

/// <br />

public partial class HelloWorldModule<br />

{<br />

/// <br />

/// The message to display<br />

/// <br />

public string Message<br />

8


}<br />

}<br />

{<br />

}<br />

// Use the alias name<br />

get { return GetValue("message"); }<br />

Klassen ”extender” den autogenererede klasse med Message propertien.<br />

Opret en ny klasse ”HelloWorldModuleRendering” under ”Modules” folderen i<br />

<strong>Go</strong><strong>Basic</strong>.Logic projektet, med følgende kode:<br />

using System;<br />

using System.Collections.Generic;<br />

using System.Linq;<br />

using System.Text;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Entities;<br />

namespace <strong>Go</strong><strong>Basic</strong>.Logic.Modules<br />

{<br />

/// <br />

/// This is the presentation class for the HelloWorld module<br />

/// <br />

public class HelloWorldModuleRendering :<br />

BaseModuleRendering<br />

{<br />

/// <br />

/// This property is currently not being used, but is reserved<br />

for future use.<br />

/// <br />

public override string ModuleDescription<br />

{<br />

get { return "A module for outputting a message"; }<br />

}<br />

/// <br />

/// This method implements the rendering of the module.<br />

/// <br />

/// A strongly typed reference to the entity<br />

being rendered<br />

/// An advanced html writer with many<br />

capabilities<br />

protected override void RenderModule(HelloWorldModule module,<br />

LinqIt.Utils.Web.HtmlWriter writer)<br />

{<br />

writer.RenderFullTag(HtmlTextWriterTag.P, module.Message);<br />

}<br />

}<br />

}<br />

Byg projektet og test modulet på en given side i Umbraco.<br />

Moduler og Cookies<br />

<strong>Go</strong> <strong>Basic</strong> implementerer en godkendt løsning på krav om cookie-beskyttelse. Dette<br />

afsnit handler om, hvordan man sikrer, at nyudviklede moduler, som benytter cookies,<br />

overholder implementeringen.<br />

9


For at fortælle systemet at et modul benytter cookies, tilføjes interfacet<br />

IRequiresCookies til præsentationsklassen. Herefter overrides RegisterScripts<br />

metoden for at inkludere eventuelle scripts som skal inkluderes på siden.<br />

RegisterScripts og RenderModule bliver kun kaldt på modulet hvis cookies er<br />

accepteret. I stedet renderes en standard boks som fortæller brugeren at cookies ikke<br />

er aktiveret, samt et link til cookie informationssiden, hvor det er muligt at aktivere<br />

cookies.<br />

Her ses koden for <strong>Go</strong>ogle Maps modulet, som overholder cookiepolitikken.<br />

using System.Web.UI;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Entities;<br />

using LinqIt.Ajax.Parsing;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Modules;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Utilities;<br />

namespace <strong>Go</strong><strong>Basic</strong>.Logic.Modules<br />

{<br />

public class <strong>Go</strong>ogleMapModuleRendering :<br />

BaseModuleRendering, IRequiresCookies<br />

{<br />

protected override void RegisterScripts()<br />

{<br />

Assert.IsNotNull(Module);<br />

if (!Module.Latitude.HasValue || !Module.Longitude.HasValue)<br />

{<br />

Visible = false;<br />

return;<br />

}<br />

if<br />

(!Page.ClientScript.IsClientScriptIncludeRegistered(Page.GetType(),<br />

"googlemapsapi"))<br />

Page.ClientScript.RegisterClientScriptInclude(Page.GetType(),<br />

"googlemapsapi", "http://maps.googleapis.com/maps/api/js?sensor=false");<br />

const int defaultZoom = 14;<br />

ModuleScripts.RegisterInitScript("googlemaps", new<br />

JSONString(ClientID), new JSONNumber(Module.Latitude.Value), new<br />

JSONNumber(Module.Longitude.Value), new JSONNumber(Module.Zoom ??<br />

defaultZoom), new JSONBoolean(Module.ShowMarker));<br />

}<br />

protected override void RenderModule(<strong>Go</strong>ogleMapModule item,<br />

LinqIt.Utils.Web.HtmlWriter writer)<br />

{<br />

if (!Visible)<br />

return;<br />

}<br />

}<br />

base.RenderModule(item, writer);<br />

writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);<br />

writer.AddStyle(HtmlTextWriterStyle.Width, "100%");<br />

writer.RenderBeginTag(HtmlTextWriterTag.Div);<br />

writer.RenderEndTag();<br />

public override string ModuleDescription<br />

{<br />

get { return "A module displaying a google map"; }<br />

}<br />

10


}<br />

Site Status<br />

Site status er en feature, som giver administratorer mulighed for nemt og hurtigt at se,<br />

hvilke komponenter der er konfigureret for de forskellige sites i en <strong>Go</strong> <strong>Basic</strong> løsning.<br />

Det er også meningen, at man herfra nemt skal kunne konfigurere komponenterne<br />

uden at behøve at foretage alle de manuelle operationer, det ellers ville kræve.<br />

En komponent kan være alt lige fra hånderingen af 404-fejl, til konfigurationselementer<br />

til videoafsplining osv. En komponent kan også være et tredjepartsmodul som <strong>Go</strong><br />

<strong>Basic</strong> ikke kender til, og som kræver oprettelse af forskellige items i sitestrukturen for<br />

at fungere. Det skal altså være muligt for tredjepart, at tilføje sektioner til statussiden.<br />

Arkitekturen omkring sitestatus fungerer således, at systemet scanner de inkluderede<br />

assemblies efter klasser som implementerer ISiteComponent interfacet.<br />

public interface ISiteComponent<br />

{<br />

string Name { get; }<br />

void Initialize(Document siteRoot);<br />

SiteComponentState State { get; }<br />

void InstantiateIn(ControlCollection controls);<br />

}<br />

Name propertien benyttes til at udskrive komponententens navn.<br />

Initialize metoden har til formål at initializere State propertien for det givne<br />

site.<br />

State propertien kan antage følgende værdier : Ok, warning, disabled. Ok<br />

betyder at komponenten er konfigureret og virker. Warning betyder at<br />

komponenten er obligatorisk og ikke konfigureret, eller at komponenten kun<br />

er delvist konfigureret. Disabled betyder at komponenten ikke er<br />

konfigureret, og at komponenten ikke er obligatorisk (eller vigtig).<br />

I InstantiateIn metoden kan man oprette og tilføje diverse kontroller til<br />

outputtet, som f.eks. en label som beskriver status, eller en knap til at udføre<br />

en kommando.<br />

Denne løsning giver en stor fleksibilitet i forhold til, hvilke kontroller man vil vise, men<br />

det kan være lidt omstændigt at implementere interfacet. I de fleste tilfælde kan man<br />

med fordel nedarve sin klasse fra BaseSiteComponent, som implementerer<br />

ISiteComponent, og som har en række hjælpemetoder til forskellige formål. Alle<br />

indbyggede <strong>Go</strong> <strong>Basic</strong> komponenter nedarver fra denne klasse.<br />

Følgende eksempel viser, hvordan News Archive komponentens state er<br />

implementeret (simplificeret en smule her for overskuelighedens skyld ).<br />

Nyhedsarkivet kræver, at der findes en side af typen NewsListPage et sted under<br />

home item’et, og at der findes et systemlink kaldet ”NewsArchivePage” for sitet, som<br />

peger på denne.<br />

11


using System;<br />

using System.Collections.Generic;<br />

using System.Linq;<br />

using System.Text;<br />

using LinqIt.Cms;<br />

using <strong>Go</strong><strong>Basic</strong>.Interfaces.Enumerations;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Exceptions;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Utilities;<br />

using <strong>Go</strong><strong>Basic</strong>.Logic.Entities;<br />

namespace <strong>Go</strong><strong>Basic</strong>.Logic.Controllers.SiteManagement<br />

{<br />

public class NewsArchiveComponent : BaseSiteComponent<br />

{<br />

public override string Name<br />

{<br />

get { return "News Archive"; }<br />

}<br />

protected override void Initialize()<br />

{<br />

// Check if a page exists, which is linked to by a system link named "NewsArchivePage"<br />

var newsArchivePage = GetSiteLinkedEntity(SystemKey.NewsArchivePage);<br />

if (newsArchivePage == null)<br />

{<br />

// Notify that the component is disabled ( this is not a required component )<br />

// Provide a button click handler and text for the "Fix it" button.<br />

throw new SiteComponentException("The News archive is not yet enabled.", SiteComponentState.Disabled,<br />

OnSetupClicked, "Enable news archive");<br />

}<br />

}<br />

}<br />

}<br />

// If no exception was thrown, the page and systemlink is configured correctly.<br />

AddMessage("The News archive has been setup correctly.");<br />

// This code will execute when the "Fix It" button is clicked, and should setup the newsarchive<br />

protected void OnSetupClicked(object sender, EventArgs e)<br />

{<br />

using (CmsContext.Editing)<br />

{<br />

// Get a reference to the home page (This snippet assumes that the home page has been configured.)<br />

var homeItem = GetSiteLinkedEntity(SystemKey.HomePage);<br />

}<br />

// Create a newlistpage called "News Archive" under the home item<br />

var resultPage = CmsService.Instance.CreateEntity("News Archive", homeItem);<br />

EnsureSiteSystemLink(SystemKey.NewsArchivePage, resultPage);<br />

}<br />

ReloadEditor();<br />

En af de væsentlige ting som foretages af BaseSiteComponent klassen er at fange<br />

den SiteComponentException, som kastes under initialize fasen, og generere en label<br />

med fejlbeskeden, samt en knap som administratoren kan benytte til at løse<br />

problemerne. NewsArchiveComponent klassen behøver derfor kun at implementere<br />

”forretningslogikken”, og nedarver præsentationskoden.<br />

12


Installation af <strong>Go</strong> <strong>Basic</strong><br />

<strong>Go</strong> <strong>Basic</strong> udbydes som en enkelt zip-fil, som indeholder et visual studio projekt, samt<br />

et script til oprettelse af databasen. Det er ikke nødvendigt at installere Umbraco, da<br />

det er indeholdt i pakken. Databasescriptet findes i folderen ”scripts”.<br />

Følg disse trin for at installere <strong>Go</strong> <strong>Basic</strong> i udviklermiljøet:<br />

Opsætning af website:<br />

1. Udpak zipfilen et sted på harddisken.<br />

2. Opret et nyt site i IIS7 som peger på <strong>Go</strong><strong>Basic</strong>.WebSite folderen du netop har<br />

udpakket.<br />

3. Ret app poolen så den kører .net framework v.4.0, og integrated pipeline<br />

mode. Notér kontonavnet for app poolen, og ret evt. til Network Service.<br />

4. Ret sikkerhedsindstillinger på <strong>Go</strong><strong>Basic</strong>.WebSite folderen, så ovenstående<br />

konto (f.eks. Network Service) samt IUSR kontoen har læse og skrive adgang.<br />

5. Opret evt. en loopback entry i hosts filen så dit hostnavn peger på din<br />

maskine (sti: c:\windows\system32\drivers\etc\hosts , tilføj linje<br />

127.0.0.1 <br />

Database :<br />

1. Opret nyt login på database-serveren<br />

2. Opret en ny database<br />

3. Eksekver setup scriptet på databasen<br />

4. Tilføj din login account til databasen og tildel rettigheder<br />

5. Åbn web.config og ret appsetting’en ”umbracoDbDsn” i web.config til at<br />

pege på din database, og med dit kontonavn / kodeord<br />

6. Ret også appsettingén ”luceneindexfolder” i web.config til at pege ned på<br />

den udpakkede sti<br />

Kompilér løsningen og log ind i umbraco på adressen ”http:///umbraco/”.<br />

Dit brugernavn er ”admin” og kodeordet ”password”.<br />

13

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!