Blogg om Umbraco och CMS

Umbraco 4.7 - Razor med Intellisense

Nya Umbraco 4.7 kommer med en ny view engine, Razor. Scott Guthrie skrev tidigare om Razor och att det kommer bli en stor del av .net och mvc framöver. Nu fungerar det även med Umbraco!

Razor med Umbraco är snabbt och enkelt att komma igång med. Kör bara igång en ny installation av Umbraco 4.7, gå till developer-sektionen och högerklicka på "Scription Files". Följande dialog dyker upp:

Skapa ett Razor Macro

Välj "Cshtml by Razor Macro Engine" och eventuellt en mall att börja med. Nu skapas ett nytt Razor Macro.

Problemet för mig var att när jag öppnade upp det i Visual Studio 2010 fick jag ingen intellisense. Intellisense för Razor finns i ASP.NET MVC 3 som man kan installera med Web Platform Installer. Det var den enkla biten. Det krångliga är att när jag försökte skriva något, så fungerade det så långt som @Model, men jag fick inte upp någon intellisense på vilka properties som min @Model har. Problemet är att @Model är definerat som Dynamic.

Hur får jag intellisense till min @model?

Det enkla svaret är att göra om Dynamic till riktiga objekt. Vi börjar med att använda Umbraco.MacroEngine högst upp i vårt Macro

@using umbraco.MacroEngines;

När vi gjort det så är det det dags att definera vad vår @Model är. I det här fallet står vår @Model för en viss sida CurrentPage och det vi vill få ut är ancestors till sidan. Så istället för var i vår foreach-loop sätter vi in att vi får ut DynamicNode (Umbraco Razors motsvarighet till Node). Resultatet blir att vi får intellisense för Level-variabeln:

Razor Intellisense

Det går såklart att gå steget längre och definiera @Model med en variabel också, men det här får börja.

Mer om Umbraco Razor senare

den 7 mars 2011

Umbraco 4.6 Juno släppt i Microsoft Web Platform Installer WPI

Året 2011 börjar med att Umbraco släpps i version 4.6 (aktuell version i skrivandets stund är 4.6.1) aka Umbraco Juno.

De sista buggarna från beta-releasen har blivit åtgärdade och den nya versionen är släppt och går att ladda ner via Windows Web Platform Installer vilket gör det helt sanslöst enkelt att komma igång.

sewen-wpi-umbraco

 

den 13 januari 2011

Umbraco släpper Juno, nya version 4.6 Beta

I helgen släpptes Umbraco Juno, nya version 4.6 Beta.

Releasen är fokuserad på ett förbättrat användargränssnitt med fokus på första-gången-användare av Umbraco. Installationsprocessen har fått en rejäl ansiktslyftning och några nya funktioner.

umbraco_46_installation

Umbraco Juno - Installationprocessen

Inbyggd databas - SQL CE 4.0

Nytt i Umbraco Juno är att man kan välja en inbyggd databas. Den stora fördelen med SQL CE 4.0 är att den fungerar i en medium-trust miljö. Det gör att valfriheten av webhotell kommer vara i stort sett obegränsad. Även version 4.5 hade stöd för medium-trust, men eftersom man behövde en extern databas blev det ändå något av en tröskel.

Starter kits och skins

För att snabbt och enkelt komma igång med en ny webbplats introducerar Umbraco Juno Starter kits och skins. Ett starter kit är ett paket av färdiga mallar och sidor (Simple, Blog, Personal och Business). För att sedan göra dessa personliga kan man välja olika skins, dvs utseende på sina mallar. Detta är något som kommer vara guld värt för första-gångs-användare.

umbraco_46_skins

Umbraco Juno - Starter kits och Skins

För utvecklare

Även utvecklare har fått massor av ny funktionalitet för att underlätta exempelvis skapandet av Custom Datatypes. Några av funktionerna är:

  • Default Data Editor Settings for Custom Data Types
  • Upgraded User Control Wrapper to support custom settings and xml return types
  • Support for Microsoft SQL CE 4.0 (CTP 2)
  • Support for Microsoft Razor Syntax
  • Standardize on .NET 4.0

Ladda ner senaste versionen av Umbraco Juno från Codeplex och testa.

den 27 december 2010

Ipad och Telenor SIM inställningar

den 17 december 2010

Grattis MatHem - Sveriges bästa matbutik på nätet, hemleverans av varor, färdig matkasse, mm

