SharePoint 2010: Cambiar la Página Maestra (MasterPage) a un sitio de tipo Blog

Hola a todos.

En este artículo explico cómo cambiar la página maestra a un sitio de tipo Blog en SharePoint 2010.

 

Escenario:

Supongamos que queremos aplicar una Página Maestra que hemos creado con un diseño personalizado a nuestro sitio de tipo blog. En principio, para cualquier sitio de publicación, esto se haría activando la característica de publicación en el propio sitio.

 

Posteriormente, habría que seleccionar la “Página Maestra para el SItio” para poder visualizar nuestro diseño personalizado.

Pero, en un sitio de tipo Blog, esto parece no funcionar y continuaremos viendo el diseño con la Página Maestra por defecto.

 

Solución:

Aunque parezca un poco extraño (por lo menos a mi), los sitios de tipo Blog utilizan la Página Maestra del Sistema, con lo que, volviendo a la zona de selección de la página maestra, seleccionamos la que deseamos aplicar, en la sección de “Página Maestra del Sistema”… e voilà! Ya tenemos nuestro sitio de tipo Blog con el diseño personalizado.

SharePoint 2010: Fijar el pie de página (Footer) en un sitio de publicación con jQuery

Hola a todos.

En este artículo explicaré cómo fijar el pie de página en un sitio de publicación de SharePoint 2010 mediante el uso de jQuery cuando el contenido sea inferior al área de visualización del navegador.

 

Escenario

Supongamos que tenemos un sitio de publicación de SharePoint 2010, con un diseño que incluye un pie de página (atractivo o no) con opciones, texto, iconos,… y todo lo que se nos ocurra. En caso de que, en alguna de las páginas, el contenido no logre rellenar el área de visualización del navegador, ese pie, en el que tanto hemos trabajado para que se integre en el diseño, se verá inmediatamente después del contenido, dejando un espacio desaprovechado entre él y el pie del navegador.

 

Solución (jQuery)

En un principio pensé en gestionarlo por mi mismo, pero… dado que SharePoint 2010 hace su propio “resize” de elementos dentro de la página, tuve que buscar información acerca de qué elementos podía tratar. Además, hay que tener en cuenta la Ribbon que es un elemento que puede aparecer o no.

Por lo tanto, he añadido a mi masterpage el siguiente script.

<script type="text/javascript">

    jQuery(document).ready(function () {

     // on resize

     jQuery(window).resize(function (e) {

         fixFooterPosition();

     });

     // on load

     fixFooterPosition();

    });

  

    function fixFooterPosition() 

     var ribbonH = jQuery("#s4-ribbonrow").height();

     var headerH = jQuery("#divHeader").height(); 

     var footerH = jQuery("#divFooter").height(); 

     var windowHeight = jQuery(window).height();

      

     var h = windowHeight - headerH - footerH - ribbonH; 

      

     var bodyContainer = jQuery("#s4-bodyContainer"); 

      

     if (h >= bodyContainer.height()) 

        {

         bodyContainer.height(h);        

     }

     else 

        {

         // reseteo de la propiedad height para que se auto ajuste

         bodyContainer.css("height","auto");

     }

    }

</script>

 

Explicación del script

  • Obtener alto de los elementos del área de trabajo: Lo que hago es sumar el tamaño de los elementos que componen el espacio de trabajo de SharePoint, exceptuando el bloque de contenido #s4-bodyContainer de la página. En mi caso, la cinta #s4-ribbonrow, la cabecera de mi sitio #divHeader y el pie de página #divFooter.
  • Obtener alto del área de visualización del navegador: Además, obtengo el ato del área de contenido del navegador windowHeight
  • Cálculo del alto que debería tener el bloque de contenido de la página #s4-bodyContainer: Esta altura deberá ser igual (salvo algún ajuste) al alto del área de visualización del navegador, menos el alto de los elementos que componen el área de trabajo de la página #s4-ribbonrow, #divHeader, #divFooter
  • Establecimiento del alto del bloque de contenido #s4-bodyContainer: Si el alto del bloque de contenido de la página es inferior al resultado de la operación anterior, querrá decir que el pie de la página se mostrará con un espacio entre él y el pie del área de visualización del navegador, luego tendremos que fijar ese algo con el resultado de la operación. En caso de que no sea así, se resetea el valor de la propiedad height para que se auto-ajuste
  • Ejecución de la función de fijado del pie de página: Esta función se debe ejecutar en dos situaciónes
    • En la carga de la página: Se deja la llamada a la función en la ejecución principal de $(document).ready()
    • Cuando se redimensione el navegador: Se maneja el evento “resize” de la ventana del navegador para que siempre que suceda se ejecute la función.

 

Fuente

http://spbrianarnold.wordpress.com/2011/04/12/sharepoint-2010-fixed-width-masterpage-with-sticky-footer/

SharePoint 2010: Mostrar lista de Variaciones en un Sitio de Publicación.

Escenario:

