Dato 28.01.2012 Go Basic udviklerdokumentation Go Basic er et asp.net projekt, som skal fungere som et startsite for nye webprojekter. I nuværende form er Go Basic implementeret i Umbraco, men i virkeligheden har vi lavet en arkitektur, som er minimalt koblet til Umbracos API for at opnå følgende fordele: Cms uafhængighed. Automatiseret kodegenerering Typestærkt datalag med auto completion og compile time-fejlhåndtering. Cms-uafhængighed Selve projektets formål er, at skabe basiskoden for en standard multisiteløsning til brug for offentlige myndigheder i Danmark. Vi har i første omgang valgt at benytte Umbraco, fordi det er gratis, men der er som sådan ikke nogen krav derud over, hvor man kan sige at det ene CMS system egner sig mere end de andre. Derfor har vi forsøgt at lave en minimal kobling til Umbraco i koden. Konceptuelt er arkitekturen derfor bygget op som illustreret i følgende diagram: Umbraco Umbraco Adapter GoBasic Cms Abstraktion Sitecore Adapter Sitecore EpiServer Adapter EpiServer I diagrammet repræsenterer Go Basic præsentations- og forretningslogikken for vores standardløsning. CMS-abstraktionen symboliserer en service, der kan kaldes for at tilgå de nødvendige data og operationer. Go Basic-koden bør dog ideelt set være skærmet af for, om det er det ene CMS eller det andet der ligger bag skyen. Den kode 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 et andet system. CMS-abstraktionen har til opgave, at stille en lang række funktioner til rådighed. Også selv om de nævnte CMS systemer har relativt forskellige API er. Items (objects) Update Templates (classes) Fields (properties) Select Create Delete Diagrammet her viser nogle af de overordnede elementer og operationer som CMSabstraktionen arbejder med. I forhold til navngivning har vi valgt at lade os inspirere Sitecore s navngivning fordi den virker mest gennemtænkt. Det kan muligvis forvirre en smule eftersom en Template i Umbraco betyder en layoutfil, mens den i Sitecore svarer til Umbracos Document Type. For at gøre forvirringen mindre, viser vi her følgende navngivningstabel, som er oversat til tilsvarende objektorienteret terminologi i c#: Go Basic Sitecore Umbraco c# Template Template DocumentType class Entity Item Node / Document object Field Field PropertyType property BaseTemplate BaseTemplate Master base class Overordnet Arkitektur Lagopdeling 2
Umbraco Api LinqIt.Cms GoBasic.UmbracoServices GoBasic.Logic GoBasic.CustomFieldTypes GoBasic.Web Størstedelen af abstraktionen ligger i at benytte LinqIt.Cms abstraktionslaget til at oprette og udtrække data i mange forskellige sammenhænge. I LinqIT.Cms biblioteket findes CmsService klassen, som er den primære adgang til dataoprettelse og udtræk. Selve CmsService klassen er abstrakt, og den specifikke implementation, som taler Umbraco s sprog er implementeret i GoBasic.UmbracoServices biblioteket. Der bør ideelt set ikke være referencer til hverken UmbracoServices eller umbracos api fra andre steder i løsningen for at bibeholde den lave kobling, men af forskellige grunde optræder de dog i praksis enkelte steder i løsningen alligevel. I Umbraco findes to forskellige begreber af data, nemlig Documents ( som er data fra databasen ) og Nodes som er publiceret data. I LinqIt.Cms abstraktionen findes der derimod kun ét begreb, nemlig Entities som altså abstraherer forskellene væk mellem de to Umbraco Typer, hvilket også er den måde som eksempelvis Sitecore og EpiServer fungerer. Det er conteksten i hvilken koden eksekveres, som bestemmer hvilke Umbraco-typer der benyttes internt. Den reelle model, som er implementeret i GoBasic.UmbracoServices, er et wrapper pattern, ser sådan her ud: 3
LinqIt.Cms Entity -WrappedItem : object GoBasic.UmbracoServices UmbracoItem UmbracoNode UmbracoDocument -_node -_document NodeFactory.Node Document umbraco cms Som det fremgår af modellen så er de to klasser UmbracoNode og UmbracoDocument, som wrapper funktionaliteten, af Umbracos interne typer, og som ensretter tilgangen til dataene. I praksis beskæftiger man sig imidlertid kun med Entity klassen, og specialiseringer af denne. Selve de specialiserede entiteter vi arbejder med i løsningen er deklareret i GoBasic.Logic biblioteket. Entiteterne er så vidt muligt en repræsentation af nedarvningsforholdene af dokumenttyper i Umbraco. F.eks. ses her et udsnit af klasserne, som benyttes til Forms modulet. Entity GridModule -highlight : bool FormsAction * 1 +Execute() FormsModule -Fields : FormsField -Actions : FormsAction 1 * FormsField FormsSendMailAction +Execute() FormsCheckBoxField FormsTextBoxField 4
Som det ses i diagrammet, nedarver alle klasserne direkte, eller indirekte fra Entity klassen, som gør at CmsService klassen kan arbejde med dem. Eksempler på brugen af API-abstraktionen kommer her: var service = CmsService.Instance; // Returnerer via provider pattern en request scope instans af GoBasic.UmbracoServices.UmbracoService. var module = service.getitem<formsmodule>(new Id(12)); Returnerer et typestærkt FormsModule objekt. Den tilsvarende kode i Umbraco ville være : var node = new NodeFactory.Node(12); eller var document = new Document(12); Modulsystem Go Basics modulsystem er udviklet for at give redaktører en større fleksibilitet i forhold til opbygning af indhold. Et modul er basalt set en lille byggeklods som kan inkluderes på en eller flere sider. Der findes en række forskellige modultyper i Go Basic, bl.a. en nyhedsliste, en call-to-action box, et billedegalleri og en videoboks. Hver modultype har en række felter som gør, at det enkelte modul kan konfigureres på forskellige måder. Rent teknisk set, består et modul af en dokumenttype, en entitets klasse og en præsentationsklasse. Dokumenttypen skal nedarve fra GridModule dokumenttypen og skal navngives med postfix Module, f.eks. MultiBox Module. Det giver et dokumenttype alias som hedder følgende MultiBoxModule. Der skal ikke specificeres en template ved oprettelsen. Derudover tilføjer man de felter man har behov for, for at konfigurere modulet. Entitetsklassen skal nedarve fra GridModule entiteten, og navnet skal svare til dokumenttypens alias. Dette sker helt automatisk ved at køre kodegenereringen. For at tilgå de enkelte felter på modulet oprettes en partiel klasse, hvorpå felterne oprettes som properties. Præsentationsklassen kan være en CustomControl eller en UserControl. Der er pt. ikke understøttelse af xslt renderinger. Præsentationsklassen skal hedde entitetsklassens navn plus postfikset Rendering. Dvs. MultiBoxModuleRendering. Når et modul skal renderes leder systemet altså efter en præsentationsklasse som hedder modulets navn + Rendering. Bemærk! Hvis man benytter sig af user controls til at implementere præsentationsklassen, skal den oprettes under /Modules folderen i roden af website projektet. Eksempel på tilføjelse af et modul I det følgende eksempel tager vi udgangspunkt i at benytte custom controls til at implementere et Hello World modul. Opret dokumenttypen Hello World Module. Log ind i Umbraco som administrator. Vælg settings sektionen i venstre side i bunden. 5
Højreklik på Grid Module dokumenttypen og vælg Create Indtast navnet Hello World Module, fjern krydset i Create matching template, og klik Create. På infofanen kan du vælge et ikon til modulet, som gør det nemmere at lokalisere moduler i grid editoren. Nye ikoner kan tilføjes til Umbraco ved at tilføje et 16x16.png billede til ~/umbraco/images/umbraco folderen. Benyt et af de ikoner som er depricated. 6
På tabs fanen tilføjes en ny tab Content. 7
På Generic properties fanen tilføjes et felt Message af typen textstring som placeres under Content. Her kan man angive en hjælpetekst til redaktøren. Bemærk at Umbraco automatisk laver et alias kaldet message. I Visual Studio åbnes filen Autogenerated.tt i GoBasic.Logic projektet under Entities folderen. Tryk ctrl+s for at aktivere autogenereringen af koden. Åbn Autogenerated.cs (under.tt filen), og verificer at der er dannet en HelloWorldModule klasse. Dette er entitets-klassen. For at tilføje vores Message property, oprettes en ny klasse under Entities-folderen kaldet HelloWorldModule med følgende kode ( bemærk at klassen skal være public partial ): using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GoBasic.Logic.Entities /// <summary> /// This is the entity class for the HelloWorld module /// </summary> public partial class HelloWorldModule /// <summary> /// The message to display /// </summary> public string Message 8
// Use the alias name get return GetValue<string>("message"); Klassen extender den autogenererede klasse med Message propertien. Opret en ny klasse HelloWorldModuleRendering under Modules folderen i GoBasic.Logic projektet, med følgende kode: using System; using System.Collections.Generic; using System.Linq; using System.Text; using GoBasic.Logic.Entities; namespace GoBasic.Logic.Modules /// <summary> /// This is the presentation class for the HelloWorld module /// </summary> public class HelloWorldModuleRendering : BaseModuleRendering<HelloWorldModule> /// <summary> /// This property is currently not being used, but is reserved for future use. /// </summary> public override string ModuleDescription get return "A module for outputting a message"; /// <summary> /// This method implements the rendering of the module. /// </summary> /// <param name="module">a strongly typed reference to the entity being rendered</param> /// <param name="writer">an advanced html writer with many capabilities</param> protected override void RenderModule(HelloWorldModule module, LinqIt.Utils.Web.HtmlWriter writer) writer.renderfulltag(htmltextwritertag.p, module.message); Byg projektet og test modulet på en given side i Umbraco. Moduler og Cookies Go Basic implementerer en godkendt løsning på krav om cookie-beskyttelse. Dette afsnit handler om, hvordan man sikrer, at nyudviklede moduler, som benytter cookies, overholder implementeringen. 9
For at fortælle systemet at et modul benytter cookies, tilføjes interfacet IRequiresCookies til præsentationsklassen. Herefter overrides RegisterScripts metoden for at inkludere eventuelle scripts som skal inkluderes på siden. RegisterScripts og RenderModule bliver kun kaldt på modulet hvis cookies er accepteret. I stedet renderes en standard boks som fortæller brugeren at cookies ikke er aktiveret, samt et link til cookie informationssiden, hvor det er muligt at aktivere cookies. Her ses koden for Google Maps modulet, som overholder cookiepolitikken. using System.Web.UI; using GoBasic.Logic.Entities; using LinqIt.Ajax.Parsing; using GoBasic.Logic.Modules; using GoBasic.Logic.Utilities; namespace GoBasic.Logic.Modules public class GoogleMapModuleRendering : BaseModuleRendering<GoogleMapModule>, IRequiresCookies protected override void RegisterScripts() Assert.IsNotNull(Module); if (!Module.Latitude.HasValue!Module.Longitude.HasValue) Visible = false; return; if (!Page.ClientScript.IsClientScriptIncludeRegistered(Page.GetType(), "googlemapsapi")) Page.ClientScript.RegisterClientScriptInclude(Page.GetType(), "googlemapsapi", "http://maps.googleapis.com/maps/api/js?sensor=false"); const int defaultzoom = 14; ModuleScripts.RegisterInitScript("googlemaps", new JSONString(ClientID), new JSONNumber(Module.Latitude.Value), new JSONNumber(Module.Longitude.Value), new JSONNumber(Module.Zoom?? defaultzoom), new JSONBoolean(Module.ShowMarker)); protected override void RenderModule(GoogleMapModule item, LinqIt.Utils.Web.HtmlWriter writer) if (!Visible) return; base.rendermodule(item, writer); writer.addattribute(htmltextwriterattribute.id, ClientID); writer.addstyle(htmltextwriterstyle.width, "100%"); writer.renderbegintag(htmltextwritertag.div); writer.renderendtag(); public override string ModuleDescription get return "A module displaying a google map"; 10
Site Status Site status er en feature, som giver administratorer mulighed for nemt og hurtigt at se, hvilke komponenter der er konfigureret for de forskellige sites i en Go Basic løsning. Det er også meningen, at man herfra nemt skal kunne konfigurere komponenterne uden at behøve at foretage alle de manuelle operationer, det ellers ville kræve. En komponent kan være alt lige fra hånderingen af 404-fejl, til konfigurationselementer til videoafsplining osv. En komponent kan også være et tredjepartsmodul som Go Basic ikke kender til, og som kræver oprettelse af forskellige items i sitestrukturen for at fungere. Det skal altså være muligt for tredjepart, at tilføje sektioner til statussiden. Arkitekturen omkring sitestatus fungerer således, at systemet scanner de inkluderede assemblies efter klasser som implementerer ISiteComponent interfacet. public interface ISiteComponent string Name get; void Initialize(Document siteroot); SiteComponentState State get; void InstantiateIn(ControlCollection controls); Name propertien benyttes til at udskrive komponententens navn. Initialize metoden har til formål at initializere State propertien for det givne site. State propertien kan antage følgende værdier : Ok, warning, disabled. Ok betyder at komponenten er konfigureret og virker. Warning betyder at komponenten er obligatorisk og ikke konfigureret, eller at komponenten kun er delvist konfigureret. Disabled betyder at komponenten ikke er konfigureret, og at komponenten ikke er obligatorisk (eller vigtig). I InstantiateIn metoden kan man oprette og tilføje diverse kontroller til outputtet, som f.eks. en label som beskriver status, eller en knap til at udføre en kommando. Denne løsning giver en stor fleksibilitet i forhold til, hvilke kontroller man vil vise, men det kan være lidt omstændigt at implementere interfacet. I de fleste tilfælde kan man med fordel nedarve sin klasse fra BaseSiteComponent, som implementerer ISiteComponent, og som har en række hjælpemetoder til forskellige formål. Alle indbyggede Go Basic komponenter nedarver fra denne klasse. Følgende eksempel viser, hvordan News Archive komponentens state er implementeret (simplificeret en smule her for overskuelighedens skyld ). Nyhedsarkivet kræver, at der findes en side af typen NewsListPage et sted under home item et, og at der findes et systemlink kaldet NewsArchivePage for sitet, som peger på denne. 11
using System; using System.Collections.Generic; using System.Linq; using System.Text; using LinqIt.Cms; using GoBasic.Interfaces.Enumerations; using GoBasic.Logic.Exceptions; using GoBasic.Logic.Utilities; using GoBasic.Logic.Entities; namespace GoBasic.Logic.Controllers.SiteManagement public class NewsArchiveComponent : BaseSiteComponent public override string Name get return "News Archive"; protected override void Initialize() // Check if a page exists, which is linked to by a system link named "NewsArchivePage" var newsarchivepage = GetSiteLinkedEntity<NewsListPage>(SystemKey.NewsArchivePage); if (newsarchivepage == null) // Notify that the component is disabled ( this is not a required component ) // Provide a button click handler and text for the "Fix it" button. throw new SiteComponentException("The News archive is not yet enabled.", SiteComponentState.Disabled, OnSetupClicked, "Enable news archive"); // If no exception was thrown, the page and systemlink is configured correctly. AddMessage("The News archive has been setup correctly."); // This code will execute when the "Fix It" button is clicked, and should setup the newsarchive protected void OnSetupClicked(object sender, EventArgs e) using (CmsContext.Editing) // Get a reference to the home page (This snippet assumes that the home page has been configured.) var homeitem = GetSiteLinkedEntity<WebPage>(SystemKey.HomePage); // Create a newlistpage called "News Archive" under the home item var resultpage = CmsService.Instance.CreateEntity<NewsListPage>("News Archive", homeitem); EnsureSiteSystemLink(SystemKey.NewsArchivePage, resultpage); ReloadEditor(); En af de væsentlige ting som foretages af BaseSiteComponent klassen er at fange den SiteComponentException, som kastes under initialize fasen, og generere en label med fejlbeskeden, samt en knap som administratoren kan benytte til at løse problemerne. NewsArchiveComponent klassen behøver derfor kun at implementere forretningslogikken, og nedarver præsentationskoden. 12
Installation af Go Basic Go Basic udbydes som en enkelt zip-fil, som indeholder et visual studio projekt, samt et script til oprettelse af databasen. Det er ikke nødvendigt at installere Umbraco, da det er indeholdt i pakken. Databasescriptet findes i folderen scripts. Følg disse trin for at installere Go Basic i udviklermiljøet: Opsætning af website: 1. Udpak zipfilen et sted på harddisken. 2. Opret et nyt site i IIS7 som peger på GoBasic.WebSite folderen du netop har udpakket. 3. Ret app poolen så den kører.net framework v.4.0, og integrated pipeline mode. Notér kontonavnet for app poolen, og ret evt. til Network Service. 4. Ret sikkerhedsindstillinger på GoBasic.WebSite folderen, så ovenstående konto (f.eks. Network Service) samt IUSR kontoen har læse og skrive adgang. 5. Opret evt. en loopback entry i hosts filen så dit hostnavn peger på din maskine (sti: c:\windows\system32\drivers\etc\hosts, tilføj linje 127.0.0.1 <dit hostnavn> Database : 1. Opret nyt login på database-serveren 2. Opret en ny database 3. Eksekver setup scriptet på databasen 4. Tilføj din login account til databasen og tildel rettigheder 5. Åbn web.config og ret appsetting en umbracodbdsn i web.config til at pege på din database, og med dit kontonavn / kodeord 6. Ret også appsettingén luceneindexfolder i web.config til at pege ned på den udpakkede sti Kompilér løsningen og log ind i umbraco på adressen http://<dit hostnavn>/umbraco/. Dit brugernavn er admin og kodeordet password. 13