Archivo

Archive for the ‘Microsoft Office SharePoint Server’ Category

Crear menú de acciones de SharePoint programáticamente


Cuando trabajamos con Windows SharePoint Services 3.0 y Microsoft Office SharePoint Server 2007, uno de los requerimientos más comunes con los que contamos es el de extender la interfaz gráfica de usuario para añadir o quitar acciones a la barra de menús de una lista o biblioteca.

image

Hacer esto es relatívamente sencillo usando acciones personalizadas, usualmente basta con seguir estos pasos:

1.- crear un feature que enlace con un manifiesto personalizado.

2.- crear el manifiesto personalizado el cual contendrá una acción personalizada similar a la que se muestra a continuación.

<CustomAction Id="UserInterfaceCustomActions.DocLibNewToolbar"
    RegistrationType="List"
    RegistrationId="101"
    GroupId="NewMenu"
    Rights="ManagePermissions"
    Location="Microsoft.SharePoint.StandardMenu"
    Sequence="1000"
    Title="MY DOCLIB NEW MENU TOOLBAR BUTTON">
    <UrlAction Url="/_layouts/CustomActionsHello.aspx?NewMenu"/>
</CustomAction>

3.- Publicar el feature en la granja y activarlo para el sitio deseado.

Easy peasy (relativamente hablando). Puedes ver más información en este enlace de MSDN, o en este artículo de Joe Ferner.

Pero hay veces en las que por cualquier motivo no podemos usar las acciones personalizadas: bien sea porque estamos desarrollando una página de aplicación, bien sea porque estamos creando un front-end totalmente diferente. En estos casos, querríamos mostrar la barra de menú programáticamente. ¿Qué hacer?

El primer paso es que necesitamos crear una clase que herede de Microsoft.SharePoint.WebControls.ToolBarMenuButton. Esta clase representa un menú en una barra de herramientas (por ejemplo, “New”, “Actions” o “Settings” en la imagen de arriba).

Lo primero que querremos hacer  hacer es seleccionar una acción por default. Para ello nos creamos una propiedad (DefaultAction) o algo así. Si no queremos acción por default, no hacemos nada y ya. La magia, en cualquier caso, ocurre cuando sobreescribimos CreateChildControls, de esta forma:

[SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
protected override void CreateChildControls()
{
    base.CreateChildControls();
    if (!string.IsNullOrEmpty(this._defaultAction))
    {
        base.MenuControl.MenuFormat = MenuFormat.ArrowSplitButton;
        base.MenuControl.ClientOnClickScript = this._defaultAction;
        base.MenuControl.HoverCellInActiveCssClass = "ms-splitbutton";
        base.MenuControl.HoverCellActiveCssClass = "ms-splitbuttonhover";
    }
}

Lo único que hacemos es ver si la propiedad _defaultAction contiene algo, en cuyo caso creamos la acción por defecto (por ello asignamos los estilos de split-button. La acción por default debe ser un JavaScript y debe estar presente en la página donde se ejecute esta acción (ya sea a través de un ClientScript.RegisterClientScriptBlock o algún otro método que elijas). Si nunca tendrás una acción por default, no hagas nada en CreateChildControls.

Lo siguiente que nos interesa es añadir los hijos a nuestro menú. Por ejemplo, el menú “New” sólo muestra un hijo: “Add Item”, mientras que “Actions” muestra cuatro: “Edit in Datasheet”, “Export to spreadsheet”, “View RSS feed” y “Alert me”. En mi ejemplo, voy a crear tres hijos: “Vista previa”, “Editar tema”, “Eliminar tema”.

protected override void AddMenuItems()
{
    base.AddMenuItem(string.Format("{0}Preview", ID),
            "Vista previa",
            "/_layouts/images/searchsettings.gif",
            "Vista preliminar de un tema seleccionado.",
            "Preview.aspx",
            string.Empty);

    base.AddMenuItem(string.Format("{0}Edit", ID),
            "Editar tema",
            "/_layouts/images/MenuEditPictures.gif",
            "Edita las propiedades de un tema seleccionado.",
            string.Empty,
            "EditItems();");

    base.AddMenuItem(string.Format("{0}Delete", ID),
            "Eliminar temas",
            "/_layouts/images/delitem.gif",
            "Elimina los temas seleccionados. ",
            string.Empty,
            "DeleteItems();");
}

Cada llamada a AddMenuItem es quien en realidad añade el hijo. Esta llamada tiene varios parámetros.

1.- En primer lugar, pasamos un ID. Esta puede ser una cadena de texto cualquiera, el chiste es que identifique de forma única al comando.

2.- El segundo parámetro es el texto de nuestro menú.

3.- El siguiente es la URL hacia la imagen que queremos mostrar. Yo me basé en las que ya existen dentro de la carpeta de layouts.

4.- Si queremos que al hacer clic el menú navegue hacia cierta página, usamos el cuarto parámetro para poner dicha URL. El primer elemento del ejemplo navegará hacia la página Preview.aspx.

5.- Por último, si en lugar de navegar queremos ejecutar un JavaScript, usamos el último parámetro. En mi caso, nótese que en la segunda y tercera opción del menú mando llamar a las funciones EditItems y DeleteItems, respectivamente, por lo que la página donde se renderize este control deberá incluir esas funciones o el asunto no funcionará.

Por cierto, puedes sobreescribir la propiedad Text para establecer el texto del menú.

He aquí el código completo de mi clase de ejemplo.

using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.WebControls;

namespace Menus
{
    class ActionsThemeMenu : ToolBarMenuButton
    {
        private string _defaultAction;

        public ActionsThemeMenu()
        {
            _defaultAction = string.Empty;
        }

        public override string Text
        {
            get { return "Acciones"; }
        }

        protected override void AddMenuItems()
        {
            base.AddMenuItem(string.Format("{0}Preview", ID),
                    "Vista previa",
                    "/_layouts/images/searchsettings.gif",
                    "Vista preliminar de un tema seleccionado.",
                    "Preview.aspx",
                    string.Empty);

            base.AddMenuItem(string.Format("{0}Edit", ID),
                    "Editar tema",
                    "/_layouts/images/MenuEditPictures.gif",
                    "Edita las propiedades de un tema seleccionado.",
                    string.Empty,
                    "EditItems();");

            base.AddMenuItem(string.Format("{0}Delete", ID),
                    "Eliminar temas",
                    "/_layouts/images/delitem.gif",
                    "Elimina los temas seleccionados. ",
                    string.Empty,
                    "DeleteItems();");
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            if (!string.IsNullOrEmpty(this._defaultAction))
            {
                base.MenuControl.MenuFormat = MenuFormat.ArrowSplitButton;
                base.MenuControl.ClientOnClickScript = this._defaultAction;
                base.MenuControl.HoverCellInActiveCssClass = "ms-splitbutton";
                base.MenuControl.HoverCellActiveCssClass = "ms-splitbuttonhover";
            }
        }
    }
}

Y ya con esto tenemos nuestro menú definido. Ahora lo que sigue es crear una instancia de la clase Microsoft.SharePoint.WebControls.ToolBar y en su colección Buttons, añadir el control que hemos creado:

ToolBar toolbar = Page.LoadControl("/_controltemplates/ToolBar.ascx") as ToolBar;
toolbar.Buttons.Controls.Add(new ActionsThemeMenu());
Controls.Add(toolbar);

En este ejemplo, creamos un cotrol ToolBar y le añadimos nuestro recién creada clase, en mi caso, ActionsThemeMenu. Y con eso tenemos. Abajo muestro una imagen de cómo se ve en acción nuestro menú.

image

Anuncios

Cómo buscar elementos en listas sin tener que usar CAML


Cuando hacemos desarrollo de componentes para SharePoint (WebParts, Application Pages, etc.) es muy común que tengamos que leer una lista (o biblioteca de documentos, galería de imágenes, etc.) para manipular suselementos. Puede ser, sin embargo, que queramos que la lectura se haga dependiendo de ciertos criterios. En otras palabras, queremos filtrar los elementos de la lista.

Hacer eso a mano (i.e. un foreach hacia SPList.Items) no suele ser una buena práctica, especialmente si nuestra lista contendrá muchos elementos. Naturalmente, la solución consiste en crearnos una consulta con CAML (Collaborative Application Markup Language, si la memoria no me falla) y buscar los elementos llamando a SPList.GetItems, como se muestra a continuación.

SPQuery query = new SPQuery();
query.Query = "[Mi consulta CAML]";

SPList list = SPContext.Current.Web.Lists["Mi Lista"];
SPListItemCollection items = list.GetItems(query);

// y hacer algo con items. 

Hacer consultas con CAML, sin embargo, es algo que lleva tiempo. El esquema de CAML para consultas tiene muchos elementos y la curva de aprendizaje es, en mi opinión, un tanto pronunciada. Por ello, muchas veces tenemos que usar herramientas como el U2U  CAML Query Builder para que nos ayude a generar la consulta.

Un truco que me ha dado resultado, y que incluso me da mucha más flexibilidad para poder configurar mi búsqueda posteriormente, consiste en crear una vista y programáticamente leer el CAML que ésta utiliza. Ya con el CAML, repetimos el paso del código anterior y listo.

Para lograr lo anterior, debemos a) obtener la lista sobre la que haremos la consulta, b) obtener la vista, usualmente por su nombre; c) obtener la consulta CAML de la vista a través de la propiedad Query, y d) con ese CAML crear un objeto SPQuery y ejecutar SPList.GetItems. Easy peasy:

SPList list = SPContext.Current.Web.Lists["Mi Lista"];
SPView view = list.Views["Mi vista"];
// alternativamente, si quieres usar la vista por defecto:
// SPView view = list.DefaultView;

SPQuery query = new SPQuery();
query.Query = view.Query;
// si analizas view.Query con el QuickWatch de Visual Studio
// pordrás ver el CAML de la vista...

SPListItemCollection items = list.GetItems(query);
// y hacer algo con los elementos encontrados. 

Este truco te permitirá, además, que el usuario (o administrador) de la lista modifique la vista en cuestión, sin tener que recompilar. Una buena técnica que últimamente me ha salvado trabajo de codificación y de estar lidiando con CAML…

Buscar los registros creados por el usuario actual


Cuando desarrollamos una solución utilizando SharePoint, al menos en mi experiencia, es común que nuestro cliente, en algún momento, nos haga el siguiente requerimiento: dada una lista o biblioteca determinada, mostrar sólamente los elementos que hayan sido creados por dicho usuario.

Hacer esto en SharePoint, por supuesto, no es problema alguno. Basta crear una vista, o modificar la vista por defecto, irnos a la sección de filtros y seleccionar el campo “Creado por”, es igual a, y en el valor, ponemos “[Yo]” (o “[Me]” en inglés).

Sin embargo, también es común que tengamos que aplicar la misma regla de negocio al momento de desarrollar nuestros componentes (por ejemplo, un WebPart con un SPGridView). Y para ello, ni modo de iterar sobre todos los elementos de una lista… mejor utilizar una consulta con CAML.

En particular, este es el CAML que nos interesa.

<Where>
    <Eq>
        <FieldRef Name="Author" />
        <Value Type="Integer">
            <UserID Type="Integer" />
        </Value>
    </Eq>
</Where>

El Where y el Eq no debe sorprendernos, son normalitos. La etiqueta FieldRef hace referencia al campo Author, que no es más que la columna de “Creado por”. Y en la etiqueta Value, hacemos dos cosas: primero, especificar que el campo será de tipo entero (naturalmente, puesto que se filtrará por el ID del usuario), y segundo, incluímos como etiqueta hija a UserID, también de tipo entero. La etiqueta UserId, por supuesto, hace referencia al usuario actualmente autenticado.

Luego entonces, el código para hacer realidad todo esto quedaría algo así:

SPQuery query = new SPQuery();
query.Query = @"<Where>
                                   <Eq>
                                       <FieldRef Name=""Author"" />
                                       <Value Type=""Integer"">
                                           <UserID Type=""Integer"" />
                                       </Value>
                                   </Eq>
                               </Where>";

SPList list = SPContext.Current.Web.Lists["Mi Lista"];
SPListItemCollection items = list.GetItems(query);

// y hacer lo que sea que hagamos con los elementos, 
// como llamar a GetData para obtener un DataTable
// y hacer un DataBind con un SPGridView... 

 