Supongamos que tenemos un Sitio de Publicación de SharePoint 2010 en el que hemos creado diferentes variaciones correspondientes a los idiomas inglés, español, italiano y alemán. Necesitaremos mostrar los diferentes idiomas de alguna forma para que el usuario que navega por el sitio pueda seleccionar el que más le convenga.

 

Solución:

Para dar solución a esta situación, propongo la creación de un control de usuario de SharePoint con Visual Studio 2010 cuyo funcionamiento sea el siguiente.

NOTA: La razón de usar un user control es la posibilidad de insertarlo en la masterpage.

Para empezar, debemos instanciar el control de SharePoint correspondiente al DataSource de las variaciones dentro de la página .ascx correspondiente a nuestro control de usuario.

<PublishingWebControls:VariationDataSource ID="CustomLabelMenuDataSource" LabelMenuConfiguration="1" Filter="" runat="server" />

 

Como se puede apreciar, le he asignado el ID CustomLabelMenuDataSource que nos hará falta más adelante. Además, la propiedad LabelMenuConfiguration la he establecido a 1 para que enlace con la página correspondiente en la variación seleccionada

 

Ahoa ya tenemos el DataSource que contiene el listado de variaciones del sitio y sólo necesitamos mostrarlo en pantalla. Para ello, haré uso de un ListView de asp para visualizarlo en una lista desordenada ul.

<asp:ListView ID="LanguageListView" runat="server" DataSourceID="CustomLabelMenuDataSource">

    <LayoutTemplate>

        <ul class="ulLanguages">

            <asp:PlaceHolder runat="server" ID="itemPlaceHolder"></asp:PlaceHolder>

        </ul>

    </LayoutTemplate>

    <ItemTemplate>

        <li class="liLanguage">

            <a class="aLanguage" href="<%# DataBinder.Eval(Container.DataItem, "NavigateUrl") %>">

                <%# DataBinder.Eval(Container.DataItem, "DisplayText") %>

            </a>

        </li>

    </ItemTemplate>

</asp:ListView>

 

El ListView recibe como origen de datos el control anteriormente declarado, esto se consigue estableciendo la propiedad DataSourceId con el valor CustomLabelMenuDataSource que corresponde con el ID que indicamos en el primer paso.

En el LayoutTemplate del ListView, indicamos que se va a mostrar un “ul” que contiene los items.

En el ItemTemplate, establecemos los elementos de lista “li” con que contendrán un enlace “a” que apunta a la propiedad “NavigateUrl” de cada variación y el texto mostrado será la propiedad “DisplayText” de la misma.

 

Una vez tengamos esto, sólo habrá que ponerlo bonito mediante CSS para lo que he establecido los atributos class de cada uno de los elementos.

SharePoint 2010: Proveedor de Autenticación Personalizado (Custom Membership Provider)

Hola a todos.

Hoy, tras mis breves vacaciones voy a exponer una cuestión bastante común sobre la autenticación de usuarios en nuestros sitios de SharePoint.

 

Escenario

En ocasiones se nos hace necesario autenticar usuarios contra almacenes externos a SharePoint o, simplemente realizar la autenticación de una forma diferente a la habitual. En este caso, haré una exposición de autenticación contra una fuente de datos externa.

 

Solución

Para empezar, lo primero es que nuestra aplicación web debe tener activada la autenticación basada en claims y la autenticación basada en formularios (FBA).

 

auth1

auth2

 

Lo siguiente es crear una clase MyMembershipProvider que herede de System.Web.Security.MembershipProvider.

public class MyMembership : System.Web.Security.MembershipProvider

{

    ...

}

 

Hecho esto, nos aparecerá subrayado “System.Web.Security.MembershipProvider” debido a que es necesario implementar sus métodos ya que se trata de una clase abstracta. Esta primera parte está descrita en el siguiente artículo de MSDN http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx.

Una vez los hayamos implementado (Click con el botón derecho del ratón –> Implementar clase abstracta), habrá que determinar qué métodos son necesarios para el correcto funcionamiento de nuestra autenticación y, escribir el código necesario. Como mínimo, se debe tener en cuenta los siguientes métodos:

  • FindUsersByEmail
  • FindUsersByName
  • GetUser(string, bool)
  • GetUser(object, bool)
  • GetUserNameByEmail
  • ValidateUser

 

Para el ejemplo, mi clase queda de la siguiente forma.

public class MyMembership : System.Web.Security.MembershipProvider

{

    private string pApplicationName = "";

 

    public override string ApplicationName

    {

        get { return pApplicationName; }

        set { pApplicationName = value; }

    }

 

    private int pMaxInvalidPasswordAttempts = 0;

 

    public override int MaxInvalidPasswordAttempts

    {

        get { return pMaxInvalidPasswordAttempts; }

    }

 

    private int pMinRequiredNonAlphanumericCharacters = 0;

 

    public override int MinRequiredNonAlphanumericCharacters

    {

        get { return pMinRequiredNonAlphanumericCharacters; }

    }

 

    private int pMinRequiredPasswordLength = 0;

 

    public override int MinRequiredPasswordLength

    {

        get { return pMinRequiredPasswordLength; }

    }

 

    private int pPasswordAttemptWindow = 0;

 

