lunes, 10 de noviembre de 2008

Agregar quien respondio por última vez a un foro

Aprovechando la entrada de Miguel Angel (Post anónimo en lista de discusión), quería agregar una columna en los foros que me permitiese ver quien fue el último usuario que contesto a un hilo de discusión.
Para ello, agregue un nuevo campo al foro ("Última entrada creada por") y un código que se ejecuta junto con el evento que guarda una respuesta en el foro (todo esto viene explicado en el enlace anterior)



El código es el siguiente:

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
namespace EventHandler
{
public class ListItemAddedNew : SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
DisableEventFiring();
if (item["IsRootPost"].ToString() == "0")
{
// Nos conectamos como administrador para obtener la informaci¢n del nodo padre
SPUser userYo = item.Web.AllUsers["DOMINIO\\USUARIO ADMINISTRADOR"];
SPUserToken miSigno = userYo.UserToken;
SPSite impersSiteCollection = new SPSite(item.Web.Url, miSigno);
SPWeb impersWeb = impersSiteCollection.OpenWeb();
SPListItemCollection miListaItemCol = impersWeb.Lists[item.ParentList.ToString()].Folders;
SPListItem parentItem = miListaItemCol.GetItemById((int)item["ParentFolderId"]);
// Ahora nos conectamos como el usuario que creo el nodo padre para que
// no nos cambie el campo "Creado por"
string IdUser = parentItem["Author"].ToString().Substring(0,parentItem["Author"].ToString().IndexOf(";"));
SPUser userYo2 = item.Web.AllUsers.GetByID((int) System.Convert.ToInt32(IdUser, 10));
SPUserToken miSigno2 = userYo2.UserToken;
SPSite impersSiteCollection2 = new SPSite(item.Web.Url, miSigno2);
SPWeb impersWeb2 = impersSiteCollection2.OpenWeb();
SPListItemCollection miListaItemCol2 = impersWeb2.Lists[item.ParentList.ToString()].Folders;
SPListItem parentItem2 = miListaItemCol2.GetItemById((int)item["ParentFolderId"]);
parentItem2["éltima entrada creada por"] = item["Author"];
parentItem2.Update();
}
EnableEventFiring();
}
}
}


También podemos ocultar este campo para que solo se muestre en la vista de "Asunto" y no al insertar elementos en el foro. Esto se hace usando este componente gratuito que permite agregar permisos de usuario por cada una de las columnas de una lista o biblioteca de documentos (Descargar aquí)

Saludos

miércoles, 29 de octubre de 2008

Abrir los datos adjuntos de una lista en una nueva ventana y cambiar el literal "Datos adjuntos"

Las cosas pequeñas son las que al final mas trabajo nos dan. Esto me llevo un día entero y al final lo tuvo que resolver el de siempre, Miguel Ángel.

Si tenemos una lista en sharepoint, y a un elemento le hemos agregado datos adjuntos, cuando vemos las propiedades del elemento y pinchamos en el enlace del adjunto, si el dato adjunto se puede abrir directamente en el explorador de internet (*.jpg, *.pdf, etc), el enlace se nos abre en la ventana actual.

Para modificar esto, basta con agregar un pequeño javascript en la página DispForm.aspx de la lista que deseamos modificar. Abrimos la pagina con Sharepoint Designer y agregamos este código:

Si ademas queremos modificar la etiqueta por defecto "Datos adjuntos", debemos agregar en este mismo script la siguiente línea:

document.getElementById("idAttachmentsRow").childNodes[0].innerHTML="Descárgate los adjuntos";

Ambos script debe incluirse aquí:


Saludos y de nuevo muchas gracias por su ayuda a Miguel Ángel.

martes, 21 de octubre de 2008

Editar formulario de InfoPath en pagina customizada

En el post anterior hemos conseguido incrustar nuestro InfoPath dentro de una página customizada de SharePoint el siguiente problema es que al intentar editar dicho formulario tenemos que ir a la vista de la librería de documentos, hacer clic en el vínculo del InfoPath y el formulario se abre en la página de _layouts/FormServer.aspx por lo cual la masterpage se pierde mostrándonos una página en blanco con el formulario en medio. Esto hace perder la navegación e incluso hacer pensar que te has salido de tu sitio.

