Umbraco CMS
"scyzoryk" w rękach .NET developera

Marcin Zajkowski  /  @zajkowskimarcin  /  Grupa .NET PW

Marcin Zajkowski

Trener i Certyfikowany Master Umbraco
@
 The Cogworks

Czarodziej od Treśći i Wiedzy, Wódz Trenerów / PedagoGEEK @ WOW School

Ja, bloger @ udfnd.pl

@zajkowskimarcin

Wstęp

CMSy

  • ~200 różnych systemów (Wiki: http://bit.ly/cms-lis)
  • Darmowe i Open Source
  • Komercyjne, płatne, "Pudełkowe"
  • Własne tzw. "autorskie"

Ale co to to Umbraco?!

Umbraco CMS

  • http://umbraco.com
  • ASP.NET MVC, Licencja MIT, Open Source (Github)
  • Ponad 400 000 witryn live, średnio 12 nowych instalacji na minutę
  • Najbardziej popularny w UK, Danii, Australii i USA - pora na Polskę!
  • Duża i bardzo aktywna społeczność
  • "Best Open Source .NET CMS 2014"

Umbraco v7+

  • Elastyczny i nastawiony na edycję / rozbudowę
  • Backoffice: AngularJS
  • Frontend: IPublishedContent (XML)
  • ASP.NET MVC - bez żadnych ograniczeń
  • Umbraco Cloud (http://umbraco.io, 14 dni za darmo, Azure + Umbraco + CI/CD)

Developerzy <3

Klienci <3

Edytorzy <3

Społeczność Umbraco

w Polsce

Demo

Demo #1

  • Umbraco możemy zainstalować / uruchomić na wiele sposobów (Web Platform Installer, NuGet, manualnie, a nawet w chmurze)
  • Różne typy baz danych + współdzielenie ich na potrzeby DEV
  • StarterKity - do nauki
  • "Framework"

Gdzie zaczyna się magia i zabawa?!

Umbraco Events

Rozszerzając klasę ApplicationEventHandler

using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace MyProject.EventHandlers
{
    public class RegisterEvents : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            ContentService.Saving += ContentService_Saving;     
        }

        private void ContentService_Saving(IContentService sender, SaveEventArgs<IContent> e)
        {                
            foreach (var content in e.SavedEntities
                .Where(c => c.Alias.InvariantEquals("MyContentType"))
                .Where(c => c.IsNewEntity()))
            {
                // Magic! GetValue(), SetValue(), etc...
            }
        }
    }
}

ContentService events

  • Saving + Saved
  • Publishing + Published
  • Copying + Copied
  • Moving + Moved
  • Trashing + Trashed
  • Deleting + Deleted
  • DeletingVersions + DeletedVersions
  • RollingBack + RolledBack
  • SendingToPublish + SentToPublish

Inne serwisy

  • MediaService
  • ContentTypeService
  • DataTypeService
  • FileService
  • LocalizationService

 

* TreeEvents

var contentService = Services.ContentService;
var parentId = -1;// root of site
var docTypeAlias = "textPage";// or whatever

// Prepare content            
var content = contentService
              .CreateContent("My New Content", parentId, docTypeAlias);

// Set properties
content.SetValue("pageTitle","My New Page Title!" );
content.SetValue("bodyText", "<p>Content for my rich text area</p>");         


// save and publish         
var publishStatus=contentService.PublishWithStatus(content);

Różne typy Controllerów

Wzbogacone o dodatkowe konteksty

SurfaceController

public class MyController : Umbraco.Web.Mvc.SurfaceController
{
    public ActionResult Index() 
    {
        return Content("hello world");
    }
}
[PluginController("SuperAwesomeAnalytics")]
public class MyController : Umbraco.Web.Mvc.SurfaceController
{
    public ActionResult Index() 
    {
        return Content("hello world");
    }
}

Web API

Web API w Umbraco

ApplicationContext ApplicationContext {get;}
ServiceContext Services {get;}
DatabaseContext DatabaseContext {get;}
UmbracoHelper Umbraco {get;}
UmbracoContext UmbracoContext {get;}

Dziedziczymy z Umbraco.Web.WebApi.UmbracoApiController

