A quick guide to Umbraco
"Umbraco è un sistema open source di gestione (CMS) per la piattaforma di pubblicazione di contenuti sul World Wide Web, scritta in C# e implementato su infrastruttura Microsoft." (wikipedia)
Umbraco è un buon e versatile CMS che usa la tecnologia Microsoft MVC 4 con il framework .NET 4.5.
L'attuale versione v8.0.2 usa il .NET 4.7.2 con Sql Server supportato dalla versione 2012+.
In questo articolo vedremo qualche consiglio pratico tra cui: multi button controller, come chiamare un controller tramite url, BeginUmbracoForm ed il passaggio dei parametri, ...
Umbraco CMS non è altro che un "Joomla like" ma costruito con la tecnologia Microsoft. A me è sembrato una buona via di mezzo tra Joomla e Drupal. E' sviluppato in C# mediante il paradigma MVC, la versione 7.6 del CMS funziona tranquillamente con il .NET Framework 4.6.2 mentre l'attuale versione v8.0.2 (Maggio 2019) funziona con la versione .NET 4.7.2.
Documentazione
La migliore documentazione su Umbraco la trovate sul sito di Umbraco!!!
Utili link per creare un custom controller
https://our.umbraco.org/documentation/reference/routing/custom-controllers
Oppure una partial-views:
https://our.umbraco.org/documentation/reference/templating/mvc/partial-views
Se volete creare un template custom un'ottima guida è questa:
http://jondjones.com/how-to-create-a-custom-template-in-umbraco-7/
Per accedere al back office di Umbraco (o alla parte amministrativa) dovete usare il seguente link:
http://vostraURL/umbraco
Ovviamente se volete estendere e/o sviluppare su Umbraco avete bisogno dell'ambiente di sviluppo Microsoft Visual Studio.
Io ho lavorato con la versione 2013 e con la 2017.
Come configurare il progetto iniziale?
Leggete questa guida
https://live4coding.wordpress.com/2014/07/21/setting-up-umbraco-with-visual-studio/
Ricordatevi che il nome del progetto che create può essere qualunque ma non "umbraco" altrimenti avrete un bellissimo errore in fase di start del web site. L'errore che otterrete è:
unable to load 'Umbraco.Web.UmbracoApplication'.
L'installazione di Umbraco sotto ambiente Visual Studio, come potete leggere, nella guida che vi ho consigliato, avviene tramite NuGet. Peccato che da qualche versione a questa parte non potete scegliere la versione di Umbraco da installare ma avete a disposizione solo l'ultima.
Un modo per aggirare questo problema è il seguente:
Install-Package UmbracoCms -Version 7.5.9
Così facendo potrete installare la versione voluta.
Per capire bene come funziona Umbraco il miglior modo è lavorare con Umbraco.
Cosa bisogna fare se si vuole reinstallare Umbraco da zero su un nuovo database?
Ci sono da fare solo due cose nel file web.config:
<connectionStrings> <remove name="umbracoDbDSN" /> <add name="umbracoDbDSN" connectionString="" providerName="" /> </connectionStrings>
e poi
<appSettings> <add key="umbracoConfigurationStatus" value="" /> </appSettings>
ovvero bisogna svuotare la connection string e il valore della umbracoConfigurationStatus. Così facendo quando Umbraco parte farà eseguira la procedura di installazione.
Umbraco si basa sul paradigma MVC. Vediamo quindi come creare una form contatti:
https://our.umbraco.org/documentation/Getting-Started/Code/Creating-Forms/
Questa guida è abbastanza completa ma andando a testare il tutto io ottenevo l'errore
Could not find a Surface controller route in the RouteTable for controller name MembershipController
Allora ho scoperto che il metodo del controller è meglio che sia questo:
[ActionName("Submit")]
public ActionResult Submit(ContactFormViewModel model)
{
if (!ModelState.IsValid)
return CurrentUmbracoPage();
}
ovvero meglio specificare la ActionName.
L'uso del controller invece l'ho corretto così:
@using MyUmbracoSite.Controllers;
@model MyUmbracoSite.Models.ContactFormViewModel
@using (Html.BeginUmbracoForm<ContactController>("Submit"))
{
<div>
@Html.TextBoxFor(m => m.Name)
</div>
<div>
@Html.TextBoxFor(m => m.Email)
</div>
<div>
@Html.TextAreaFor(m => m.Message)
</div>
<div>
@Html.CheckBoxFor(m => m.Check)
</div>
<input type="submit" name="Submit" value="Submit" />
}
In questa maniera tutto funziona perfettamente.
Un'altra utilissima guida per capire il funzionamento dell'MVC di Umbraco è la seguente:
http://jondjones.com/how-to-create-a-log-in-page-with-umbraco-7/
Come risolvere l'errore "this document is published but is not in the cache"?
A me questo errore capita quando la cache viene corrotta (ma non ne ho capito il motivo).
Altro errore che viene generato sempre per problemi di cache è il seguente:
"This document is published but its url would collide with content"
Per far ricostruire la cache è sufficiente essere collegati nel back office ed andare al seguente link:
http://<my host>/Umbraco/dialogs/republish.aspx?xml=true
In questa maniera la cache viene ricostruita e tutto riprende a funzionare.
Contenuto Pubblicato?
Cosa fare per verificare se un contenuto è pubblicato o meno?
var node = Umbraco.TypedContent(nodes);
if(node==null)
{
//contenuto non pubblicato
}
else
{
//contenuto pubblicato
}
MACRO
Le macro sono utilissime per renderizzare in un determinato punto un contenuto.
Se in una vista vogliamo inserire una macro si può fare così:
@Umbraco.RenderMacro("NomeMacro", new { NomeParametro = 1 })
Se invece abbiamo un contenuto che ha a disposizione un RichText Editor, Umbraco mette a disposizione un pulsante apposito per inserire la macro.
Come risolvere l'errore CS0234
Questo è un tipico errore in fase di compilazione/esecuzione del nostro progetto.
Per maggiori dettagli vedete questa guida.
Se il problema dipende da una data libreria io consiglio di aprire la console di NuGet "Package Manager Console" e di reinstallare la libreria. Ad esempio:
Install-Package Microsoft.AspNet.Web.Optimization
Impostare i TimeOut
A volte un problema di timeout può generare un errore del tipo:
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Vediamo come impostare alcuni parametri per evitare questo errore.
Alcuni TimeOut vengono impostati nel file di configurazione web.config.
In particolare abbiamo:
<add key="umbracoTimeOutInMinutes" value="20" />
TimeOut espresso in secondi. Il valore di default è 20. Scaduto questo timeout l'utente non sarà più loggato.
Io consiglio un più idoneo valore di 40, soprattutto se sul sito gli utenti ci devono lavorare e non solo consultare le informazioni.
Altro TimeOut è
<httpRuntime executionTimeout="99999" />
Numero di secondi per la durata massima della esecuzione della request.
Altro valore che consiglio di usare è
<httpRuntime maxRequestLength="102400" />
Ovvero la dimensione massima della request. Questo valore è espresso in KB (102400KB=100MB).
Altro utile timeout è
Connection Timeout=15
Questo valore, espresso in secondi, indica il timeout della connessione verso il database. 15 secondi è il valore di default.
Creare un nuovo Media Type: My Slider
Il nostro scopo è semplicissimo: in un determinato punto della nostra Home vogliamo far vedere uno slider di contenuti.
Come si fa?
Allora, si accede al back office e si va nella sezione Media. Creiamo un Folder che conterrà i contenuti multimediali del nostro slider. Creiamo sotto questo folder tutti i contenuti che ci servono, ad esempio contenuti di tipo "My Image Slider".
Se siamo nella sezione Media, dentro un folder possiamo creare oggetti di tipo Media Type (proprio come per i contenuti c'è il Document Type). I Media Type standard sono Folder, Image e File.
Si può creare un nuovo Media Type "My Image Slider"? Certo!
Lo si fa nella sezione Settings, sotto la voce Media Types. Possiamo aggiungere il nostro Media Type con tutti i campi necessari (nella parte Design si mettono i field necessari, nella parte Composition si possono ereditare caratteristiche da altri Media Type). Dato che poi gli oggetti di tipo "My Image Slider" li devo mettere dentro un Folder devo andare nella sezione Permissions del Folder e dire che posso creare dentro un folder oggetto di tipo "My Image Slider".
Creato il nuovo Media Type e messi i contenuti cosa si fa? Si deve creare un nuovo Document Type che possa avere questo tipo di contenuti, o modificarne uno esistente. Ad esempio si può aggiungere un nuovo tab chiamato "Slider" che contenga una property che abbia come editor il Media Picker. Usando come edito il Media Picker comparirà la voce Start Node nella quale si potrà specificare il folder dei nostri nuovi media creato prima.
Quindi il nostro Document Type punterà ad un folder che conterrà elementi di tipo "My Image Slider".
Ora dobbiamo andare nel nostro contenuto, o crearne uno nuovo, ed associare i dati necessari nel nuovo tab. Vedremo il nuovo tab e potremo scegliere dalla cartella che abbiamo creato quale slider far vedere.
Ora è quasi tutto pronto. Possiamo gestire i nuovi dati ma ora li dobbiamo far vedere. Bisogna quindi agire a livello di viste.
Una scelta può essere quella di creare una Macro che chiami la nostro vista che visualizza il nostro nuovo Media Type.
La vista va messa qui: ~/Views/MacroPartials/MyImageSliderHome.cshtml
Notate il nome: MyImageSlider --> indica il tipo di Media Type che viene trattato e Home --> indica dove lo fa vedere.
Alcuni consigli per il codice della vista:
//Ci prendiamo la nostra pagina
var currentPage = Model.Content;
//ci prendiamo il nostro media. "mySlider" indica l'id del field dato nel document type
var mySlider = Umbraco.TypedMedia(Model.Content.GetPropertyValue<int>("mySlider"));
//esempio di come si prendono i parametri del media type
if (mySlider .HasValue("titolo")){
string titolo = slider.GetPropertyValue<string>("titolo");
}
Reinstallare un pacchetto con NuGet
Come si fa a reinstallare un pacchetto tramite NuGet?
Basta aprire da Visual Studio la console di NuGet e digitare il comando:
Update-Package –reinstall <package_name>
Questo non farà altro che reinstallare ed aggiornare all'ultima versione il pacchetto indicato.
Per una guida più completa fare riferimento al seguente link:
https://docs.nuget.org/ndocs/consume-packages/reinstalling-and-updating-packages
Errore 0x80070020
Ricevete questo errore quando avviate la vostra applicazione? Molto probabile che avete attivo un sistema anti-virus o anti-malware che scansione la vostra DLL nel momento in cui la eseguite. Se il vostro sistema non è molto performante allora mentre l'anti-virus fa la scansione blocca la DLL ed il sito web non parte più!
Ricavare un nodo e Redirect verso la pagina
Vediamo come si può ricavare un determinato nodo in Umbraco al fine di leggerne una sua proprietà.
Ricaviamo la Root del sito (questa soluzione va bene se applicata ad una vista dove abbiamo il Model):
var root = Model.Content.AncestorOrSelf(1);
Dalla root ricaviamo una determinata pagina:
var miaPagina = root.DescendantOrSelf("miaPagina");
miaPagina rappresenta l'ID del document type di pagina che vogliamo ottenere. Attenzione se ce ne sono più di una!
Da qui leggiamo la proprietà che ci serve:
var miaProp = miaPagina.GetPropertyValue<string>("miaProprietà");
Un'altro piccolo pezzo di codice molto utile è il seguente:
@foreach (var child in Model.Content.Children)
{
Html.RenderPartial(child.DocumentTypeAlias, child);
}
Questo codice gira tutti i contenuti figli della home. Per ogni contenuto viene richiamata la sua Partial View per essere renderizzato.
Si suppone che la vista abbia lo stesso nome del document type.
Vediamo ora come ricavare i nodi da un componente MultiPicker:
var typedMultiNodeTreePicker = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("myAlias");
if (typedMultiNodeTreePicker!=null)
{
foreach (var item in typedMultiNodeTreePicker)
{
@item.Name <br />
@item.Url <br />
}
}
In un controller Umbraco per fare la redirect verso una determinata pagina si fa così:
IPublishedContent myPage = UmbracoContext.ContentCache.GetByRoute("/myPath/");
if (myPage != null)
{
return RedirectToUmbracoPage(myPage);
}
La Redirect alla Root di Umbraco la si fa così:
return this.Redirect(Umbraco.TypedContentAtRoot().FirstOrDefault().Url);
Questo metodo va bene anche per fare la redirect conoscendo una URL.
Visualizzare una immagine
Vediamo come si può visualizzare una immagine scelta mediante Medie Picker in Umbraco ed in particolare in una partial view.
var image = Umbraco.TypedMedia(@MyPage.image); //image è l'id dell'attributo messo nel Document Type
if(image!=null)
{
var imageUrl = image.Url;
var imageAlt = image.Name;
<img src="/@imageUrl"/>
}
Come cancellare gli utenti in UMBRACO
Gli utenti in Umbraco sono di due tipologie: users e members.
I primi sono utenti del back-office mentre i secondi sono utenti del front-office.
I members possono essere eliminati direttamente dalla pagina di amministrazione presente nel back-office.
Gli users non possono essere eliminati. Ma perché? Semplicemente perché collegato ad un utente ci stanno alcune informazioni chiave.
Vengono memorizzati gli accessi, le proprietà dei documenti e altro ancora.
L'unica alternativa che abbiamo è cancellare gli utenti direttamente da DB.
Vi mostro come esempio alcune query che possono essere eseguite per ripulire il DB di Umbraco da tutti gli utenti del back-office tranne l'admin:
-- Questi nodi vengono riassegnati all'Admin
UPDATE umbracoNode SET nodeUser = 0 WHERE nodeUser > 0
UPDATE cmsDocument SET documentUser = 0 WHERE documentUser > 0
-- Queste informazioni vengono cancellate
DELETE FROM umbracoLog WHERE userId > 0
DELETE FROM umbracoUser2App WHERE [user] > 0
DELETE FROM umbracoUserLogins WHERE userID > 0
DELETE FROM umbracoUser WHERE id > 0
Multi button controller
Una problematica molto comune è la seguente: una form con più di un pulsante per la submit. Come si fa?
Ho trovato sul web ua guida perfetta: MVC SurfaceController -Post Action
Usate la soluzione di "Nicholas Westby" che è perfetta e funziona benissimo.
Call a controller from a URL
E' possibile impostare una URL in maniera da richiamare la Action di un dato controller?
Certo che si può.
La URL la si deve comporre così:
myHost/umbraco/surface/{controllername}/{action}?{parameter}
In questo caso il nostro controller deve essere così fatto:
public class MyGestController : Umbraco.Web.Mvc.SurfaceController
{
[HttpGet]
[ActionName("MyAction")]
public ActionResult MyAction(OrderProcessModel model)
{
....return umbraco page
}
}
Gestione chiamate Ajax
Vediamo in breve quali sono gli step per fare una chiamata Ajax sotto Umbraco.
1. La chiamata Ajax va abilitata
Nel web.config, nella sezione appSettings ci va la chiave seguente:
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="ClientValidationEnabled" value="true" />
2. Caricare gli script
Nel nostro template bisogna far caricare gli script JS necessari:
<script src="/path/js/jquery.unobtrusive-ajax.min.js"></script>
3. Abilitare il file global.asax che altrimenti non verrà caricato:
<%@ Application Codebehind="Global.asax.cs" Inherits="Umbraco.Global" Language="C#" %>
come si vede, si fa chiamare la classe Umbraco.Global definita nel file Global.asax.cs come questo:
public class Global : UmbracoApplication
{
protected override void OnApplicationStarted(object sender, EventArgs e)
{
LogHelper.Debug(this.GetType(), "OnApplicationStarted");
base.OnApplicationStarted(sender, e);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Questo file serve per chiamare la nostra regola di routing.
4. Regola di routing
Nella cartella App_Start si mette il file RouteConfig.cs con leregole di routing:
namespace Umbraco.Global
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "Ajax",
url: "Ajax/{controller}/{action}"
);
}
}
}
5. Nella form si mette la chiamata Ajax:
<div id="myTarget">
</div>
@using (Ajax.BeginRouteForm("Ajax", new { controller = "MyClass", action = "MyAction" }, new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "myTarget",
OnFailure = "ShowFailure()",
OnSuccess = "ShowSuccess()"
}, new { @id = "myFormAjaxId" }))
{
<input type="submit" value="Submit" />
}
Questa è la form che invoca il nostro controller MyClass e la sua action MyAction. Al ritorno, grazie all'istruzione InsertionMode.Replace, il risultato verrà messo nel div target.
Modello nelle pagine
Ci troviamo nella seguente casistica: si chiama una pagina umbraco, questa pagina avrà un suo template e quindi un suo layout.
Se associata alla pagina abbiamo un FORM e quindi un Controller associato, spesso può succedere che al refresh della pagina, a seguito di una chiamata Action, si perde il payout della pagina stessa.
Per bypassare questo problema si deve usare la seguente classe:
public static class ExtensionMethods
{
public static void MapModel<T>(this WebViewPage<T> page) where T : class
{
var models = page.ViewContext.TempData.Where(item => item.Value is T);
if (models.Any())
{
page.ViewData.Model = (T)models.First().Value;
page.ViewContext.TempData.Remove(models.First().Key);
}
}
}
Questo codice serve a rimettere il modello nella pagina, così facendo non si perde il layout.
BeginUmbracoForm e passaggi di parametri
Vediamo come invocare il submit di una form Umbraco e passare alla action del controller un parametro:
@using (Html.BeginUmbracoForm<MyController>("MyAction", new { myParam = "1", @class="myClassCss"}))
{
<button type="submit" name="Submit" value="Submit">Submit</button>
}
Nel nostro esempio, invochiamo il metodo MyAction del controller MyController che riceve il parametro myParam valorizzato a "1".
Come si può vedere dall'esempio di sopra, vi mostro anche come impostare la class CSS per la vostra form. In questo caso la classe associata si chiama myClassCss.
Logging on Umbraco v8
Con la versione 8 di Umbraco sono cambiate molte cose tra cui "come fare il log".
Per loggare si deve usare il seguente codice:
using Umbraco.Core.Composing;
...
Current.Logger.Debug(this.GetType(), "my log message");
Il Team di Umbraco consiglia/sconsiglia le seguenti modalità di log:
//GOOD - Do use :)
Logger.Info<MyApiController>("We are saying hello to {Name}", name);
//BAD - Do not use :(
Logger.Info<MyApiController>($"We are saying hello to {name}");
//BAD - Do not use :(
Logger.Info<MyApiController>("We are saying hello to " + name);
Umbraco V8 e Bundle e Route config
Nella versione 8 di Umbraco è cambiata anche la gestione dei BundleConfig e RouteConfig che ora si fanno così:
public class ApplicationEventComposer : IComposer
{
public void Compose(Composition composition)
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
ovvero si usano gli oggetti composer per lo scopo.
Download Umbraco
Il download di Umbraco lo potete fare direttamente dal sito del produttore qui: umbraco.com/download
Ottimizzare Umbraco
Vi lascio una risorsa web molto utile se volete ottimizzare il vostro sito Umbraco: 11+ Tips to optimize Umbraco CMS
Buon lavoro!