La vista normal seria asi, el usuario hace clic en el elemento:


Al hacer clic se veria esto (página en blanco):


La solución que encontré, de nuevo no es la más óptima pero funciona, es modificar la página DispForm.aspx de dicha librería. Esto hara que al hacer clic en "Ver propiedades" de los elementos abra el Infopath.

Al crear la librería de documentos que contiene el InfoPath automáticamente te crea 2 páginas para mostrar las propiedades de los elementos (DispForm.aspx y EditForm.aspx), lo que vamos a hacer es incrustar un iframe dentro de DispForm.aspx para que se muestre el formulario seleccionado por medio de la ayuda de una función de javascript.

Inicialmente la página DispForm.aspx muestra los campos que pueden ser editados desde SharePoint del campo seleccionado, por Url se envía el ID del elemento, sin embargo el ID no nos sirve de mucho ya que nosotros requerimos la url del InfoPath almacenado.





Como se ve en la imagen nosotros tenemos dentro de una tabla el nombre del formulario InfoPath, y eso es justo lo que necesitamos. Para ello usaremos javascript para obtener ese nombre y junto con la ruta de la librería formar la url del formulario que deseamos mostrar.

Abriremos nuestro SharePoint Designer en el sitio donde esta nuestra librería, nos vamos a la carpeta que genera, en este caso se llama Notificaciones.



Crear una copia de la página DispForm.aspx para tener un backup en caso de algún error y desprotegerla para comenzar a editar. Abrirla y agregar este código en la parte de arriba justo debajo del primer <table> que se crea:

<tr><td><iframe marginwidth="0" marginheight="0" id="marco" src="" frameborder="0" width="99%" scrolling="no" height="600"></iframe></td></tr>



Lo que estamos haciendo es crear un iframe que será contenedor del formulario de InfoPath. Debajo de nuestro código estarán los valores que se muestran en la página, si nosotros no queremos que nuestros usuarios vean esa tabla hay que agregar un div para hacerlos invisibles quedando de esta forma:

<div style="display:none">
<WebPartPages:WebPartZone
…..
</WebPartPages:WebPartZone>
<div>


Asi conseguimos que el webpart que muestra la tabla con los campos editables por SharePoint sea invisible para el usuario.

Lo que nos quedaría sería obtener el nombre del formulario, formar la url completa y hacer que el iframe lo abra, para eso debemos poner este código de javascript justo antes del tag </table> que cierra la tabla de html.


<tr><td> <script>
infopath = document.getElementById("formTbl").rows[0].cells["SPFieldFile"].innerHTML;
infopath = infopath.substring(infopath.indexOf("href=")+6,infopath.indexOf(".xml")+4);
if (document.getElementById("formTbl").rows.length > 0)
{
nextUrl = "
http://servidor/_layouts/FormServer.aspx?XmlLocation="
+infopath+
"&Source=
http://servidor/Libreria/Forms/AllItems.aspx&DefaultItemOpen=1";
document.getElementById("marco").src = nextUrl;
}
</script></td></tr>


Este script busca el id SPFieldFile que contiene el nombre de nuestro InfoPath y después generemos la URL para pasársela al Iframe llamado “marco” a su propiedad src. No olvides cambiar el servidor por el nombre de tu servidor.


Guardamos y el resultado es:



El formulario se muestra dentro de la pagina DispForm.aspx la cual tiene la apariencia de nuestro sitio y al cerrar el formulario se va a la pagina que hemos puesto en el código de javascript con el parámetro &Source.

Ahora solo nos faltaria crear una vista para que no sea necesario hacer clic en el menu del elemento, pero eso lo dejamos para otro post.

Saludos.

miércoles, 1 de octubre de 2008

Cerrar formulario de InfoPath con un redirect