And be done with it…

Ejemplo sobre el uso de metadatos de SharePoint


Ayer puse un pequeño tip sobre cómo utilizar SPListItem.Properties y SPWeb.AllProperties para guardar información y que ésta no se muestre al usuario, de forma accesible. Pues bien, quisiera mostrarles esto que he hecho a fecha reciente para que vean cómo puede usarse.

Primero, les comento el requerimiento. Tengo una lista que contiene información sobre productos: código del material, nombre, descripción y una columna que indica los usuarios asignados a dicho producto. Posteriormente, es responsabilidad de cada usuario asignado a dicho producto, capturar diario un valor que refleje la cantidad de producto habido en el almacén.

Entonces, primero tengo mi lista. Ésta luce así.

image

Como pueden ver, es una lista normal y de hecho el sitio no tiene ninguna otra lista. Hasta aquí easy peasy. Ahora, necesitamos crear un Application Page que muestre los productos por fecha y permita capturar una cantidad. Para ello, tomamos un control calendario y un SPGridView, al cual le creamos la columna “Cantidad”. El código ASP del grid luce algo así:

<SharePoint:SPGridView ID="_grid" runat="server" 
    AutoGenerateColumns="false" DataSourceID="_mainSource">
    <Columns>
        <asp:CommandField ButtonType="Image" ShowEditButton="true" 
            EditImageUrl="/_layouts/images/edit.gif" 
            UpdateImageUrl="/_layouts/images/save.gif" 
            CancelImageUrl="/_layouts/images/delete.gif" />
        <SharePoint:SPBoundField HeaderText="Código de material" DataField="Code" />
        <SharePoint:SPBoundField HeaderText="Nombre del producto" DataField="Name" />
        <SharePoint:SPBoundField HeaderText="Descripción" DataField="Description" />
        <asp:TemplateField HeaderText="Cantidad">
            <ItemTemplate>
                <asp:Label runat="server" 
                    Text='<%# ((double)Eval("Quantity")).ToString("0.00") %>' />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="_quantityText" runat="server" Width="100px" 
                    Text='<%# Bind("Quantity") %>' />
                <br />
                <asp:RangeValidator runat="server" EnableClientScript="true" Type="Double"
                    MinimumValue="0" MaximumValue="9999999999" 
                    ControlToValidate="_quantityText" 
                    Text="Sólo se admiten números positivos" />
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Fecha">
            <ItemTemplate>
                <asp:Label runat="server" 
                    Text='<%# ((DateTime)Eval("Date")).ToShortDateString() %>' />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="">
            <ItemTemplate>
                <asp:HiddenField ID="_idHiddenField" runat="server" 
                    Value='<%# Bind("ID") %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <EmptyDataTemplate>
        <h3>No hay información a mostrar.</h3>
    </EmptyDataTemplate>    
</SharePoint:SPGridView>

Utilizo un ObjectDataSource como proveedor de datos, el cual manda llamar un método que retorna un DataTable, filtrado por la fecha seleccionada en el calendario. Éste método realiza lo siguiente:

1.- Obtiene una referencia a la lista (SPList) de los productos e itera sobre cada uno de ellos.

2.- Para cada producto (SPListItem) verifica que el usuario actualmente autenticado (SPContext.Web.CurrentUser) esté dentro de los elementos de la columna “Usuarios”.

3.- Crea una llave de texto con base en los siguientes datos: cuenta del usuario, guión bajo, la fecha del día seleccionado en el calendario.

private string GetDatedProductKey(string user, DateTime date)
{
    if (user == null)
        throw new ArgumentNullException("user");

    return string.Format("{0}_{1}{2}{3}", user, date.Year, date.Month, date.Day);
}

4.- Revisamos en el Hashtable “Properties” del producto (SPListItem) para ver si la clave existe. Si sí, obtenemos el valor y hacemos la conversión a un double. Si no, simplemente dejamos un 0.0.