    public override int PasswordAttemptWindow

    {

        get { return pPasswordAttemptWindow; }

    }

 

    private MembershipPasswordFormat pPasswordFormat = MembershipPasswordFormat.Clear;

 

    public override System.Web.Security.MembershipPasswordFormat PasswordFormat

    {

        get { return pPasswordFormat; }

    }

 

    private string pPasswordStrengthRegularExpression = "";

 

    public override string PasswordStrengthRegularExpression

    {

        get { return pPasswordStrengthRegularExpression; }

    }

 

    private bool pRequiresQuestionAndAnswer = false;

 

    public override bool RequiresQuestionAndAnswer

    {

        get { return pRequiresQuestionAndAnswer; }

    }

 

    private bool pRequiresUniqueEmail = false;

 

    public override bool RequiresUniqueEmail

    {

        get { return pRequiresUniqueEmail; }

    }

 

    private bool pEnablePasswordReset = false;

 

    public override bool EnablePasswordReset

    {

        get { return pEnablePasswordReset; }

    }

 

    private bool pEnablePasswordRetrieval = false;

 

    public override bool EnablePasswordRetrieval

    {

        get { return pEnablePasswordRetrieval; }

    }

 

    public override bool ChangePassword(string username, string oldPassword, string newPassword)

    {

        throw new NotImplementedException();

    }

 

    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)

    {

        throw new NotImplementedException();

    }

 

    public override System.Web.Security.MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status)

    {

        throw new NotImplementedException();

    }

 

    public override bool DeleteUser(string username, bool deleteAllRelatedData)

    {

        throw new NotImplementedException();

    }

 

    public override System.Web.Security.MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)

    {

        // Valido que el email introducido contenga la @

        if (!emailToMatch.Contains("@"))

        {

            totalRecords = 0;

            return null;

        }

 

        // Mediante el uso de un servicio WCF obtengo los usuarios que coincidan con el email especificado.

        using (var service = new WCFUser.ServiceUserClient())

        {

            var users = service.FindByEmail(emailToMatch);

            MembershipUserCollection mUsers = new MembershipUserCollection();

 

            foreach (WCFUser.User user in users)

            {

                MembershipUser mUser = new MembershipUser("MySecurityMembershipProvider",

                                                         user.Name,

                                                         user.Username,

                                                         user.Email,

                                                         "",

                                                         "",

                                                         true,

                                                         false,

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime());

                mUsers.Add(mUser);

            }

            totalRecords = mUsers.Count;

            return mUsers;

        }

    }

 

    public override System.Web.Security.MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)

    {

       // Mediante el uso de un servicio WCF obtengo los usuarios que coincidan con el username especificado.

       using (var service = new WCFUser.ServiceUserClient())

        {

            var users = service.FindByName(usernameToMatch);

            MembershipUserCollection mUsers = new MembershipUserCollection();

 

            foreach (WCFUser.User user in users)

            {

                MembershipUser mUser = new MembershipUser("MySecurityMembershipProvider",

                                                         user.Name,

                                                         user.Username,

                                                         user.Email,

                                                         "",

                                                         "",

                                                         true,

                                                         false,

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime(),

                                                         DateTime.Now.ToLocalTime());

                mUsers.Add(mUser);

            }

            totalRecords = mUsers.Count;

            return mUsers;

        }

    }

 

    public override System.Web.Security.MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)

    {

        throw new NotImplementedException();

    }

 

    public override int GetNumberOfUsersOnline()

    {

        throw new NotImplementedException();

    }

 

    public override string GetPassword(string username, string answer)

    {

        throw new NotImplementedException();

    }

 

    public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline)

    {

        // Mediante el uso de un servicio WCF obtengo el usuario.

        using (var service = new WCFUser.ServiceUserClient())

        {

            WCFUser.User user = service.GetUser(username);

            MembershipUser mUser = new MembershipUser("MySecurityMembershipProvider",

                                                     user.Name,

                                                     user.Username,

                                                     user.Email,

                                                     "",

                                                     "",

                                                     true,

                                                     false,

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime());

            return mUser;

        }

    }

 

    public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline)

    {

        // Mediante el uso de un servicio WCF obtengo el usuario.

        using (var service = new WCFUser.ServiceUserClient())

        {

            WCFUser.User user = service.GetUser(providerUserKey.ToString());

            MembershipUser mUser = new MembershipUser("MySecurityMembershipProvider",

                                                     user.Name,

                                                     user.Username,

                                                     user.Email,

                                                     "",

                                                     "",

                                                     true,

                                                     false,

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime(),

                                                     DateTime.Now.ToLocalTime());

            return mUser;

        }

    }

 

    public override string GetUserNameByEmail(string email)

    {

        // Mediante el uso de un servicio WCF obtengo el usuario.

        using (var service = new WCFUser.ServiceUserClient())

        {

            return service.GetUsernameByEmail(email);

        }

    }

 

    public override string ResetPassword(string username, string answer)

    {

        throw new NotImplementedException();

    }

 

    public override bool UnlockUser(string userName)

    {

        throw new NotImplementedException();

    }

 

    public override void UpdateUser(System.Web.Security.MembershipUser user)

    {

        throw new NotImplementedException();

    }

 

    public override bool ValidateUser(string username, string password)

    {

        // Mediante el uso de un servicio WCF realizo la validación del usuario

        using (var service = new WCFUser.ServiceUserClient())

        {

            return service.Validate(username, password);

        }

    }

}

 