Internetworld utsåg i förra veckan Sveriges 100 bästa sajter. På en sjätteplats och utmärkelsen "Sveriges bästa matbutik" kom en sida som Sewen är inblandad i, mathem.se - Mat för hemleverans på nätet.

MatHem - Mat för hemleverans

MatHem levererar för närvarande i Stockholm och Uppsala, men kommer inom kort täcka in andra områden i Sverige också. Förutom att beställa mat för hemleverans kan du även beställa en färdig matkasse, handla direkt från ett recept du hittar samt annat smått och gott.

Sewen vill skicka ett STORT GRATTIS till alla som jobbar med MatHem och säga att det bara är att fortsätta kämpa!

den 22 november 2010

Umbraco 4 - Svensk manual för redaktörer

Matt Brailsford har kommit med det utmärkta initiativet att skapa en gemensam manual för redaktörer av Umbraco 4. Tack vare Umbraco communityt har den spritt sig och finns nu tillgänglig på fem olika språk. Däribland svenska.

Sewens senaste kunder har fått manualen i samband med överlämnande av webplatser. Det har varit tummen upp varenda gång.

Manualen är på 46 sidor och behandlar allt en redaktör behöver veta. Det är dels grundläggande om Umbraco samt ett avslutande kapitel med tips om något inte beter sig som det borde.

Bra jobbat ännu en gång Umbraco communityt.

Direktlänk till manualen på svenska»

Länk till projektet»

den 5 november 2010

Microsofts www.asp.net kör nu Umbraco CMS som publiceringssystem

Scott Guthrie skrev i tidigare att Microsofts egna utvecklarresurs www.asp.net skulle börja köra Umbraco CMS som publiceringssystem.

Nu har det blivit verklighet. En skön skalp för Umbraco när ett så stort och inflytelserikt företag som Microsoft själva väljer det som plattform.

Grattis Umbraco!

den 6 maj 2010

Medium Trust - Installera Umbraco på Binero

Rent traditionellt har inte Umbraco gått att installera i Medium Trust tidigare. Många webhotell använder Medium Trust för att skydda åtkomst till andra sajter på samma server. Att inte kunna installera Umbraco i Medium Trust har varit en stor nackdel. Nu är det möjligt! Teamet bakom Umbraco, den här gången med Benjamin Howarth i spetsen, har lagt ner stor möda och besvär för att reda ut vad det var som begränsade Umbraco från att köra i Medium Trust. Från version 4.1, som planeras att släppas någon gång i slutet av Q2, kommer Medium Trust stödjas från grunden. Tillvidare finns en lösning för att även köra 4.0.x.

Nedan tänkte jag gå igenom hur jag installerade Umbraco på Binero, som kör Medium Trust på sina windowsbaserade webbhotell. Efter det kommer jag föra ett kort resonemang varför Medium Trust är viktigt.

Umbraco på Binero

Bakgrund

Vi fick i uppdrag att bygga en mindre bloggportal för en av Sveriges stigande stjärnor inom komikerhimlen. Kunden hade redan tidigare webbhotell Privat (69:-/månad) hos Binero. Eftersom jag just läst om Umbraco i Medium Trust tyckte jag det här var ett utmärkt tillfälle att testa om det skulle fungera.

Installation

Först och främst satte vi upp ett lokalt projekt och utvecklade sajten precis som vanligt. När allt var klart och det såg snyggt ut på vår lokala installation, tog vi tag i problemet att få allt att lira på Binero.

Vi började med att teckna tilläggstjänsten MSSql-server (vi hade likaväl kunnat köra med MySql, men jag föredrar MSSql när det finns den möjligheten). Sedan publicerade vi vår databas till den nya MSSql-servern. Vi använder Visual Studios inbyggda funktion Server Explorer - Right click on database - Publish to provider för att snabbt och enkelt få ut en scriptad databas med allt innehåll.

Nu kunde vi köra vår lokala kopia mot databasen för att verifiera att allt innehåll kommit med som det ska.

Nästa steg var att ladda ner patchen för att kunna köra Umbraco i Medium Trust.

För säkerhets skull tog vi nu en kopia av hela projektet (jag erkänner och skäms; vi fuskade genom att inte använda versionshantering). Sedan ersatte vi alla filer i vår lösning med dem från patchen.

OBS! Eftersom patchen innehåller hela config-mappen, var noga med att inte skriva över ändringar du gjort själv i config-filerna.