string key = GetDatedProductKey(user, date);
if (product.Properties.Contains(key))
{
    string value = product.Properties[key] as string;
    double quantity;
    double.TryParse(value, out quantity);
    row["Quantity"] = quantity;
}
else
{
    bool allowUpdates = Web.AllowUnsafeUpdates;
    Web.AllowUnsafeUpdates = true;
    product.Properties.Add(key, "0.0");
    product.Update();
    Web.AllowUnsafeUpdates = allowUpdates;

    row["Quantity"] = 0.0;
}

5.- Añadimos una fila al DataTable, llenamos los datos y al final, retornamos la tabla. El SPGridView enlazará sin problemas.

Hacer lo anterior nos garantiza que la cantidad se guarde por fecha y usuarios diferentes. Es decir, para el 9 de mayo de 2011, asgard\loki muestra una cantidad de 10 unidades mientras que asgard\heimdall muestra 20. De esta manera almacenamos la información en el mismo elemento SPListItem, pero sin comprometer la información.

image

El proceso de hacer la actualización de la información en el SPGridView es muy similar a cualquier otro GridView. Baste decir que dicho control está enlazado con un método Update en el ObjectDataSource. Este método termina por modificar el valor de forma relatívamente fácil, usando la misma llave generada por la fecha y la cuenta del usuario.

public void UpdateDatedProducts(string user, DateTime date, int id, double quantity)
{
    SPListItem product = List.Items.GetItemById(id);
    string key = GetDatedProductKey(user, date);

    bool allowUpdates = Web.AllowUnsafeUpdates;
    Web.AllowUnsafeUpdates = true;
            
    if (!product.Properties.ContainsKey(key))
        product.Properties.Add(key, quantity.ToString("0.00"));
    else
        product.Properties[key] = quantity.ToString("0.00");
    product.Update();

    Web.AllowUnsafeUpdates = allowUpdates;
}

Y listo, easy peasy.

Apóyate en los metadatos de SharePoint


SharePoint tiene una herramienta peculiar para varios elementos, dentro del modelo de objetos. Muchos elementos tienen una propiedad a través de la cual es posible acceder a los metadatos de ese elemento. En particular, hay dos de éstos que son de mucha utilidad: SPWeb y SPListItem.

SPWeb tiene la propiedad AllProperties, mientras que SPListItem tiene la propiedad Properties. Ambas son de tipo Hashtable, así que es hora de desempolvar mi post sobre esa colección, si tienes dudas.

Ambas colecciones guardan metadatos, es decir, información adicional. SharePoint las guarda en la base de datos, como todo, y las carga al obtener el objeto dueño de los mismos. Esto los convierte en un perfecto lugar para guardar información relacionada o de configuración. Muchas veces esto es mejor que crear otras listas o guardarlas en el web.config.

La regla es así: si quieres guardar información a nivel del sitio, utiliza SPWeb.AllProperties. Por ejemplo:

SPContext.Current.Web.AllProperties.Add("Mi propiedad", "Mi valor");
SPContext.Current.Web.Update();
...
string value = SPContext.Current.Web.AllProperties["Mi propiedad"] as string;
Console.WriteLine(value); // imprime "Mi valor"

Para guardar información sobre un elemento de cualquier lista (incluyendo bibliotecas de documentos y galerías) usa SPListItem.Properties. Por ejemplo:

SPList list = SPContext.Current.Web.Lists["Mi lista"];
foreach (SPListItem item in list.Items)
{
    item.Properties.Add("Mi propiedad", "ID " + item.ID);
    item.Update();
}
...
foreach (SPListItem item in list.Items)
{
    string value = item.Properties["Mi propiedad"] as string;
    Console.WriteLine(value); // imprime "ID 1", etc
}

En general, es una buena idea usar SPWeb.AllProperties para cuestiones de configuración sobre el sitio (en lugar del web.config, por ejemplo) y usar SPListItem.Properties sobre información que no deberían ver los usuarios en la lista. Esto último, por ejemplo, es mejor que crear una columna en el SPList y ocultarla en las vistas.

Juega un rato con esto y verás qué tan útil resulta esto.

Cómo identificar si el usuario autenticado pertenece a un grupo de SharePoint