Finalmente, hay que crear el Proveedor de Roles (RoleProvider) de la misma forma que el MembershipProvider. Para ello creamos la clase MyRoleProvider que hereda de System.Web.Security.RoleProvider. Al ser también una clase abstracta, habrá que implementar sus métodos.

En este caso, como mínimo debemos tener en cuenta los métodos

  • GetRolesForUser
  • RoleExists

 

Para el ejemplo, mi clase queda de la siguiente forma.

public class MyRoleProvider : System.Web.Security.RoleProvider

 {

 

     public override void AddUsersToRoles(string[] usernames, string[] roleNames)

     {

         throw new NotImplementedException();

     }

 

     private string pApplicationName = "";

 

     public override string ApplicationName

     {

         get { return pApplicationName; }

         set { pApplicationName = value; }

     }

 

     public override void CreateRole(string roleName)

     {

         throw new NotImplementedException();

     }

 

     public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)

     {

         throw new NotImplementedException();

     }

 

     public override string[] FindUsersInRole(string roleName, string usernameToMatch)

     {

         throw new NotImplementedException();

     }

 

     public override string[] GetAllRoles()

     {

         throw new NotImplementedException();

     }

 

     public override string[] GetRolesForUser(string username)

     {

        // Hago uso de un servicio para obtener los roles de un usuario

         using (var service = new WCFSecurity.ServiceRole())

         {

             var roles = service.GetRolesForUser(username);

             var result = new List<string>();

             foreach (WCFSecurity.Resort role in roles)

                 result.Add(role.Code);

             return result.ToArray();

         }

     }

 

     public override string[] GetUsersInRole(string roleName)

     {

         throw new NotImplementedException();

     }

 

     public override bool IsUserInRole(string username, string roleName)

     {

         throw new NotImplementedException();

     }

 

     public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)

     {

         throw new NotImplementedException();

     }

 

     public override bool RoleExists(string roleName)

     {

        // Hago uso de un servivio para comprobar si existe el rol

         using (var service = new WCFSecurity.ServiceRole())

         {

             return (service.RoleExists(roleName));

         }

     }

 }

 

Llegado este punto ya tenemos preparado nuestro proveedor de autenticación personalizado (custom membership provider) y sólo tendremos que integrarlo en SharePoint 2010 haciendo uso, para ello, de los web.config.

NOTA: No es nada recomendable tocar los archivos web.config de SharePoint (ni en general) directamente.

 

Para ello, debemos seguir los siguientes pasos:

  • Ubicar la dll de nuestro custom membership en el GAC. Para esto, debemos copiar la dll resultante de la compilación de nuestro proyecto en “C:Windowsassembly”.
    • Es muy importante, ver las propiedades de la dll tras copiarla en el GAC para quedarnos con la clave pública que se genera.
  • Editar el web.config de la aplicación web de SharePoint 2010 en la que queremos activar nuestra autenticación y añadir las siguientes referencias. Al añadirlas, se podrá asignar permisos a los usuarios o roles del custom membership provider desde la administración del sitio web
<membership …>

    <providers>

        <add name="MyMembership" 

             type="ProjectName.Namespace.MyMembership, DllName, 

             Version=1.0.0.0,Culture=neutral, PublicKeyToken=GAC_TOKEN" />

    </providers>

</membership>

 

Dentro de <roleManager…> <providers…></providers></roleManager>

<roleManager … enabled="true">

    <providers>

        ...

        <add name="MyRoleManager" 

             type="ProjectName.Namespace.MyRoleProvider, DllName, Version=1.0.0.0,                    

             Culture=neutral, PublicKeyToken=GAC_TOKEN" />

    </providers>

</roleManager>

 

  • Editar el web.config de la administración central de SharePoint 2010 las siguientes referencias con lo que se tendrá la posibilidad de agregar usuarios o roles desde el sitio de la administración central
<membership …>

  <providers>

    <add name="MyMembership" 

         type="ProjectName.Namespace.MyMembership, DllName, 

         Version=1.0.0.0,Culture=neutral, PublicKeyToken=GAC_TOKEN" />

  </providers>

</membership>

 
<roleManager … enabled="true">

    <providers>

        ...

        <add name="MyRoleManager" 

             type="ProjectName.Namespace.MyRoleProvider, DllName, Version=1.0.0.0,                    

             Culture=neutral, PublicKeyToken=GAC_TOKEN" />

    </providers>

</roleManager>

 

  • Editar el web.config del servicio de acceso de SharePoint 2010 las siguientes referencias con se podrá hacer el registro de los usuarios pertenecientes al custom membership provider, en las aplicaciones web. Este servicio se encuentra en el IIS en Sites->SharePoint WebServices->SecurityTokenServiceApplication