Uno de los problemas mas fastidiosos de cuando tenemos un InfoPath dentro de una pagina web customizada en un sitio de SharePoint es cuando cerramos el formulario y nos muestra el tipico texto de:
Se ha cerrado el formulario.

Dando miles de vueltas al asunto encontre una opción, no la mas optima como siempre sucede en proyectos de sharepoint, pero al final una opción que hace que nos desaparezca ese mensaje y nos haga un redirect a la pagina que deseamos.

Primero bajar el InfoPath view webpart creado por Marwan Tarek en su blog, descargarlo de aqui: http://www.sharepointblogs.com/files/folders/marwantarek/entry13087.aspx

Una vez que se ha bajado hay que instalarlo en el servidor de SharePoint, la descarga tiene dos archivos:
- InfoPathFormViewer.wsp: Archivo de la solución web part.
- InfoPathFormViewer.bat: Archivo batch que deberá ser ejecutado en el servidor (editar el batch y reemplazar "http://londonmoss/" con el url de tu sitio)

Ya instalado deberas crear tu InfoPath con la opcion de web enable.

Crea una pagina customizada y dentro de una zona de web parts agrega el webpart de Marwan llamado InfoPathFormViewer. Modifica sus propiedades:

Html code: Codigo html que contiene el Iframe, ejemplo:
<div class="EForms">
<div class="EForms2">
<div class="EForms3">
<h5>form title</h5>
<div style="MARGIN-TOP: 10px; TEXT-ALIGN: center">
<iframe marginwidth="0" marginheight="0" src="%formurl%" frameborder="0" width="99%" scrolling="no" height="600">info path here</iframe>
</div>
</div>
</div>
</div>


Form URL: La url del formulario, ejemplo:
http://moss.dep/English/_layouts/FormServer.aspx?XsnLocation=http://moss.dep/English/Meeting%20Request%20English/Forms/template.xsn&amp;SaveLocation=http%3A%2F%2Fmoss%2Edep%2FEnglish%2FMeeting%20Request%20English&amp;DefaultItemOpen=1

Una vez hecho eso el fomulario Infopath sera visible desde la pagina customizada, ahora deberemos crear otra pagina html que contendrá un redirect.

Crear la nueva pagina llamada Redirect.html con este contenido dentro del head:

<script language="javascript" type="text/javascript">
window.open('/Paginas/Inicio.aspx', '_top');
</script>


Ahora solo deberemos agregar el parametro de Source dentro de la cadena de la url del form. Ir a la pagina customizada donde se encuentra el formulario de Infopath y editar sus propiedades, en la opcion de Form URL agregar este texto: &Source=/Paginas/Redirect.html

Por lo que nos quedaria de esta forma:

Form URL
http://moss.dep/English/_layouts/FormServer.aspx?XsnLocation=http://moss.dep/English/Meeting%20Request%20English/Forms/template.xsn&amp;SaveLocation=http%3A%2F%2Fmoss%2Edep%2FEnglish%2FMeeting%20Request%20English&amp;Source=/Paginas/Redirect.html&amp;DefaultItemOpen=1

Lo que hace es que al cerrar el formulario se va directamente a esa pagina, pero al estar dentro de un Iframe es necesario crear una pagina intermedia que haga un redirect al target top.

Y lo que da de resultado es que al cerrar o enviar el formulario nos manda a la pagina que hemos puesto dentro de la pagina de redirect.html.

Saludos

miércoles, 20 de agosto de 2008

Post como anónimo en lista de discusión

Los campos Autor y Modificador de elementos en Listas y Librerías no se pueden modificar por ser de solo lectura, incluso por código nos regresa una excepción del tipo “read only”. Lo que yo necesitaba era poder hacer que una lista de discusión tuviera la capacidad de permitir a los usuarios ocultar su identidad al momento de generar una pregunta o dar respuesta dentro de la lista.

La verdad busque un buen rato por internet pero encontré muy poco que me sirviera de ayuda por lo que se me ocurrió otra alternativa no del todo optima pero funcional, llamémosle chapusa.
A la lista de discusión le agregue un campo llamado Anónimo del tipo “Si o No”.