Även web.config är nu överskriven. Det vi gjorde var att ändra tillbaka

  1. umbracoDbDSN - till den connectionstring som ansluter till MSSql hos Binero
  2. umbracoConfigurationStatus - till den Umbraco-version vi använder (value="4.0.3")

Sedan var det dags att ladda upp vår lösning till Binero.

Problem 1 - Integrated Pipe-line mode

Vi trodde (läs: hoppades) nu att vi skulle ha en färdig lösning. Men det första vi stötte på var ett problem med Integrated Pipe-lines som är default i IIS 7. Tidigare har jag löst det genom att sätta Classic mode i Application Pool, men eftersom vi tänkte köra på Binero som det är utan att krångla gav vi oss på att vi skulle hitta en lösning för det.

Dum som jag är ibland, öppnade jag web.config direkt från notepad++ (som iofs är en utmärkt editor) och försökte editera därifrån. Av någon anledning har jag då ingen färgkodning för config-filer, trots att de egentligen är vanliga (?) xml-filer. Jag gissar att det är en enkel inställning och att notepad++ kan lösa det, men jag har inte känt något behov av det tidigare. I alla fall, det jag missade, var att hela stycket som gör att Umbraco fungerar i Integrated Pipe-line mode är bortkommenterat per default. Jag slet mitt hår i en timme eller så innan jag förstod vad felet var.

Problem 2 - msxml:script

Msxml:script är ett sätt att skriva .net-kod inline i en xslt-fil. Det finns inget sätt (känt än så länge i alla fall) att komma runt det. För min del ser jag det inte som något problem, eftersom det är väldigt enkelt att skriva egna extensions till xslt i kompilerade .net-filer. Men, det gör så att vissa befintliga paket inte fungerar som de ska. Det utomordentliga paketet XSLTSearch använder sig till stor del av msxml:script. Vår enkla lösning var att ta bort det så länge. Vi hade inga krav på oss att ha med en sökning på den här webbplatsen. En annan lösning hade varit att flytta över alla msxml:script i en kompilerad dll och helt enkelt skriva om XSLTSearch. Dock tror jag att det finns andra i Umbraco communityt som redan har det på sin agenda.

Egentligen stötte vi på ett problem till. Det var när vi laddade upp allt så klagade den på att den inte kunde köra några xslt-macron. Det felet har jag redan påpekat tidigare. Vår xsltExtensions.config blev överskriven av patchen, vilket gjorde att xslt-macrona inte kände igen vårt library som vi försökte implementera. Det löstes enkelt genom att kopiera in vår gamla config-fil.

Fördelarna med Medium Trust

Den stora anledningen till varför man vill att Umbraco (eller vilken annan webbapplikation som helst) ska köra på Medium Trust är att troligtvis 90% av världens alla webbsidor befinner sig i Medium Trust. Konkurrerande open source CMS som Joomla, Wordpress och Drupal har inga problem med Medium Trust. Alltså, missar man en otroligt stor del av marknaden om det inte går att installera applikationen.

Anledningen till att webbhotellen väljer att köra Medium Trust på sina servrar är helt enkelt för att skydda kod. Din kod från de andra som du delar server med. De andra från din kod.

En tredje fördel med Medium Trust är även att det tvingar dig som utvecklare att skriva snyggare kod. En kod som är anpassad för en säker miljö, där du inte har möjlighet att sänka en server om något skulle gå fel.

Det är alltså

  1. Billigare
  2. Säkrare
  3. Bättre kod

Sammanfattning

Umbraco har nu gått in i ytterligare en utvecklingsfas där man nu helt plötsligt kan installera Umbraco i snart vilken miljö som helst. Eftersom Umbraco även har/kommer ha fullt stöd för Mono-plattformen är det helt klart ett CMS att räkna med i framtiden. Linux och Medium Trust är inga hinder längre.

Det är dags att ta upp kampen på riktigt med de populära php-CMS:en.

 

 

den 7 april 2010

Skapa en Custom section i Umbraco (steg 2)

Det här är steg 2 i en serie om hur man skapar en Custom section i Umbraco. Läs första delen här. I steg 1 skapade vi en ny nod i User-sektionen och mappar användare till en eller flera MemberGroups.

I den här delen kommer vi skapa en Custom section som heter CustomMember. Den kommer påminna om den Member-sektion som redan finns i Umbraco, men endast lista medlemmar från de grupper som du är mappad till (se steg 1).