¿Recuerdan que el viernes les comentaba sobre cómo identificar si un usuario de SharePoint pertenece a un grupo cualquiera? A grandes rasgos, la única forma de hacerlo era mediante un proceso manual de obtener el usuario y buscar en sus grupos, o bien obtener el grupo y buscar sus usuarios: si no encontrábamos el elemento, no existía.

Pues bien, si ese usuario es el usuario actualmente autenticado, hay un camino más fácil: primero, obtenemos la referencia al SPGroup que queramos; y posteriormente, invocamos la propiedad SPGroup.ContainsCurrentUser. Esta propiedad nos devuelve true si el usuario autenticado pertenece a dicho grupo.

El codiguillo sería algo así:

private void MatchByGroup(HtmlTextWriter writer, string groupName, string userLogin)
{
    SPGroup spgroup = SPContext.Current.Web.SiteGroups[groupName];
    bool match = spgroup.ContainsCurrentUser;

    writer.Write("El grupo '{0}' contiene al usuario '{1}': {2}<br/>",
        spgroup.Name, userLogin, match);
}

Mucho más sencillo, ¿no?

Usando el control de búsqueda de usuarios


Cuando en una lista o biblioteca de documentos agregas un campo de tipo “Persona o Grupo”, al momento de editar el elemento verás un control como el que se muestra en la siguiente imagen, al lado de la fila “Cuenta”.

image

Este control cuenta con dos cosas útiles. La primera, cuando escribo algún nombre y hago clic en el primer iconito (el que muestra la palomita), el control valida si lo que escribí pertenece a algún usuario dentro de SharePoint / Directorio Activo. De ser así, lo muestra como tal, y en caso contrario lo marca como inválido.

image

La segunda funcionalidad es que, si no conocemos el nombre del usuario, podemos hacer clic en el segundo iconito (el del libro) y entonces un buscador aparecerá para ayudarnos a elegir alguno.

image

Sobra decir que este control es súmamente útil para buscar gente. Más aún, cuando hacemos algún componente para SharePoint, la gente esperará ver este control cuando tenga que ingresar gente, en lugar de un vil TextBox.

Afortunadamente, el control que hace todas estas monerías está disponible para nosotros en la clase PeopleEditor, ubicado en el espacio de nombres Microsoft.SharePoint.WebControls.

Utilizar esta clase es sencillo. Algunas propiedades son conocidas por nosotros, como AutoPostBack y Width. Otras son fáciles de adivinar: AllowEmpty nos permite determinar si el control debe quedar vacío o no (en caso de que sea false, si no ponemos algo el validador que lleva consigo nos lo hará saber); mientras que MultiSelect nos permite determinar si el control permitirá un solo elemento, o varios (el alto del control varía dependiendo de esto).

He aquí el un código de ejemplo que muestra lo anterior. Es un fragmento de un WebPart creado a guisa de ejemplo, perteneciente al método CreateChildControls.

_singleEditor = new PeopleEditor();
_singleEditor.ID = "_singleEditor";
_singleEditor.AutoPostBack = true;
_singleEditor.Width = new Unit(300, UnitType.Pixel);
_singleEditor.AllowEmpty = false;
_singleEditor.MultiSelect = false;

_multipleEditor = new PeopleEditor();
_multipleEditor.ID = "_multipleEditor";
_multipleEditor.AutoPostBack = true;
_multipleEditor.Width = new Unit(300, UnitType.Pixel);
_multipleEditor.AllowEmpty = true;
_multipleEditor.MultiSelect = true;

Todo más o menos sencillo, ¿no? Bueno, supongamos ahora que queremos pre-cargar nuestros controles con alguna información. Para ello, necesitamos el método UpdateEntities del control. Este método toma como parámetro un ArrayList (lo sé, lo sé, pero recordemos que WSS salió antes que .NET Frameowork 2.0), el cual debe contener cada uno de los usuarios a mostrar en el control. Cada entrada al arreglo debe ser de tipo PickerEntity, con su propiedad Key establecida al nombre del usuario o el de su cuenta. Por ejemplo:

PickerEntity entity = new PickerEntity();
entity.Key = "ASGARD\\freyja";
ArrayList entities = new ArrayList();
entities.Add(entity);
_singleLoadedEditor.UpdateEntities(entities);

