CodeProject

Una limitazione che incontriamo sviluppando Web Application con SharePoint, è non poter eseguire codice direttamente nelle pagine aspx.

Ma non è del tutto vero: per capire meglio, vediamo in dettaglio le pagine che possiamo creare all’interno di SharePoint.

leisure_468x60.gif

Site Pages

Sono pagine comunemente create dagli utenti ed in particolare sono le “web part pages”, “standard pages”, “master pages” e “page templates”.

Eccetto le pagine aggiunte tramite features, le site pages sono memorizzate nel “content database”. Ad esempio la master page di default di ogni sito, in realtà esiste come v4.master nel file system, ma viene messa a disposizione come default.master nella galleria “master pages” di ogni sito.

Application Pages

Sono pagine che forniscono le funzionalità dell’interfaccia come la “site administration” e i tool di configurazione. Sono presenti nel file system nella cartella mappata su IIS come “/_layouts”.

Soluzione

Per ovvi motivi di sicurezza e stabilità dell’applicazione, le site pages sono renderizzate in “safe mode” che non permette la presenza di codice inline all’interno della pagina stessa. La programmazione è quindi rimandata nella costruzione di web parts custom.

In alcuni casi però potrebbe essere necessario gestire i vari eventi della pagina per gestirne il rendering, come ad esempio cambiare dinamicamente la master page utilizzata.

Una soluzione possibile è aggiungere nel web.config un PageParserPath, autorizzando quindi SharePoint a compilare ed eseguire l’eventuale codice inline di tutte le pagine presenti nella cartella indicata (/myFolder):

  <SharePoint>
    <SafeMode ...>
      <PageParserPaths>
        <PageParserPath VirtualPath="/myFolder/*" CompilationMode="Always"
                        AllowServerSideScript="true" IncludeSubFolders="true" />
      </PageParserPaths>

Utilizzando questo approccio si va incontro ad un rischio: gli utenti possono eseguire codice arbitrario sul server.

Una soluzione migliore è creare una classe (un modulo) ed effettuare il deploy sul server. Le pagine aspx possono quindi ereditare da questa classe e quindi eseguirne il relativo codice.

Vediamo come creare questa soluzione utilizzando Visual Studio 2010.

 

SharePoint Project

Dopo aver creato un nuovo progetto “Empty SharePoint Project” (SP_Presentation), aggiungo un Modulo (SP_CustomApplicationPages)

Aggiuinta di un modulo

Aggiuinta di un modulo

468x60_tutti-gli-eventi-.gif

ed aggiungo una Application Page (myApplicationPage.aspx)

Aggiuinta di Application Page

Aggiuinta di Application Page

Orsetti di Peluche Trudi

Normalmente le Application Pages vengono salvate nella cartella apposita, infatti Visual Studio vi importerà automaticamente la cartella mappata “Layouts” e ci includerà la pagina appena creata, ma non è il nostro intento. Quello che vogliamo fare è una Site Page. Bene, basterà spostare la pagina all’interno del modulo SP_CustomApplicationPages ed eliminare la cartella Layouts.

Aprendo la pagina appena creata, bisogna cambiare la direttiva @Page ed aggiungere alcuni riferimenti in questo modo:

<%@ Register TagPrefix="wssuc" TagName="InputFormSection" Src="/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" Src="/_controltemplates/InputFormControl.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="/_controltemplates/ButtonSection.ascx" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" Inherits="SP_Presentation.myApplicationPage" %>

Per creare un semplice esempio, aggiungiamo poco contenuto nella pagina inserendolo nel content con ID=Main (Per gli altri content ognuno può usare la propria ispirazione Sorriso)