public class ProductsController : UmbracoApiController
{       
    public IEnumerable<string> GetAllProducts()
    {
        return new[] { "Table", "Chair", "Desk", "Computer", "Beer fridge" };
    }
}

~/Umbraco/Api/[YourControllerName]

~/Umbraco/[YourAreaName]/[YourControllerName]

[PluginController("AwesomeProducts")]
public class ProductsController : UmbracoApiController
{       
    public IEnumerable<string> GetAllProducts()
    {
        return new[] { "Table", "Chair", "Desk", "Computer", "Beer fridge" };
    }
}

Backoffice Controller

Dziedziczymy z UmbracoAuthorizedApiController lub UmbracoAuthorizedJsonController

~/Umbraco/backoffice/Api/[YourControllerName]
~/Umbraco/backoffice/[YourAreaName]/[YourControllerName]

Własne Controllery

tzw. Route Hijacking

public class HomeController : Umbraco.Web.Mvc.RenderMvcController
{
    public override ActionResult Index(RenderModel model)
    {
        //Do some stuff here, then return the base method
        return base.Index(model);
    }

    public ActionResult MobileHomePage(RenderModel model)
    {
        //Do some stuff here, the return the base Index method
        return base.Index(model);
    }
}

UmbracoHelper

Bo większość ktoś już za nas napisał :)

Umbraco.{metoda}()

// Get the children of the first content item found in the root
@foreach (var child in Umbraco.ContentAtRoot().First().Children) {
    <a href="@child.Url">@child.Name</a>
}
@{
    var newsArticles = Umbraco.ContentAtXPath("//newsArticle");
}
<h3>@Umbraco.Field("bodyText")</h3>
@if(Umbraco.MemberHasAccess(CurrentPage.Id, CurrentPage.Path)){
    <h1>Welcome!</h1>
} 
<p>@Umbraco.GetDictionaryValue("createdOn"): @CurrentPage.CreateDate</p>

Własne sekcje

a.k.a. aplikacje

<?xml version="1.0"?>
<applications>
  <add alias="content" name="Content" icon="traycontent" sortOrder="0" />
  <add alias="media" name="Media" icon="traymedia" sortOrder="1" />
  <add alias="settings" name="Settings" icon="traysettings" sortOrder="2" />
  <add alias="developer" name="Developer" icon="traydeveloper" sortOrder="3" />
  <add alias="users" name="Users" icon="trayuser" sortOrder="4" />
  <add alias="member" name="Members" icon="traymember" sortOrder="5" />
  <add alias="translation" name="Translation" icon="traytranslation" sortOrder="6" />
</applications>

~/Config/applications.config

ApplicationContext.Current.Services.SectionService (v7+)

SectionService.MakeNew("nazwa", "alias", "awesome-icon", 99)

Własne edytory właściwości

JSON (config)
+
HTML (widok)
+
AngularJS (controller)

package.config

{
    //you can define multiple editors
    propertyEditors: [
        {
            /*this must be a unique alias*/
            alias: "My.MarkdownEditor",
            /*the name*/
            name: "My markdown editor",
            /*the icon*/
            icon: "icon-code",
            /*grouping for "Select editor" dialog*/
            group: "Rich Content",
            /*the HTML file we will load for the editor*/
            editor: {
                view: "~/App_Plugins/MarkDownEditor/markdowneditor.html"
            }
        }
    ]
    ,
    //array of files we want to inject into the application on app_start
    javascript: [
        '~/App_Plugins/MarkDownEditor/markdowneditor.controller.js'
    ]
}

editor.html

<div ng-controller="MyController">
    <textarea ng-model="model.value"></textarea>
</div>

controller.js

(function () {
    'use strict';

    angular.module("umbraco")
        .controller("MyController", FindAndReplaceCtrl);

    MyController.$inject = ['$scope', 'MyService'];

    function MyController($scope, MyService) {
        var vm = this;

        //Data...

        //Functions...
        vm.initMyEditor = initMyEditor;

        function initMyEditor() {
            console.log("Init...");
        }
    }
})();

Demo

Co dalej?

Gdzie szukać wiedzy, jak żyć ;)

Gdybym to był ja...

Dziękuję za uwagę!

@zajkowskimarcin  /  marcin.zajkowski@thecogworks.com