De esta forma tengo manera de controlar si el usuario quiere que su nombre aparezca como Autor del elemento o por el contrario quiere quedar como anónimo.

Ahora viene crear el manejador de evento de la lista, para saber cómo crear un evento para una lista ver este post Los eventos en WSS3 allí explica lo que son los eventos y los diferentes tipos que existen.

Deberemos sobrescribir el método ItemAdded, dentro tomaremos los valores que el usuario esta guardando en ese momento y en caso de que el usuario haya seleccionado el check de “Anónimo”, utilizaremos un impersonalizador para crear un nuevo elemento por código con un usuario diferente al actual, para esto debemos tener ya un usuario creado llamado Anónimo (o como quieras que aparezca en los elementos), y una vez que creamos el nuevo elemento con el usuario impersonalizado solo nos queda borrar el elemento que el usuario actual agrego como nuevo.

Código:
public override void ItemAdded(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
if (item["Anonimo"].ToString() == "True")
{
DisableEventFiring();
SPUser userYo = item.Web.AllUsers["dominio\\Anonimo"];
SPUserToken miSigno = userYo.UserToken;
SPSite impersSiteCollection = new SPSite(item.Web.Url, miSigno);
SPWeb impersWeb = impersSiteCollection.OpenWeb();

if (item["IsRootPost"].ToString() == "0")
{
SPListItemCollection miListaItemCol = impersWeb.Lists["Discusion"].Items;
SPListItem parentItem = miListaItemCol.GetItemById((int)item["ID"]);
SPListItem nuevoItem = SPUtility.CreateNewDiscussionReply(parentItem);
nuevoItem["Categoria"] = item["Asunto"];
nuevoItem["Detalle"] = item["Cuerpo"];
nuevoItem["Anonimo"] = item["Anonimo"];
item.Delete();
nuevoItem.Update();
}
else
{
SPListItemCollection miListaItemCol = impersWeb.Lists["Discusion"].Items;
SPListItem nuevoItem = miListaItemCol.Add();
nuevoItem["Categoria"] = item["Asunto"];
nuevoItem["Detalle"] = item["Cuerpo"];
nuevoItem["Anonimo"] = item["Anonimo"];
item.Delete();
nuevoItem.Update();
}
EnableEventFiring();
}
}

Una vez que compila se deberá agregar firmar y agregar la dll al GAC del servidor.

Para asociar el evento con la lista de discusión que nos interesa podemos usar esta herramienta EventHandlerExplorer. (ejemplo de uso aquí).

Y ahora tenemos una lista de discusión con opción a postear comentarios como anónimos dentro de un site de sharepoint.

jueves, 7 de agosto de 2008

Quitar el acceso anónimo a un elemento de una lista o librería con permisos de acceso anónimo

Cuando tengamos una lista o librería de documentos con acceso anónimo habílitado en ella y queramos que uno varios de los elementos de esa lista no puedan ser visible para los usuarios anónimos, basta con editar los permisos de ese elemento y asi dejaran de heredar los permisos de la lista o librería.

Lo vemos mejor con un ejemplo

Tenemos la lista TEST. Configuramos sus permisos para que los elementos de la lista sean visibles a los usuario anónimos.

Entramos en permisos para esta lista

y despues de editar los permisos en Configuración --> Acceso anónimo



Marcamos ver elementos y aceptamos


Volvemos a la lista, y ahora vamos a quitar el acceso anónimo al Elemento 3. Desplegamos el menu del elemento en cuestión, y pinchamos en Administrar Permisos



Ahora basta con Editar los permisos

Este elemento no será visible para los usuarios anónimo.
Saludos
Emilio Albo

lunes, 4 de agosto de 2008

Iniciar automáticamente un workflow asociado a un formulario de Infopath con usuario anónimo

Aprovechando el post anterior y viendo que el administrador del sistema no inicia automaticamente un workflow al crearse un elemento, basta con agregar el siguiente código al codigo del post anterior (en rojo el código ya agregado en el post anterior).