<system.web>

    <membership>

        <providers>

            <add name="MyMembership" 

                 type="ProjectName.Namespace.MyMembership, DllName, 

                 Version=1.0.0.0,Culture=neutral, PublicKeyToken=GAC_TOKEN" />

        </providers>

    </membership>

    <roleManager>

        <providers>

            <add name="MyRoleManager" 

                 type="ProjectName.Namespace.MyRoleProvider, DllName, Version=1.0.0.0,                    

                 Culture=neutral, PublicKeyToken=GAC_TOKEN" />

        </providers>

    </roleManager>

</system.web>

 

Con esto, ya debería funcionar nuestra autenticación personalizada en el sitio de SharePoint 2010.

 

Posibles incidencias

  • Error al hacer referencia al ensamblado del membership custom provider en el web.config
  • No poner las referencias en alguno de los web. Config
  • No tener referenciado el ensamblado en el GAC

 

Algunos enlaces interesantes:

SharePoint 2010: “Proveedor de mapa de sitio” personalizado para ocultar la Variación (Custom SiteMap Provider)

Escenario

En estos días nos encontramos el caso de tener que ocultar el nodo correspondiente a la variación en el camino de hormigas, migas de pan,… (breadcrumb).

Home > Español > MySite > MyPage

Esto era imposible hacerlo con las herramientas que tenemos por defecto mediante la configuración del control que estábamos usando

   1: <asp:SiteMapPath SiteMapProvider="SPContentMapProvider" id="ContentMap" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>

 

Solución

Como solución a nuestro problema, decidimos crearnos nuestro propio proveedor para el mapa del sition (Custom SiteMap Provider). Para ello, nos creamos un proyecto de SharePoint 2010 en Visual Studio 2010 y añadimos una clase que herede de “SiteMapProvider”

   1: public class Navigation : SiteMapProvider

 

Esto nos obliga a implementar ciertos métodos necesarios para el correcto funcionamiento del provider.

   1: public override SiteMapNode FindSiteMapNode(string rawUrl)

   2:  {

   3:      throw new NotImplementedException();

   4:  }

   5:  

   6:  public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)

   7:  {

   8:      throw new NotImplementedException();

   9:  }

  10:  

  11:  public override SiteMapNode GetParentNode(SiteMapNode node)

  12:  {

  13:      throw new NotImplementedException();

  14:  }

  15:  

  16:  protected override SiteMapNode GetRootNodeCore()

  17:  {

  18:      throw new NotImplementedException();

  19:  }

 
 
De estos métodos, el que más nos interesa es FindSiteMapNode, y es ahí donde debemos escribir nuestro código para evitar mostrar el nodo de la variación. En nuestro caso, lo que hemos hecho ha sido comparar el nombre del nodo con el de la variación actual y si coinciden, quitamos el nodo de la lista y enlazamos su nodo padre con su nodo hijo para que permanezca la relación.
 
De esta forma, el breadcrum nos quedará así: Home > MySite > MyPage
El código completo que escribimos es el siguiente.
   1: public class Navigation : SiteMapProvider

   2: {

   3:     private List<string> variations = new List<string>();

   4:     SiteMapNode rootnode;

   5:  

   6:     public Navigation()

   7:     {

   8:         ReadOnlyCollection<VariationLabel> allVariations = Variations.Current.UserAccessibleLabels;

   9:         foreach (VariationLabel item in allVariations)

  10:             variations.Add(item.DisplayName);

  11:     }

  12:  

  13:     private bool ExistsVariation(string Title)

  14:     {

  15:         return variations.Contains(Title);

  22:     }

  23:  

  24:     private void NodeToList(SiteMapNode node, ref SiteMapNodeCollection list)

  25:     {

  26:         if (node != null)

  27:         {

  28:             if (!ExistsVariation(node.Title))

  29:                 list.Add(node);

  30:             NodeToList(node.ParentNode, ref(list));

  31:         }

  32:     }

  33:  

  34:     private SiteMapNode GetNodeWithoutVar(SiteMapNode node)

  35:     {

  36:         SiteMapNodeCollection NodesCol = new SiteMapNodeCollection();

  37:         NodeToList(node, ref(NodesCol));

  38:  

  39:         for (int i = 0; i < NodesCol.Count - 1; i++)

  40:             NodesCol[i].ParentNode = NodesCol[i + 1];

  41:         return NodesCol[0];

  42:     }

  43:  

  44:     public override SiteMapNode FindSiteMapNode(string rawUrl)

  45:     {

  46:         PortalSiteMapProvider portalProvider = PortalSiteMapProvider.WebSiteMapProvider;

  47:         PortalSiteMapNode webNode = ((PortalSiteMapNode)portalProvider.FindSiteMapNode (rawUrl));

  48:         

  49:         return GetNodeWithoutVar(webNode.WebNode);

  50:     }

  51:  

  52:     protected override SiteMapNode GetRootNodeCore()

  53:     {

  54:         return rootnode;

  55:     }

  56:  

  57:     public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)

  58:     {

  59:         SiteMapNodeCollection children = new SiteMapNodeCollection();

  60:         if (node.HasChildNodes)

  61:             foreach (SiteMapNode cNode in node.ChildNodes) children.Add(cNode);

  62:         return children;

  63:     }

  64:  

  65:     public override SiteMapNode GetParentNode(SiteMapNode node)

  66:     {

  67:         return node.ParentNode;

  68:     }

  69: }

 
 
 
 

