Come perdere 2 giorni di lavoro, ovvero: nascondere il Ribbon per gli anonimous users

Il problema di per se non è nascondere il Ribbon, bensi il problema che è venuto fuori è quello che spiego di seguito. Requisito: Nascondere il controllo Ribbon di SharePoint 2010 nel caso la pagina sia vista da un Anonimous user. Soluzione: Inserisco il controllo Ribbon (che è presente nella master page custom che è stata creata per il progetto) nel controllo asp.net che si chiama LoginView usando il template LoggedInTemplate, penso che sia la soluzione migliore evitare di renderizzare il markup quando non ce nè bisogno. Problema: Per qualche strano motivo, se aggiungo una web part CEWP (Content Editor Web Part) alla pagina che usa la mia master page custom, formatto il testo usando i comandi nel tab "format text", salvo la pagina e la rimetto in editing, il tab "format text" non ricompare più. In pratica, non era più possibile formattare il contenuto della CEWP una volta che si metteva in editing la pagina. La prima cosa che mi viene in mente è cercare su google/bing se qualcuno ha avuto lo stesso problema, ma niente, nessuno sembra aver avuto questo problema che mi/ci sta facendo perdere oltre che tempo, anche ore di sonno! Dopo un paio di giorni di ricerche invane, mi imbatto in un post che mi illumina e ci fa uscire dal tunnel in cui io e il buon Francesco ci eravamo infilati.Il problema era appunto (abbastanza assurdo per il vero) che il controllo Ribbon non digerisce bene il controllo LoginView, infatti si può leggere: I finally figured out what was causing this issue.  I'm posting it here for anyone else who happens to run into it.  I was putting the SPRibbon control inside of a LoginView control so that it would only show up when the user was authenticated.  Apparently SharePoint does not like this, and the original issue I posted will show up.  I now hide it via CSS styles by default, and use a LoginView control to change the style of the container for logged in users. Ok, finalmente è chiaro il motivo di questo problema!Come si può quindi risolvere il requisito di nascondere il Ribbon in caso l'utente sia di tipo anonimo? Esistono diversi modi: Nascondere il controllo Ribbon inserendolo in un controllo SPSecurityTrimmedControl e impostando la proprietà PermissionsString con i permessi che l'utente deve avere per poter visualizzare appunto il Ribbon, nel mio caso (sito pubblico) dovrei mettere "AddAndCustomizePages" in modo che sono chi abbia quel permesso possa appunto visualizzare il Ribbon, gli altri niente. <SharePoint:SPSecurityTrimmedControl PermissionsString="AddAndCustomizePages" runat="server"> <div id="s4-ribbonrow" class="s4-pr s4-ribbonrowhidetitle"> .... </div> </SharePoint:SPSecurityTrimmedControl> Nascondere il controllo Ribbon usando JavaScript (magari usando jQuery) al load della pagina, inserendolo in un controllo LoginView usando "AnonymousTemplate" come template: <asp:LoginView ID="LoginView1" runat="server"> <AnonymousTemplate> <script type="text/javascript"> function HideRibbon() { $("#s4-ribbonrow").hide(); } _spBodyOnLoadFunctionNames.push("HideRibbon"); </script> </AnonymousTemplate> </asp:LoginView> Nascondere il controllo Ribbon usando i css (in modo tale che se per qualsiasi motivo JavaScript non sia abilitato, il controllo viene comunque nascosto dal Browser), inserendolo sempre in un controllo LoginView usando "AnonymousTemplate" come template: <asp:LoginView ID="LoginView1" runat="server"> <AnonymousTemplate> <style type="text/css"> #s4-ribbonrow { display:none !important; } </style> </AnonymousTemplate> </asp:LoginView> Qual'è la soluzione migliore? Come sempre dipende dal contesto.In questo caso, la soluzione migliore (che è anche quella sponsorizzata da Francesco :) ) è la prima in quanto si tratta di un sito di publishing ed è meglio evitare di portare sul browser del markup quando non ce nè bisogno.Le altre due soluzioni sono migliori solo quando si vuol evitare di dover impostare ogni singolo permesso che l'utente dovrà avere per soddisfare il requisto di visualizzazione del Ribbon (con la soluzione 1) bensi lo si vuole nascondere solo per gli utenti anonimi.Fra queste ultime due soluzioni preferisco l'ultima in quanto appunto viene eseguita anche se il browser ha problemi con JavaScript. Buon lavoro a tutti! ;)