folder.Files.Add(formName + ".xml", data, true);
//**************
// Iniciar workflow
//**************
Microsoft.SharePoint.SPListItem ListI = null;
foreach (Microsoft.SharePoint.SPFile FI in folder.Files)
{
//Inscripcion_Evento de Prueba_mm_mm
if (FI.Name.ToString() == formName + ".xml")
{
ListI = FI.Item;
//b = ListI.ID.ToString();
}
}
Microsoft.SharePoint.Workflow.SPWorkflowAssociation WFA = null;
foreach (Microsoft.SharePoint.Workflow.SPWorkflowAssociation wf in Library.WorkflowAssociations)
{
if (wf.Name.Equals("Nombre de tu workflow"))
{
WFA = wf;
}
}
foreach (SPWorkflow wf2 in ListI.Workflows)
{
if (wf2.ParentAssociation.Name.Equals("Nombre de tu workflow"))
{
ListI.Web.Site.WorkflowManager.RemoveWorkflowFromListItem(wf2);
}
}
try
{
Microsoft.SharePoint.Workflow.SPWorkflow instanceWF = site.WorkflowManager.StartWorkflow(ListI, WFA, WFA.AssociationData, true);
web.AllowUnsafeUpdates = false;
}
catch
{
}
finally
{
web.AllowUnsafeUpdates = false;
}

//**************

});
}


Basta con agregar este código para que se inicie automaticamente el workflow asociado al elemento.

Saludos

Enviar formularios de Infopath Form Services con usuario anónimo

Si queremos enviar un formulario de Infopath con un usuario anónimo, el formulario se debe enviar a través de un Web Services que se conecte a la biblioteca de documentos con una cuenta con privilegios de escritura en esa biblioteca.

Creamos un nuevo proyecto de Visual Studio 2005 (con 2008 no lo he hecho todavía), proyecto de C# de biblioteca de clases
El código es este:
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Workflow ;

namespace WebServiceAnonimo
{
[WebService(Namespace = "Aqui_tu_Namespace")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public void SubmitToFormLibrary(string siteName, string webName, string formLibraryName, string formName, string formXml)
{
SPSecurity.RunWithElevatedPrivileges(delegate
{
formName = formName.TrimEnd('.');
Microsoft.SharePoint.SPSite site = new Microsoft.SharePoint.SPSite(siteName + "/");
Microsoft.SharePoint.SPWeb web = site.OpenWeb(webName);
Microsoft.SharePoint.SPFolder folder = web.GetFolder(formLibraryName);
Microsoft.SharePoint.SPDocumentLibrary Library = (SPDocumentLibrary)web.Lists[formLibraryName];

web.AllowUnsafeUpdates = true;

UTF32Encoding encoder = new UTF32Encoding();
byte[] data = encoder.GetBytes(formXml);
folder.Files.Add(formName + ".xml", data, true);

});
}
}
}

Debemos publicar el web services en algún puerto distinto al que tengamos el MOSS.

Una vez hecho esto, debemos cambiar el método de envío de nuestro formulario de Infopath para que ahora sea a través de un web services. Abrimos el diseño del formulario de infopath, y creamos una conexión de envío del formulario que sea del tipo de Web Services.

Agregamos la dirección del web services y después seleccionamos la función.

Por último agregamos los valores de los parámetros del web services, que son:
Si la biblioteca de documentos asociada al formulario se encuentra en


http://localhost/Pruebas/Biblioteca


siteName --> Nombre del site, http://localhost/
webName --> Nombre del web, Pruebas
formLibraryName --> Nombre de la biblioteca, Biblioteca
formName --> Nombre con el que queremos que se almacene el formulario
formXML --> Aquí debemos marcar la opción de formulario completo


Es conveniente que estos datos se extraigan de etiquetas ocultar insertadas en el formulario

Pinchamos en siguiente y le damos un nombre a la conexión.
Ahora basta con asignar esta conexión con el método de envío del formulario (Herramientas --> Opciones de envio).

Saludos