SharePoint 2010: Agregar un campo de Rich Text a una lista desde Visual Studio 2010 (C#)

En un anterior artículo expliqué cómo añadir un campo de tipo Publishing Image a una lista desde Visual Studio 2010. Hoy me explicaré cómo añadir un campo de texto enriquecido a una lista.

El escenario es el siguiente:

Supongamos que tenemos una lista que se crea desde código, de la que se alimenta un WebPart para mostrar información de los registros de esa lista pero que, además, se desea que en alguno de esos campos de la lista se pueda almacenar texto enriquecido para poder añadir enlaces, listas de html, maquetar el texto, etc.

 

Solución:

  • En primer lugar obtemenos el sitio en el que se encuentra la lista
SPWeb oWeb = SPContext.Current.Web;

 
  • Seguidamente tenemos que acceder a la lista en la que queremos añadir el campo.

SPList oList = web.Lists.TryGetList("NombreDeLaLista");

 
  • Seguidamente, debemos crear el campo.
HtmlField field = new HtmlField(Web.Fields, "HTML", "Nombre a mostrar");   

field.StaticName = "MyRichTextField";   

field.Title = "MyRichTextField";   

field.RichText = true;   

field.RichTextMode = SPRichTextMode.FullHtml;

 
  • Por último añadiremos el campo a la lista y, lo ponemos visible en la DefaultView
// Añadir el campo a la lista

oList.Fields.Add(field);

SPView view = oList.DefaultView;

 

// Añadir el campo a la vista por defecto de la lista

view.ViewFields.Add("MyRichTextField");

view.Update();

 

 

NOTA: No nos olvidemos de liberar las instancias que hemos utilizado.

 

Una vez seguidos estos pasos y ejecutado el código, la lista ya estaría preparada para que, en la ventana de edición-creación de un elemento podamos escribir texto enriquecido en el campo que acabamos de crear.

 

El código completo quedaría así:

// Obtener el sitio donde se encuentra la lista

SPWeb web = SPContext.Current.Web;

 

// Obtener la lista

SPList oList = web.Lists.TryGetList("NombreDeLaLista");

 

// Si existe la lista

if (oList != null)

{

    // Crear el campo

    HtmlField field = new HtmlField(Web.Fields, "HTML", "Nombre a mostrar");   

    field.StaticName = "MyRichTextField";   

    field.Title = "MyRichTextField";   

    field.RichText = true;   

    field.RichTextMode = SPRichTextMode.FullHtml;

    

    // Añadir el campo a la lista

    oList.Fields.Add(field);

    

    // Añadir el campo a la vista por defecto de la lista

    SPView view = oList.DefaultView;

    view.ViewFields.Add("MyRichTextField");

    view.Update();

}

 

web.Dispose();

oList = null;

 

 

El resultado final sería el siguiente

RichTextField1RichTextField2

 

Espero que os sirva de ayuda.

Sharepoint 2010: Linq To Sharepoint y los campos Publishing Image

Hola a todos.

Para los que no lo conocen, Linq To SharePoint nos permite conectar directamente a Listas de SharePoint, mediante un fichero con el nombre de nuesta lista «NombreLista.cs». Esto nos permite interactuar fácilmente con la lista, realizar consultas a los registros y acceder de una forma simple a los campos.

El problema con el que me he encontrado es que, Linq To Sharepoint» no enlaza los campos de tipo «Publishing Image» directamente y… nos tocará hacerlo a mano.

Los primero es identificar la clase «NombreListaItem» dentro del fichero «NombreLista.cs». En ella, se encuentran como propiedades las columnas de nuestra lista y, sólo deberemos añadir el siguiente código por cada una de los campos de tipo «Publishing Image» que hayamos añadido y sustituir los correspondientes nombres

    [System.Runtime.Serialization.DataMemberAttribute()]
    private Microsoft.SharePoint.Publishing.Fields.ImageFieldValue _myimage;

    [Microsoft.SharePoint.Linq.ColumnAttribute(Name = "MyImage", Storage = "_myimage", FieldType = "Image")]
    public Microsoft.SharePoint.Publishing.Fields.ImageFieldValue MyImage
    {  
        get 
        {
            return _myimage;
        }
        set
        {
            if (value != this._myimage)
            {
                this.OnPropertyChanging("MyImage", _myimage);
                this._myimage= value;
                this.OnPropertyChanged("MyImage");
            }
        }
    }

SharePoint 2010: Añadir un campo de tipo Publishing Image a una lista desde Visual Studio 2010 (C#)

 
En este artículo voy a explicar cómo añadir un campo de tipo
Publishing Image a una lista de SharePoint 2010.
 
He de indicar que es necesario que el Sitio sea de Publicación o que tengamos
activada la característica de Publicación en el Sitio.
 
Bueno, pongámonos en situación. Supongamos que tenemos una lista
que queremos que contenga el enlace a una imagen, como por ejemplo, una lista que
contenga los datos de productos y la foto correspondiente. Podríamos utilizar
un campo de texto o un campo de tipo URL para almacenar la ruta de la imagen.





Pero, se complica mucho tener que insertar los registros, ya que se debe conocer
esa ruta o buscarla para después insertarla. Sería más sencillo
utilizar un navegador de archivos con el que, visualmente, acceder hasta la imagen
deseada y, esto es precisamente lo que voy a explicar.
 
Lo primero de todo es añadir una referencia a la librería
Microsoft.SharePoint.Publishing
en nuestro proyecto de Visual
Studio
 
1: using Microsoft.SharePoint.Publishing;

 

Una vez añadida la referencia, pasemos a obtener la lista, para ello, primero
obtendremos acceso al sitio en el que se encuentra la lista. Una posible solución
sería:

1: SPWeb oWeb = SPContext.Current.Web;

 

Ahora que tenemos instanciado nuestro sitio, ya podemos obtener acceso a la lista:

1: SPList oList = web.Lists.TryGetList(
            "MyList");

 

Y posteriormente añadiremos el campo imagen, para lo que necesitamos hacer
lo siguiente.

   1:  ImageField field =
new
ImageField(Web.Fields, «Image»,
«My Image Field»
);

2: field.StaticName = "MyImage";
3: field.Title = "MyImage";
4: field.RichText = true;
5: field.RichTextMode = SPRichTextMode.FullHtml;
6:  
7: oList.Fields.Add(field);
 
 

Por último, agregamos este campo a la vista por defecto (DefaultView) de
la lista

1: SPView view = oList.DefaultView;
2: view.ViewFields.Add("MyImage");
3: view.Update();

 

NOTA: No nos olvidemos de liberar las instancias que hemos utilizado

 

Una vez seguidos estos pasos y ejecutado el código, la lista ya estaría
preparada para que, en la ventana de edición-creación de un elemento,
se pueda abrir un popup que nos permita navegar por la estructura de directorios
de nuestro sitio para seleccionar la imagen.

 

El código completo quedaría de la siguiente manera.

1: //Instanciar el sitio
2: SPWeb oWeb = SPContext.Current.Web;
3:     
4: //Instanciar la lista 
5: SPList oList = web.Lists.TryGetList(
            "MyList");   
6:     
7: //Comprobamos que exista la lista
        
8: if (oList != null)   
9: {  
10:     //Crear el campo de tipo imagen
        
11:     ImageField field = new ImageField(Web.Fields, "MyImage", "My Image Field");  
12:     field.StaticName = "MyImage"  
13:     field.Title = "MyImage";  
14:     field.RichText = true;  
15:     field.RichTextMode = SPRichTextMode.FullHtml;  
16:     
17:     //Añadir el campo a
            la lista 
18:     oList.Fields.Add(field);  19:     
19:     //Añadir el campo a
            la vista por defecto 
20:     SPView view = oList.DefaultView;  
21:     view.ViewFields.Add("MyImage");  
22:     view.Update();  
23:  }  
24:     
25:  web.Dispose();  
26:  oList = null;

 

Y el resultado final sería el siguiente:


 

 

 

Sharepoint 2010: Sitios de publicación (Introducción)

En Microsoft Sharepoint 2010 se dispone de Sitios de Publicación que, haciendo uso de sus características, nos proveen de las herramientas necesarias para gestionar contenidos web. Además, es muy fácil, personalizar para que se adecúe a la identidad corporativa de nuestra empresa.

Como ejemplo, el último sitio de publicación que he diseñado y que ha visto la luz. Este es muy sencillo http://www.silverpointtravelltd.com/en/Pages/default.aspx.

También he participado del diseño de este otro más complejo http://tenerife.silverpointhotelsandresorts.com/en/Pages/default.aspx

 

Mi experiencia anterior a SharePoint 2010

En mi dilatada experiencia (7 años desarrollando páginas y aplicaciones web) me he encontrado con muchos CMS y sistemas de generación de páginas que, aunque nos ayudan en nuestra labor, en ocasiones resultan un verdadero quebradero de cabeza. Diseños imposibles de ajustar, funcionalidades dificilmente aplicables, plugins y módulos a desarrollar con una complejidad muy alta,… Son algunas de las trabas con las que me he encontrado. Entre los CMS más destacados con los que he trabajado, puedo citar:

  • Mambo: Fue una gran revolución pero… al final, se convirtió más en un problema que en una ayuda. Los diseños más sencillos llevaban casi tanto trabajo como los diseños complejos, con lo que para clientes que únicamente querían una web simple con un diseño sencillo, se hacía más fácil generar un producto a medida y, para clientes que querían una web compleja, se ahorraba tiempo diseñando también un producto a medida en vez de adaptar el Mambo con plugins y módulos. Las tablas que usa en el diseño… una gran muralla que difícilmente se podía derribar.
  • Joomla!: Partiendo de un fork de Mambo, Joomla! mejoró considerablemente las interfaces y la integración de módulos, plugins y demás extensiones, pero… se me hacía demasiado complejo para cosas sencillas y, demasiado simple para cosas complejas. Otra vez la misma encrucijada que con Mambo, ¿realmente es rentable para webs simples o muy complejas? Finalmente tuve que optar por adaptar mi propio framework en PHP para poder integrarlo como un módulo de Joomla! y poder así sacarle rendimiento. Y seguimos teniendo tablas interminables que son como la gran muralla china en el diseño.
  • OpenCMS: Al principio me gustó mucho, pero rápidamente lo descarté por la imposibilidad que generar nuevos módulos rápidamente.
  • DiamondCMS y YafCMS: Estos dos CMS, fueron desarrollados haciendo uso de Castleproject como base tecnológica por Shidix Technologies, empresa con la que colaboré durante casi 5 años como autónomo, ayudando al desarrollo de estos productos. Estos dos CMS supusieron un gran avance para nosotros ya que, al haberlos hecho a nuestra medida, podíamos configurarlo, modificarlo, actualizarlo, ampliarlo,… a nuestra gusto de una forma rápida y sencilla. Además, el desarrollo de módulos y extensiones se consiguió simplificar gracias a una buena programación orientada a objetos. De esta forma, se consiguió tener un producto escalable, estable y bastante optimizado. Un claro ejemplo de su uso es la Universidad de La Laguna en la que se utilizó este sistema para la práctica totalidad de los portales web asociados (facultades, departamentos, escuelas, biblioteca, gabinete de prensa,…)
  • Mi propio CMS en PHP: Como última opción y para mi uso profesional como autónomo, desarrollé y sigo haciéndolo, un framework en PHP que se adapte a los requerimientos que la experiencia que tengo me ha enseñado que son los más necesarios y he obviado configuraciones a muy bajo nivel y simplificado las interfaces gracias al uso de jQuery. Pero… hay mucho que programar para que llegue a ser un producto completo y muchas cosas que no se podrán hacer por ser un lenguaje interpretado.

 

Mi experiencia con sitios de publicación de SharePoint 2010

Aunque sea tópico, mi relación con SharePoint 2010 fue un verdadero flechazo. Microsoft se disfrazó de cupido y me hizo la vida más sencilla. Desde el momento en que empecé a formarme hasta ahora, no he hecho más que descubrir las grandes ventajas. SharePoint 2010, es más que un CMS, más que un portal de intranet o gestor documental como algunos pueden creer. Realmente Sharepoint 2010 es una plataforma en la cuál, los sitios de publicación son una pequeña parte que funciona como CMS en su forma más básica pero, personalizados, son herramientas increíblemente útiles y con muchas posibilidades. Gracias a los Modelos de Objeto en el Servidor y los Modelos de Objeto en el Cliente, se pueden realizar desarrollos con una funcionalidad que antes con los CMS’s tradicionales no es posible. En cuanto al diseño, gracias al sistema de Masterpages de asp.NET, complementado con el sistema de Layouts de SharePoint 2010, es muy simple generar las diferentes interfaces de nuestros sitios web dando la posibilidad a los encargados de gestionar el contenido de personalizar las páginas del sitio web a su gusto, de una forma tan sencilla como seleccionar una plantilla, agregarle los WebParts que desee y editarlos, … todo ello acompañado de una experiencia WYSIWYG que les permitirá generar contenidos a un ritmo vertiginoso, optimizando su tiempo de trabajo.

 

Ventajas y Desventajas de los sitios de publicación de Sharepoint 2010 con respecto a CMS’s tradicionales

Ventajas

Como bien indicaba en mi experiencia con SharePoint 2010, ha múltiples ventajas, más incluso de las que describo:

  • Modelo de Objetos en el Cliente.
  • Modelo de Objetos en el Servidor.
  • Flujos de trabajo.
  • Manejadores de eventos y Trabajos de temporizador.
  • Sistema de Masterpages y Layouts.
  • Posibilidad de editar con Visual Studio 2010 o SharePoint Designer 2010.
  • Gestión de la seguridad desde SharePoint 2010.
  • Integración con Directorio Activo, membership’s personalizados, …
  • Backups desde Sharepoint.

Desventajas

Como en todo, también podemos encontrar desventajas entre las que destaco:

  • Alto coste de las licencias. Esta desventaja se puede subsanar gracias a Office 365 que incluye una versión de SharePoint en la nube con un coste muy bajo, acorde con las necesidades de los clientes.
  • Pocos desarrolladores familiarizados con SharePoint 2010. Sin embargo, la adaptación a la plataforma puede ser bastante rápida gracias a que en el fondo, sigue siendo .NET.

 

En resumen, en mi opinión, SharePoint es un producto excelente.

 

Bienvenidos!

Bienvenidos a mi blog sobre tecnologías Microsoft.

Me gustaría compartir mi experiencia diaria desarrollando con .Net, para SharePoint, Windows Azure y cualquier otra curiosidad que me parezca importante. Espero que os guste.