En un control cuya propiedad MultiSelect sea true, podemos agregar varios elementos al arreglo. Por ejemplo, el siguiente código obtiene un grupo de SharePoint e itera sobre cada usuario, añadiéndolo al arreglo y por ende, al control PeopleEditor en cuestión.

entities = new ArrayList();
foreach (SPUser user in SPContext.Current.Web.SiteGroups["Asgard Æsir"].Users)
{
    entity = new PickerEntity();
    entity.Key = user.Name;
    entities.Add(entity);
}
_multipleLoadedEditor.UpdateEntities(entities);

¿A poco no gana en la vida? Así es como luce mi WebPart.

image

Chidito, ¿no? ¡Y profesional! Este es el código completo del WebPart mostrado en la imagen anterior.

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebControls;
using System.Web.UI.WebControls;
using System.Collections;

namespace Fermasmas.Wordpress.Com.WebParts
{
    public class PeopleEditorExample : WebPart
    {
        private PeopleEditor _singleEditor;
        private PeopleEditor _multipleEditor;
        private PeopleEditor _singleLoadedEditor;
        private PeopleEditor _multipleLoadedEditor;

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            _singleEditor = new PeopleEditor();
            _singleEditor.ID = "_singleEditor";
            _singleEditor.AutoPostBack = true;
            _singleEditor.Width = new Unit(300, UnitType.Pixel);
            _singleEditor.AllowEmpty = false;
            _singleEditor.MultiSelect = false;

            _multipleEditor = new PeopleEditor();
            _multipleEditor.ID = "_multipleEditor";
            _multipleEditor.AutoPostBack = true;
            _multipleEditor.Width = new Unit(300, UnitType.Pixel);
            _multipleEditor.AllowEmpty = true;
            _multipleEditor.MultiSelect = true;

            _singleLoadedEditor = new PeopleEditor();
            _singleLoadedEditor.ID = "_singleLoadedEditor";
            _singleLoadedEditor.AutoPostBack = true;
            _singleLoadedEditor.Width = new Unit(300, UnitType.Pixel);
            _singleLoadedEditor.AllowEmpty = false;
            _singleLoadedEditor.MultiSelect = false;

            _multipleLoadedEditor = new PeopleEditor();
            _multipleLoadedEditor.ID = "_multipleLoadedEditor";
            _multipleLoadedEditor.AutoPostBack = true;
            _multipleLoadedEditor.Width = new Unit(300, UnitType.Pixel);
            _multipleLoadedEditor.AllowEmpty = true;
            _multipleLoadedEditor.MultiSelect = true;

            Controls.Add(new Literal { Text = "Sencillo:<br/>" });
            Controls.Add(_singleEditor);
            Controls.Add(new Literal { Text = "<br/>Sencillo cargado:<br/>" });
            Controls.Add(_singleLoadedEditor);
            Controls.Add(new Literal { Text = "<br/>M&uacute;ltiple:<br/>" });
            Controls.Add(_multipleEditor);
            Controls.Add(new Literal { Text = "<br/>M&uacute;ltiple cargado:<br/>" });
            Controls.Add(_multipleLoadedEditor);

            PickerEntity entity = new PickerEntity();
            entity.Key = "ASGARD\\freyja";
            ArrayList entities = new ArrayList();
            entities.Add(entity);
            _singleLoadedEditor.UpdateEntities(entities);

            SPGroup group = SPContext.Current.Web.SiteGroups["Asgard Æsir"];
            entities = new ArrayList();
            foreach (SPUser user in group.Users)
            {
                entity = new PickerEntity();
                entity.Key = user.Name;
                entities.Add(entity);
            }
            _multipleLoadedEditor.UpdateEntities(entities);
        }
    }
}

Para finalizar, solo comentar una cosa curiosa con la que me topé cuando escribía el ejemplo de este texto: las llamadas a UpdateEntities tienen que hacerse después de agregar los controles al WebPart (es decir, después de hacer el Controls.Add). Si no, el UpdateEntities no reconocerá a los usuarios y presentará un comportamiento extraño. No sé por qué ocurre así, pero lo hace (haz la prueba si gustas). Si alguien sabe la explicación, compártala con el mundo.

¡Buen fin de semana!