Problemformulering

  1. Skapa en ny CustomMember-section
  2. Lägga till en nod som tillåter oss att
    1. Lägga till nya medlem (mappad till samma grupper som vår användare)
    2. Lista befintliga medlemmar
    3. Editera befintlig medlem
    4. Ta bort medlem

Lägga till en Custom section

Eftersom jag redan i steg 1 förklarat hur vilka tabeller i Umbraco databasen som påverkar sektioner kommer jag sätta igång direkt.

Först lägger vi till en ny sektion

INSERT INTO umbracoApp (sortOrder, appAlias, appName, appIcon)
VALUES (9,'customMember','CustomMember','.traymember')

Vi döper sektionen till customMember, säger att den ska ha css-klassen traymember och lägga sig på plats 9 (sist) i sektionsmenyn. För appIcon kan man antingen ange en bild-fil direkt eller en css-klass. Anger man en bild kommer Umbraco leta efter bilden i mappen "~/umbraco/images/tray/". Anger man en css-klass behöver man uppdatera någon av de css:er som Umbraco admin användare i sin backend. De återfinns i mappen "~/umbraco/css/". Eftersom våra användare aldrig kommer ha tillgång till båda Member-sektionerna samtidigt ger vi den samma css-klass som Member-sektionen.

För att sektionen ska dyka upp i Umbraco admin behöver vi säga att vår användare ska ges tillgång till sektionen. Gå till Users-sektionen och leta upp din användare. Under Sections ska en ny sektion [customMember] dykt upp. Anledningen att den ser konstig ut är att den letar efter en nyckel i Umbracos språkfiler. Vi öppnar filen "/umbraco/config/lang/en.xml" (eller det språk du har inställt att Umbraco admin använder. Där finns en <area alias="sections">. Lägg till en ny rad där

<key alias="customMember">Custom members</key>

Klicka sedan på din användare igen i Umbraco admin så ser du att den tar översättningen från din nya nyckel.

Vi klickar för att vår användare ska se vår CustomMembers-sektion och sparar. När vi sedan uppdaterar sidan (ctrl-F5) ska vår nya sektion dyka upp.

Nästa steg blir att lägg till ett träd till vår nya sektion så att något händer när man klickar på den.

INSERT INTO umbracoAppTree (treeSilent, treeInitialize, treeSortOrder, appAlias, treeAlias, treeTitle, treeIconClosed, treeIconOpen, treeHandlerAssembly, treeHandlerType)

VALUES ('false', 'true', 0, 'customMember', 'Member', 'Member', 'folder.gif', 'folder_o.gif', 'umbBoRatt.Umbraco.Sections.CustomMember', 'LoadMembers')

Vi har sagt att vår första nod ska heta Member, ha den vanliga folder-ikonen och laddas från klassen LoadMembers i namespace umbBoRatt.Umbraco.Sections.CustomMember

Eftersom vi redan bestämt att det gränssnittet ska likna den Member-sektion som redan finns gör vi så att vi använder den som mall. Vi öppnar filen "/umbraco/presentation/umbraco/Trees/loadMembers.cs" från Umbracos källkod. Vi börjar med att kopiera hela källkoden till vår nya klass och bygger vår lösning. När vi sedan öppnar vår CustomMember-sektion laddas hela användarträdet. Vi behöver ändra i koden för att endast lista medlemmar från grupper som vi är mappade till.

Vi lägger till följande metod

        private bool memberInGroup(int memberId)
        {
            umbBoRatt.Core.Controllers.UserMemberGroupController controller = new umbBoRatt.Core.Controllers.UserMemberGroupController(new umbBoRatt.Core.Models.UserMemberGroupRepository(GlobalSettings.DbDSN));

            Member m = new Member(memberId);

            int userId = umbraco.BasePages.UmbracoEnsuredPage.CurrentUser.Id;

            foreach (object value in m.Groups.Keys)
            {
                int groupId;
                if (int.TryParse(value.ToString(), out groupId) && controller.HasGroup(userId, groupId))
                {
                    return true;
                }
            }
            
            return false;
        }

Och på rad 142 kör vi en if-sats för att kolla om medlemmen tillhör gruppen

if (memberInGroup(m.Id)) {

När vi byggt den koden borde vi få upp en listning av alla medlemmar som tillhör endast vår egen grupp.

OBS! För att inte behöva jobba med sökningar av medlemmar, det är inget krav för oss ännu, kommenterar vi bort den noden från trädet.

Skapa nya medlemmar

Nästa punkt blir att skapa nya medlemmar. Umbracos inbyggda funktion för att skapa nya medlemmar duger gott åt oss. Den enda ändringen vi behöver göra är att när vi skapat medlemmen ska vi lägga till en grupp till den. För att göra det använder vi Umbracos Event Model. Mer om det senare.

Umbraco använder UserControls för de olika event som sker vid högerklick på en nod. Mappningarna sker i filen "~/Umbraco/config/create/UI.xml". Vi hittar:

<nodeType alias="initmember">
<header>Member </ header>
<usercontrol>/create/member.ascx </usercontrol>
<tasks> < delete assembly="umbraco" type="memberTasks" /></tasks >
</nodeType>

Vi säger nu att när en create med alias initmember (definerat i CreateRootNode-metoden i LoadMembers-klass) uppstår, öppnar vi kontrollen "/umbraco/create/member.ascx". Vi måste nu bara se till att vårt alias verkligen stämmer överens med det skrivna ovan. Vi ändrar koden till:

rootNode.NodeType = "init" + TreeAlias.ToLower();

Hade vi velat göra vårt eget event behöver vi lägga till en ny <nodeType> med vårt eget unika alias och kalla på en egen custom UserControl. Dessutom om vi vill skapa våra egna tasks, måste vi göra en ny klass som implementerar: umbraco.interfaces.ITaskReturnUrl

Editera medlemmar

I vår LoadMember-klass har vi definerat vad som ska hända när vi klickar på en Medlem. Umbraco kommer automatiskt att trigga det javascript som står i action-attributet.

treeElement.SetAttribute("action", "javascript:openMember('" +

HttpContext.Current.Server.UrlEncode(u.UserName) + "');");

Vi säger att vi kallar på javascriptet openMember(id). I RenderJS-metoden definerar vi javascriptet som:

        public override void RenderJS(ref StringBuilder Javascript)
        {
            Javascript.Append(
                @"
                    function openMember(id) {
                        parent.right.document.location.href = 'plugins/CustomMember/editMember.aspx?id=' + id;
                    }

                    function openContentItem(id) {
                        parent.right.document.location.href = 'ContentItem/edit.aspx?id=' + id;
                    }
                    ");
        }

Vi säger alltså till Umbraco att när man klickar på en Medlem, öppna i din högra Frame editMember.aspx som ligger i "\umbraco\plugins\CustomMember". Vi behöver alltså skapa sökväg och sida.

Jag valde länge här mellan att använda samma kontroll för att editera medlemmar som Umbraco redan använder, eller att skapa en egen kontroll. Jag kom fram till att jag inte vill att mina användare ska kunna byta grupper på medlemmarna. Alltså vill jag ha en sida som ser ut exakt som den vanliga editera-medlemmar sidan i Umbraco, men utan grupper.

Jag kopierade hela sidan som Umbraco använder. Sen la jag till ett Javascript längst upp som gömmer grupperna. Det kan diskuteras om det här är rätt sätt att göra det på, och jag skulle vara den första att erkänna att det kanske inte är det bästa. Men det är tillräckligt bra för min lösning och det är garanterat den snabbaste, godtagbara lösningen. Märker vi framöver att den inte är acceptabel kan vi ändra det då.

Javascriptet för att "ta bort" grupper:

//Javascript function to remove groups (Users shouldn't be able to save groups)
        $(document).ready(function() {
            var node = $('#ctl00_body_Membergroups')[0];
            $(node.parentNode.parentNode.parentNode.parentNode).remove();
        });

Ta bort medlemmar

Eftersom vi valde att använda samma <nodeType> som Umbracos Medlem-sektion redan använder, fick vi funktionen att ta bort medlemmar gratis. Vill man kan man skapa sina egna events i en egen klass. Jag nämnde det förut, men nämnder det igen att kravet då är att man ärver från umbraco.interfaces.ITaskReturnUrl.

Slutsats

Det här är mer eller mindre det jag tänkte gå igenom i den här delen.

Vi har lärt oss hur man skapar upp en ny sektion. Hur man lägger till en huvudnod och subnoder till sektionen. Vi har även tummat på hur man skapar events för noderna. I kommande del kommer jag gå igenom hur man skapar sina egna event med egna tasks.

den 30 mars 2010

Skapa en custom section i Umbraco (Steg 1)

Bakgrund:

Vi har en sajt för föreningar. Föreningarna ser bara sin egen förenings innehåll i Content-sektionen. Nu har det kommit en förfrågan från flera föreningar att de vill kunna lägga till sina medlemmar och ha en inloggad del. Vi vill inte ge administratörerna för de olika föreningarna direkt tillgång till Umbracos member-sektion eftersom de då skulle se alla föreningars medlemmar. Alltså faller valet på att skapa en ny custom section där de kan administrera den egna föreningens medlemmar. Vi tyckte det här var ett ypperligt tillfälle att dela med oss hur vi skapar en custom section.

Problemformulering:

  1. I Users-sektionen lägger vi till en User -> MemberGroup - mappning.
  2. Skapa en ny CustomMember-sektion som liknar den Member-sektion som redan finns i Umbraco (CRUD).

Om Umbraco databasen:

Sektioner i Umbraco hanteras av två databastabeller.

umbracoApp

Består av Krasst ansvarar umbracoApp för visningen av menyn för sektionerna i Umbraco admin. Tabellen består av fem kolumner som beskrivs nedan. De flesta har med just visningen att göra, men appAlias-kolumnen är värd att lägga på minnet för den kommer spela roll i vår nästa tabell och i config.

umbracoAppTree

När en sektion laddas så är det den här tabellen som ansvarar för vad som kommer dyka upp i Content-menyn.

För att läsa mer om tabellerna gå till http://www.geckonewmedia.com/blog/2009/8/3/how-to-create-a-custom-section-in-umbraco-4

Eftersom vår första uppgift var att lägga till en User -> MemberGroup - mappning i Users-sektionen som redan finns så är det umbracoAppTree som vi ska lägga till en ny rad i.

INSERT INTO umbracoAppTree

(treeSilent, treeInitialize, treeSortOrder, appAlias, treeAlias, treeTitle, treeIconClosed, treeIconOpen, treeHandlerAssembly, treeHandlerType)

VALUES ('false', 'true', 3, 'users', 'userGroupMapping', 'User Group mapping', 'folder.gif', 'folder_o.gif', 'umbBoRatt.Umbraco.Sections.Users', 'LoadUserGroupMappings')

Vi väljer att inte krångla till det med design här, eftersom det bara kommer vara sajtens administratörer som ser det. Vi ser till att våra andra ikoner ser snyggare ut framöver.

TIPS! Umbraco förutsätter att tree-ikonerna ligger i mappen "~/umbraco/images/umbraco/"

Det vi nu har sagt till Umbraco är att när den laddar Users-sektionen ska den lägga till en ny folder med namnet "User Group mapping". För att veta folderns innehåll ska den ladda classen LoadUserGroupMapping med namespace umbBoratt.Umbraco.Sections.Users. Alltså får vi se till att skapa den klassen.

OBS! Om Umbraco inte hittar ditt namespace eller klassen kommer det inte synas något i admin. Kontrollera referenser, namespace och klassnamn en gång till. När du gjort ändringar i databasen, se till att röra web.config för att starta om applikationen.

BaseTree

Nu kommer det härligaste med Umbraco. Eftersom Umbraco är Open Source behöver man aldrig gissa sig fram till hur man ska göra. Det finns alltid ett exempel att ta av. I det här fallet ska vårt nya träd lista användare. Vi har redan ett likadant träd som listar användare. Vi ser i databasen att klassen ska heta loadUsers. Vi gör en sökning i Umbracos källkod och finner klassen i mappen "/umbraco/presentations/umbraco/trees/". Vi använder källkoden från den filen med några smärre förändringar.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using umbraco.cms.presentation.Trees;
using umbraco.BusinessLogic.Actions;
using umbraco.interfaces;
using umbraco.BusinessLogic;
using umbraco.BasePages;

namespace umbBoRatt.Umbraco.Sections.Users
{
    public class LoadUserGroupMappings : BaseTree
    {
        public LoadUserGroupMappings(string application) : base(application) { }

        protected override void CreateRootNode(ref XmlTreeNode rootNode)
        {
            rootNode.Icon = FolderIcon;
            rootNode.OpenIcon = FolderIconOpen;
            rootNode.NodeType = "init" + TreeAlias;
            rootNode.NodeID = "init";
            rootNode.Action = "";
        }

        protected override void CreateRootNodeActions(ref List actions)
        {
            actions.Clear();
            actions.Add(ActionRefresh.Instance);
        }

        protected override void CreateAllowedActions(ref List actions)
        {
            actions.Clear();
        }

        public override void Render(ref XmlTree tree)
        {
            User[] users = User.getAll();

            User currUser = UmbracoEnsuredPage.CurrentUser;

            bool currUserIsAdmin = currUser.IsAdmin();
            foreach (User u in users)
            {
                XmlTreeNode xNode = XmlTreeNode.Create(this);

                // special check for ROOT user
                if (u.Id == 0)
                {
                    //if its the administrator, don't create a menu
                    xNode.Menu = null;
                    //if the current user is not the administrator, then don't add this node.
                    if (currUser.Id != 0)
                        continue;
                }
                // Special check for admins in general (only show admins to admins)
                else if (!currUserIsAdmin && u.IsAdmin())
                {
                    continue;
                }

                if (u.Disabled)
                    xNode.IconClass = "umbraco-tree-icon-grey";

                xNode.NodeID = u.Id.ToString();
                xNode.Text = u.Name;
                xNode.Action = "javascript:openUserMemberGroupMappings(" + u.Id + ");";
                xNode.Icon = "user.gif";
                xNode.OpenIcon = "user.gif";

                tree.Add(xNode);
            }
        }

        public override void RenderJS(ref StringBuilder Javascript)
        {
            Javascript.Append(
               @"
                    function openUserMemberGroupMappings(id) {
                        parent.right.document.location.href = 'plugins/CustomMember/Users/editUserMemberGroupMappings.aspx?id=' + id;
                    }
                ");
        }
    }
}

Förklaringar till BaseTree

Vår klass ärver från BaseTree. Vi har valt att override ett antal metoder. Jag kommer förklara deras funktion en och en.

CreateRootNode: I vårt träd har vi en rootNode. Vi kan göra ändringar i den noden i CreateRootNode metoden. Vi väljer ikon, sätter ett id och en eventuell action (kommer kalla på ett eget javascript om det är satt). rootNode.NodeType är viktig om man ska koppla Create-event till sin nod. Vi kommer gå igenom det närmre när vi skapar vår CustomMember-sektion.

CreateRootNodeActions: Definierar vad som ska hända när man högerklickar på en nod i trädet. I vårt fall tillåter vi endast att man kan uppdatera trädet.

CreateAllowedActions: Definerar vilka actions som kommer vara tillåtna för vår sub-noder. I vårt fall vill vi bara att man ska kunna klicka på varje användare och därifrån definiera User -> MemberGroup - mappningen, därför tömmer vi sub-noderna på actions.

Render: När man väljer att expandera trädet kommer Render metoden att kallas på. Det är här man skapar sub-noder till trädstrukturen. I vårt fall hämtar vi alla Users. Efter några kontroller om det är admin-användare eller inte som är inloggad tar vi var och en av våra Users och skapar noder av dessa.

foreach (User u in users)

{

XmlTreeNode xNode = XmlTreeNode.Create(this);

För varje nod väljer vi dessutom ikoner, text och en action (ett javascript som kallas på vid klick på noden). Slutligen lägger vi till noden till vår rootNode .

tree.Add(xNode);

RenderJS: Kommer lägga till ett javascript till sidan, som vi sedan kan kalla på via våra actions från noderna.

Den stora förändringen vi gjort ligger i vad som ska hända när man klickar på en användare.

xNode.Action = "javascript:openUserMemberGroupMappings(" + u.Id + ");";

Ovan har vi har definerat att när man klickar på en medlem så kommer ett javascript openMemberGroupMappings att anropas. I vår RenderJS metod definerar vi det javascriptet. Det är så enkelt att det öppnar en aspx-fil i Umbraco admins stora iFrame. Nu är det dags att skapa den aspx-filen.

UI

Eftersom vi vill att vårt gränssnitt ska påminna så mycket som möjligt om Umbracos eget gränssnitt börjar vi med att ta lite hjälp. I loadUsers-klassen ser vi att den kallar på en fil som heter editUser.aspx. Vi öppnar den och får vår grunddesign. Vi importerar umbraco.ui och får tillgång till kontroller som har ett native Umbraco utseende.

<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>

Se hela front-end koden här:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="editUserMemberGroupMappings.aspx.cs" MasterPageFile="/umbraco/masterpages/umbracoPage.Master" Inherits="umbBoRatt.Web.umbraco.plugins.CustomMember.Users.editUserMemberGroupMappings" %>

<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>
<asp:Content ContentPlaceHolderID="body" runat="server">
 <cc1:UmbracoPanel runat="server" ID="UserMemberGroupMappings" Text="User Member group mappings" hasMenu="true">
 <cc1:Pane ID="Pane1" runat="server">
 <cc1:PropertyPanel runat="server" ID="PropertyPanel2" Text="Member">
 <asp:Literal ID="lblMemberName" runat="server"></asp:Literal>
 </cc1:PropertyPanel>
 <cc1:PropertyPanel runat="server" ID="PropertyPanel1" Text="Member groups">
 <asp:CheckBoxList ID="memberGroups" runat="server" OnDataBound="memberGroups_DataBind">
 </asp:CheckBoxList>
 </cc1:PropertyPanel>
 </cc1:Pane>
 
</cc1:UmbracoPanel>
</asp:Content>

I page_load i vår back-end lägger vi till en spara-knapp till menyraden i vår UmbracoPanel. Sedan binder vi vår CheckBoxList med alla MemberGroups.

I spara-metoden itererar vi listan och sparar alla ikryssade MemberGroups. Vi ser även till att aktivera Umbraco admins inbyggda pratbubbla (speechBubble). Vi får tillgång till den genom att vi ärver från UmbracoEnsuredPage istället för den vanliga Page.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using umbraco.BasePages;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic.propertytype;
using umbraco.cms.businesslogic.web;
using umbraco.presentation.channels.businesslogic;
using umbraco.uicontrols;
using umbraco.providers;
using umbraco;
using umbraco.cms.businesslogic.member;

namespace umbBoRatt.Web.umbraco.plugins.CustomMember.Users
{
    public partial class editUserMemberGroupMappings : UmbracoEnsuredPage
    {
        public User pageUser { get { return new User(int.Parse(Request["id"])); } }
        private umbBoRatt.Core.Controllers.UserMemberGroupController Controller;
         

        protected void Page_Load(object sender, EventArgs e)
        {
            Controller = new umbBoRatt.Core.Controllers.UserMemberGroupController(new umbBoRatt.Core.Models.UserMemberGroupRepository(GlobalSettings.DbDSN));

            //Add save to menu
            MenuImageButton save = UserMemberGroupMappings.Menu.NewImageButton();
            save.ImageUrl = GlobalSettings.Path + "/images/editor/save.gif";
            save.Click += new ImageClickEventHandler(saveUser_Click);

            if (!Page.IsPostBack)
            {
                lblMemberName.Text = pageUser.Name;

                //Bind all member groups
                memberGroups.DataSource = MemberGroup.GetAll;
                memberGroups.DataTextField = "Text";
                memberGroups.DataValueField = "Id";
                memberGroups.DataBind();
            }
        }

        /// 
        /// Handles the databind event of the list control.
        /// 
        /// The source of the event.
        /// The  instance containing the event data.
        protected void memberGroups_DataBind(object sender, EventArgs e)
        {
            int userId = pageUser.Id;
            foreach (ListItem li in memberGroups.Items)
            {
                li.Selected = Controller.HasGroup(userId, int.Parse(li.Value));
            }
        }

        /// 
        /// Handles the Click event of the saveUser control.
        /// 
        /// The source of the event.
        /// The  instance containing the event data.
        private void saveUser_Click(object sender, ImageClickEventArgs e)
        {
            //1) Remove all groups for user
            //2) Foreach li.selected add group to user
            //3) Show speechbubble

            int userId = pageUser.Id;
            Controller.RemoveAllGroups(userId);

            foreach (ListItem li in memberGroups.Items)
            {
                if (li.Selected)
                {
                    Controller.AddMemberGroup(userId, int.Parse(li.Value));
                }
            }

            speechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editUserSaved", base.getUser()), "");     
        }
    }
}

Resultatet blir:

Sammanfattning:

Vi har gått igenom de tabeller som Umbraco använder för sektioner och trädstrukturen för sektionerna. Vi har även skaffat oss en övergripande förståelse för hur Umbraco admin använder klassen BaseTree för trädstrukturen i sektionerna. Med hjälp av blogginlägget http://www.geckonewmedia.com/blog/2009/8/3/how-to-create-a-custom-section-in-umbraco-4 kommer det inte vara något problem för dig som läsare att redan nu ge dig på att skapa en Custom Sections i Umbraco. Mer om det kommer i Steg 2 av serien att skapa en Custom Section i Umbraco.

den 26 februari 2010

Sewen bloggar om Umbraco

To english version of blog»

Kategori

Senaste kommentarer

Powered by Disqus

Prenumerera

Rss