<table id="maintable" border="0" cellspacing="0" cellpadding="0"
    width="100%">
    <wssuc:InputFormSection Title="<%$Resources:glucolo.SP_Learning.SF_Presentation.AppResources,SFCustomApplicationPage_FormSection1Title%>"
        runat="server">
        <template_description>
        <SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="<%$Resources:glucolo.SP_Learning.SF_Presentation.AppResources,SFCustomApplicationPage_FormSection1Description%>" EncodeMethod="HtmlEncode"/>
    </template_description>
        <template_inputformcontrols>
        <wssuc:InputFormControl runat="server">
        <Template_Control>
            <table border="0" cellspacing="1">
            <tr>
                <td colspan="2" nowrap="nowrap">
                <SharePoint:EncodedLiteral ID="EncodedLiteral7" runat="server" text="Sample Label" EncodeMethod="HtmlEncode"/>:
                <font size="3"> </font><br />
                </td>
            </tr>
            <tr>
                <td dir="ltr">                
                    <SharePoint:InputFormTextBox title="FormSection1Input" ID="TxtInput" Columns="35" Runat="server" maxlength="255" size="60" width="100%" />
                </td>
            </tr>
            </table>
        </Template_Control>
        </wssuc:InputFormControl>
    </template_inputformcontrols>
    </wssuc:InputFormSection>
    <wssuc:ButtonSection runat="server" ShowStandardCancelButton="False">
        <template_buttons>
        <asp:placeholder ID="Placeholder1" runat="server">
        <asp:Button runat="server" ID="BtnOk" Text="Ok" />
        <SeparatorHtml>
            <span id="idSpace" />
        </SeparatorHtml>
        <asp:Button runat="server" ID="BtnCancel" Text="Cancel" />         
        </asp:PlaceHolder>
    </template_buttons>
    </wssuc:ButtonSection>
</table>
<SharePoint:FormDigest ID="FormDigest1" runat="server" />

e poche righe di codice

namespace SP_Presentation
{
 public partial class myApplicationPage : LayoutsPageBase
 {
  protected InputFormTextBox TxtInput {get;set;}
  protected Button BtnOk {get;set;}
  protected Button BtnCancel {get;set;}
  protected override void OnLoad(EventArgs e)
  {
      this.BtnOk.Click += new EventHandler(this.BtnOk_Click);
      this.BtnCancel.Click += new EventHandler(this.BtnCancel_Click);
  }
  private void BtnCancel_Click(object sender, EventArgs e)
  {
      this.TxtInput.Text = "Cancel clicked";
  }
  private void BtnOk_Click(object sender, EventArgs e)
  {
      this.TxtInput.Text = "Ok clicked";
  }
 }
}

Dobbiamo fare ancora una modifica al file Elements.xml, dando per scontato di aver creato sul sito (utilizzando ad esempio SharePoint Designer) una document library di nome MyCustomPages:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="<a href="http://schemas.microsoft.com/sharepoint/">http://schemas.microsoft.com/sharepoint/</a>">
  <Module Name="SP_CustomApplicationPages" Url="MyCustomPages">
  <File Path="SP_CustomApplicationPages\myApplicationPage.aspx"
        Url="myApplicationPage.aspx" Type="GhostableInLibrary" />
</Module>
</Elements>

Rimane un’ultima cosa da fare prima di fare il Deploy: dire a SharePoint che la dll che verrà distribuita, contiene del codice sicuro. Normalmente è proibito utilizzare classi e/o controlli a meno che non vengano inseriti all’interno del web.config nella sezione “SafeControls”. Per evitare di farlo a mano, bisogna cliccare con il pulsante destro del mouse sul modulo SP_CustomApplicationPages, scegliere proprietà e modificare “Voci di controllo sicure” (purtroppo ho la versione in italiano installata).

Il nome non ha importanza, mentre è fondamentale scrivere “SP_Presentation” nella proprietà “Spazio dei nomi” (NameSpace – questo è facile).

Aggiunta di SafeControls

Aggiunta di SafeControls

 

Problema con la masterpage

Ho scoperto che in alcuni casi si riceve errore dal server in quanto non viene supportata la proprietà “MaterPageFile” della direttiva @Page. Confrontandomi con chi ha ricevuto questo errore (compreso me stesso), in verità non ho ancora capito la causa. Si risolve velocemente togliendo questa proprietà ed intervenendo sul code behind:

protected override void OnPreInit(EventArgs e)
{
this.MasterPageFile = "~/_layouts/application.master";
base.OnPreInit(e);
}

 

Per concludere

Sarei molto grato ricevere commenti, suggerimenti, eventuali correzioni, domande a riguardo.

Ammetto che non sia molto semplice come sembra, infatti la mia prima volta mi ha portato via ben 2 giorni, anche perché sul libro dove ho studiato, la soluzione proposta peccava su precisione e completezza, ed alcuni accorgimenti (che qui già trovate inclusi nella soluzione) li ho dovuti scoprire a scapito di tempo e salute (qualcuno si è anche rivoltato nella tomba Occhiolino ).