Building a ASP.NET Packaging Site Manager Using NuGet

Lately I've been studying NuGet, a system for creating packages of code can install a number of features in an existing project or that is being developed. The operating mechanism is very simple. A package is a file that has a ".Nupkg" (it's a. zip file) that contains a series of folders and files, including the most important one with the ".Nuspec"which is the manifest of the whole package. The package must meet a number of conventions that are available in the documentation for developers here. NuGet was founded for the management of these modules under development, although in Web Matrix, is also used to install extensions at runtime. This function is very useful in all those cases where you need to make customizations of sites / applications and you want to go to install them without having to hack the code or even better without having to open Visual Studio. Searching the internet I find a nice post by Phil Haack, describing a way to create a series of pages to remove, install and update package at runtime. Having failed to find the source code I decided to follow his way and I extracted the code from the library "System.Web.WebPages.Administration.dll” with reflector. This step is necessary because many class have been decorated to serve as internal and not public. Haacked is also confirmed this in his post. The project attached, allows you to manage the lifecycle of packages in a asp.net site, is not complete and should be taken only as an example of possible implementation. In this example we see how powerful a tool like this. NuGet can we package as well as portions of applications to create modifications to the configuration files like web.config. Think for example of having to create a package, once deployed should insert a new module in the web.config. With NuGet is much simpler since it is already provided this functionality by default. FabioFranzini.NuGet.Package.WebManager.zip (745,50 kb)

ASP.NET 2.0 e Default Button

Post veloce (ultimamente ho poco tempo :-( ) per dimostrare come non si finisce mai di imparare nel nostro lavoro.Mi sono appena imbattuto nel classico problema di rendere un bottone quello di default all'interno di un form asp.net e ho scoperto che basta settare la proprietà "defaultbutton" proprio del form con l'id del bottone che vogliamo rendere appunto quello di default.Cosi facendo, anche se premiamo Enter sulla tastiera, verrà generato il postback associato al bottone che abbiamo reso di default.Ottimo direi!!

WPF al servizio del Web

L’idea di poter realizzare immagini in WPF e poi pubblicarle sul web è nata un pomeriggio insieme al mio collega Matteo. Di per se la realizzazione è banale: Il classico HttpHandler che riceve una richiesta con dei parametri, chiama una libreria e ritorna un array di byte al browser. Quello che è stato fatto invece va ben oltre. La libreria comprende un controllo chiamato XamlImage che eredita da System.Web.UI.WebControls.Image e che ha diverse proprietà fra cui: ImageType di tipo ImageType (enumerazione con valori Gif, Jpg, Png); XamlFile che contiene il percorso relativo del file Xaml che si vuole renderizzare; Parameter che è una lista di oggetti XamlImageParameter che viene usata per passare i parametri al motore di WPF che effettuerà il bindign prima di renderizzare l’immagine. Questo è un esempio di configurazione del controllo all’interno della pagina di esempio: <ff:XamlImage ID="XamlImage1" runat="server" ImageType="Png" XamlFile="~/Xaml/ImageRender.xaml"> <ff:XamlImageParameter Name="BorderBrush" Value="Blue" /> <ff:XamlImageParameter Name="BorderThickness" Value="2" /> <ff:XamlImageParameter Name="CornerRadius" Value="5" /> <ff:XamlImageParameter Name="RotationAngle" Value="0" /> <ff:XamlImageParameter Name="ShadowDepth" Value="10" /> </ff:XamlImage> I vari parametri di tipo XmlImageParameter che sono stati inseriti nel controllo, non sono le vere proprietà dei controlli WPF, ma solamente dei nomi delle proprietà che sono bindate ai controlli effettivi. Ma questo ve lo spiega meglio Matteo che ha realizzato la libreria come mi serviva. :-) Nel metodo Render, il controllo costruisce il link per recuperare l’immagine passando tutti i parametri dichiarati come parametri in querystring. Ma a quale immagine punta il controllo? Punta nient’altro che all’indirizzo del file Xaml dichiarato con l’aggiunta dell’estensione webXaml. Bisogna quindi andare a registrare nel webconfig l’HttpHandler per gestire questo tipo di richieste, operazione che è stata resa facile grazie al designer del controllo che tramite smarttag permette di fare questa operazione con un solo click. Il problema principale nella realizzazione dell’HttpHandler è stato quello di istanziare e usare gli oggetti di WPF all’interno dell’HttpHandler. WPF funziona in modalità STA ovvero “Single Thread Apartment” mentre ASP.NET no. Cercando su Google il metodo che viene maggiormente utilizzato è quello descritto in questo post del grande Rick Strahl. La realizzazione di questa libreria è stata molto utile anche come esercizio per la creazione di designer per controlli custom. Questa libreria è sicuramente migliorabile sotto diversi aspetti (non ho effettuato nessun tipo di test per quanto riguarda le performance), ma comunque è da prendere come esempio per la creazione di controlli custom e per la realizzazione di HttpHandler un po' diversi dai soliti. :-) In allegato la libreria con un sito di test. XamlImageGenerator.zip (603,79 kb)

Visualizzare un User Control come se fosse una pagina

Questo post nasce dall’esigenza di utilizzare i controlli utente in accoppiata al plug-in ThickBox per jQuery. Per chi non conoscesse jQuery, sto parlando di una libreria JavaScript open source molto avanzata, leggera, estensibile e di facile utilizzo che permette di aggiungere “behavior” all’html. Essa è balzata agli onori della cronaca perché sia Microsoft che Nokia hanno annunciato il loro supporto rispettivamente in Visual Studio e nel Nokia Web Run-Time Platform. ThickBox è un’estensione a questa libreria, e consente di creare delle finestre modali alla Web 2.0 semplicemente utilizzando link html. Uno delle modalità di utilizzo di ThickBox è quella di aprire appunto un link all’interno di un iframe, permettendo di visualizzare, per esempio in un contesto master-detail, una pagina all’interno della pagina corrente. Un esempio classico è quello del master-detail, abbiamo una GridView e una delle colonne è un link al dettaglio della riga. Bene, ma concentriamoci su cos’è uno user control. Esso è un tipo di controllo composito che funziona come una pagina Web ASP.NET. È possibile aggiungervi markup e controlli server esistenti e definirne le proprietà e i metodi e incorporarlo nelle pagine Web ASP.NET. Esso è praticamente uguale a una pagina ASP.NET ad eccezione di alcune cose: L'estensione del nome file del user control è ASCX. Al posto di una direttiva @ Page, l’user control contiene una direttiva @ Control che definisce la configurazione e altre proprietà. Nell’user control non sono presenti elementi html, body o form, che devono invece trovarsi nella pagina che contiene il controllo. Gli user control non possono essere eseguiti come file autonomi. Quest’ultimo punto è quello che mi ha fatto più riflettere. Abbiamo capito che gli user control sono simili alle Page (tant’è che esiste una guida su come convertire User Control in Page), ma allora perché non posso richiamarli direttamente come se fossero delle Pagine? Se provo a chiamare direttamente un user control questo è il risultato Questo perché come detto precedentemente un user control non è in grado di generare da solo i tag html necessari per il corretto funzionamento cioè html, body e soprattutto form. L’unico modo per risolvere il problema è quello di creare una pagina che faccia da “host” per il nostro controllo, una per ogni controllo che vogliamo visualizzare come se fosse una pagina. L’idea che ho avuto, banale ma efficace, è quella di realizzare una pagina che permetta questo, caricando in modo dinamico il controllo che si vuole visualizzare. Per caricare un user control all’interno di una pagina si utilizza il metodo LoadControl della classe Page, che accetta come parametro il path virtuale del controllo da caricare. 1: String VirtualPath = "~/DateTime.ascx"; 2: UserControl userControl = (UserControl)this.LoadControl(VirtualPath); Il punto ideale dove caricare i controlli all’interno del ciclo di vita della pagina è il Page_Init, perché questo è il punto dove l’albero dei controlli della pagina viene creato e inizializzato. Se vogliamo che il nostro controllo persisti il suo viewstate, dobbiamo aggiungerlo proprio in questo evento, altrimenti si avrà un disallineamento fra i vari postback. Un’altra cosa da ricordare è che dobbiamo aggiungere il controllo dinamicamente alla pagina a ogni caricamento, nel viewstate viene persistito solo lo stato dei controlli e non i controlli stessi. 1: protected override void OnInit(EventArgs e) 2: { 3: base.OnInit(e); 4:  5: String VirtualPath = "~/DateTime.ascx"; 6: UserControl userControl = (UserControl)this.LoadControl(VirtualPath); 7:  8: this.Form.Controls.Add(userControl); 9: } Questo è il metodo classico per poter aggiungere un controllo dinamicamente a una pagina. Il passo successivo è quello di poter chiamare direttamente il controllo, senza passare per una pagina, almeno in apparenza :-) Per questo, basta registrare new web.config un HttpHandler che intercetti una determinata richiesta e la gestisca, per esempio la classe che ho implementato serve per gestire la richiesta “*.ascx.aspx” cioè tutte le richieste che finiscono con .ascx.aspx. Ho scelto l’estensione .aspx perché è già registrata su IIS come estensione gestita dall’isapi di ASP.NET 1: <httpHandlers> 2: ... 3: ... 4: <add verb="*" path="*.ascx.aspx" type="UserControlPage"/> 5: </httpHandlers> La classe deriva da Page e permette semplicemente di confondere il motore di ASP.NET facendogli credere di effettuare una richiesta a una pagina esistente invece che a un controllo ascx. 1: public class UserControlPage: Page 2: { 3: protected override void OnInit(EventArgs e) 4: { 5: base.OnInit(e); 6:  7: HtmlForm form = new HtmlForm(); 8: UserControl userControl = (UserControl)this.LoadControl(this.Request.AppRelativeCurrentExecutionFilePath.Replace(".aspx", "")); 9: 10: form.Controls.Add(userControl); 11: this.Controls.Add(form); 12: } 13:  14: protected override void Render(HtmlTextWriter writer) 15: { 16: writer.Write(@"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd""><html xmlns=""http://www.w3.org/1999/xhtml""><head><title></title></head><body>"); 17: base.Render(writer); 18: writer.Write("</body></html>"); 19: } 20: } Utilizzando la proprietà AppRelativeCurrentExecutionFilePath dell’oggetto Request ricaviamo il VirtualPath della richiesta pervenuta a ASP.NET, a questo punto basta eliminare il suffisso .aspx e avremo il path del controllo da caricare. Tutti i controlli che necessitano di iterazione con l’utente devono essere inseriti all’interno di un form ed essendo la classe UserControlPage una pagina senza markup, bisogna creare un controllo HtmlForm per poter aggiungere il nostro user control in modo corretto. Infine nel metodo Render è stato aggiunto il codice html necessario per rendere la pagina valida. A questo punto per richiamare un controllo e visualizzarlo come una comune pagina ASP.NET basterà indicare il percorso aggiungendo l’estensione “.aspx”Questo è il risultato UserControlPage.zip (38,16 kb)

About the author


Fabio Franzini is Senior Consultant, Software Engineer and Trainer, specializing mainly on SharePoint, ASP.NET, web solutions and in general about everything that revolves around the Microsoft web platform. [more]

Translate

Month List

Page List