jueves, 27 de agosto de 2009

Custom ASP.net Web Application in MOSS

Actualmente para un proyecto hemos decidido crear una aplicación web dentro del directorio _layouts de MOSS 2007, nos encontrabamos con el error siguiente al cargar una master page que esta ubicada en el mismo directorio _layouts:

The referenced file '/TEMPLATE/LAYOUTS/MyWebApp/MyMasterPage.master' is not allowed on this page. at System.Web.UI.TemplateParser.ProcessError(String message) at System.Web.UI.BaseTemplateParser.GetReferencedType(VirtualPath virtualPath, Boolean allowNoCompile) at System.Web.UI.PageParser.ProcessMainDirectiveAttribute(String deviceName, String name, String value, IDictionary parseData) at System.Web.UI.TemplateParser.ProcessMainDirective(IDictionary mainDirective)

Para solucionarlo hemos hecho lo siguiente:

- Modificar el atributo MasterPageFile de la directiva Page de nuestra custom .aspx indicandole la página maestra application.master.



MasterPageFile="~/_layouts/application.master"



- Luego en el fichero .cs (Code Behind) hemos asignado nuestra página maestra en el evento PreInit



protected void Page_PreInit(object sender, EventArgs e)
{
this.MasterPageFile = "~/_layouts/MyWebApp/MyMasterPage.master";
}

jueves, 28 de mayo de 2009

Permisos EventLog

Para permitir a los usuarios impersonados escribir en el eventlog, es necesario modificar una clave del registro de windows, en concreto:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\MyLogName\CustomSD

Para permitir a los usuarios autenticados permisos de escritura se ha de aññadir el siguiente permiso:
(A;;0x0002;;;AU)

Para permitir a un usuario específico permiso de escritura debermos especificar su SID:
(A;;0x0002;;;SID-OF-USER-ACCOUNT)

martes, 21 de abril de 2009

CopyUtil.aspx - Una gran Application Page

CopyUtil.aspx es una application page dentro de MOSS 2007 que nos permite navegar a un item sabiendo el id del item, el id de la lista, el id de la site y el id de la site collection, esto nos ahorra mucho trabajo a la hora de componer urls hacia los items.

Para ir a un item concreto tenemos que construir la siguiente URL:

http://server/_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId=X&ListId=X&WebId=X&SiteId=X

sustituyendo las X por los ID's correspondientes. El parámetro "action" puede ser sustituido por dispform o editform, para que nos rediriga a la URL que nos muestra el item o para que nos rediriga a la URL para editar el item.

jueves, 12 de febrero de 2009

Error wizard sharepoint

Tras instalar el SP1 de MOSS 2007 en una granja de servidores e intentar pasar el wizard de configuración me he encontrado con la siguiente excepción:

Microsoft.SharePoint.Administration.SPUpdatedConcurrencyException was thrown. Additional exception information: An update conflict has occurred, and you must re-try this action. The object DiagnosticsService Parent=SPFarm Name=SharePoint_Config is being updated by Domain\User, in the w3wp process, on machine SERVER. View the tracing log for more information about the conflict. Microsoft.SharePoint.Administration.SPUpdatedConcurrencyException: An update conflict has occurred, and you must re-try this action. The object DiagnosticsService Parent=SPFarm Name=SharePoint_Config is being updated by Domain\User, in the w3wp process, on machine SERVER. View the tracing log for more information about the conflict.

Al parecer esto sucede cuando hay un objeto bloqueado a nivel de SPFarm. Para solventar este problema hay que limpiar la cache de configuración de Windows SharePoint Services.

http://support.microsoft.com/kb/944267/en-us/

¿Y donde se encuentra esta cache?

Esta caché se encuentra en:

C:\Documents and Settings\All Users\Application Data\Microsoft\SharePoint\Config\[GUID]

La solución pasa por borrar todos los xml de esta carpeta [CUIDADO NO BORRAR EL ARCHIVO cache.ini], luego se ha de editar el archivo cache.ini y poner un 1 y guardar.

http://support.microsoft.com/kb/939308/en-us/

jueves, 5 de febrero de 2009

Copiar items y folders entre DocumentLibrary manteniendo sus campos

Me he encontrado en un proyecto en que unas librerías de sharepoint que fueron migradas de 2003 daban un error al intentar mover elementos usando el sitemanger. Vimos que esto no pasaba en las otras librerías por lo que tuvimos que hacer un script para mover documentos y folders de una librería a otra, eso sí manteniendo todos sus metadatos, aquí algunos de los pasos que hemos seguido:

1. Igualar los campos de las bibliotecas:


public static void IgualarCampos(SPDocumentLibrary origen, SPDocumentLibrary destino)
{
if (origen == null destino == null) throw new ArgumentException("Las bibliotecas destino y origen no pueden ser nulas");
try
{
List listaCamposDestino = new List();
foreach (SPField field in destino.Fields)
{
listaCamposDestino.Add(field);
}
// Compara con origen y crea las necesarias en destino
foreach (SPField field in origen.Fields)
{
SPField campoBuscado = listaCamposDestino.Find(delegate(SPField campo)
{
return campo.Id == field.Id;
});
// Si no existe lo añadimos
if (campoBuscado == null )
{
// Se crea campo en lista destino.
destino.Fields.Add(field);
}
}
}
catch (Exception ex)
{
throw ex;
}
}



2. Funciones recursivas para recorrer todos los folders y crearlos en el destino así como los items por folder:



public static void CopyFoldersAndItems(SPDocumentLibrary origen, SPDocumentLibrary destino)
{
if (origen == null destino == null) throw new ArgumentException("Las bibliotecas destino y origen no pueden ser nulas");
try
{
string urldestinofolders = destino.RootFolder.ServerRelativeUrl.ToString();
SPFolderCollection foldersdestino = destino.ParentWeb.GetFolder(urldestinofolders).SubFolders;
string urlorigenfolders = origen.RootFolder.ServerRelativeUrl.ToString();
SPFolderCollection foldersorigen = origen.ParentWeb.GetFolder(urlorigenfolders).SubFolders;
foreach (SPFile file in origen.RootFolder.Files)
{
SPFile newFile = destino.RootFolder.Files.Add(file.Name, file.OpenBinary(), file.Author, file.ModifiedBy, file.TimeCreated, file.TimeLastModified);
SPListItem newItem = newFile.Item;
foreach (SPField f in file.Item.Fields)
{
if (!f.ReadOnlyField) newItem[f.InternalName] = file.Item[f.InternalName];
}
newItem["Modificado"] = file.TimeLastModified;
newItem["Creado"] = file.TimeCreated;
newItem.UpdateOverwriteVersion();
}
CopyFoldersAndItems(foldersorigen, foldersdestino);
}
catch (Exception ex)
{
throw ex;
}
}
public static void CopyFoldersAndItems(SPFolderCollection origen, SPFolderCollection destino)
{
try
{
foreach (SPFolder folder in origen)
{
if (folder.Name != "Forms")
{
SPFolder nuevo = destino.Add(folder.Name);
foreach (SPFile file in folder.Files)
{
SPFile newFile = nuevo.Files.Add(file.Name, file.OpenBinary(), file.Author, file.ModifiedBy, file.TimeCreated, file.TimeLastModified);
SPListItem newItem = newFile.Item;
foreach (SPField f in file.Item.Fields)
{
if(!f.ReadOnlyField) newItem[f.InternalName] = file.Item[f.InternalName];
}
newItem["Modificado"] = file.TimeLastModified;
newItem["Creado"] = file.TimeCreated;
newItem.UpdateOverwriteVersion();
}
if (folder.SubFolders.Count > 0) CopyFoldersAndItems(folder.SubFolders, nuevo.SubFolders);
}
}
}
catch (Exception ex)
{
throw ex;
}
}


lunes, 2 de febrero de 2009

Obtener webparts de una página (MOSS 2007)

Aquí tenemos un ejemplo para obtener las webparts de una página de publicación,
una vez tenemos el tipo de la webpart podemos hacer un casting y acceder a sus propiedades para consultarlas y/o modificarlas.




using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using(SPSite siteCollection = new SPSite("http://localhost:44236"))
{
using(SPWeb site = siteCollection.OpenWeb())
{
using (SPLimitedWebPartManager manager = site.GetLimitedWebPartManager("Paginas/default.aspx", PersonalizationScope.Shared))
{
foreach (System.Web.UI.WebControls.WebParts.WebPart wp in manager.WebParts)
{
Console.WriteLine(wp.GetType().ToString());
}
}
}
}

Console.ReadLine();
}
}
}

Añadir link abra el panel de control en una webpart

Si nuestra webpart necesita tener configuradas ciertas propiedades podemos mostrar un link que abra el panel de control (toolpane) hasta que estas propiedades no estén configuradas. Para ello tenemos que distinguir entre dos casos: webparts que derivan de Microsoft.SharePoint.WebParts.Webpart o webparts que derivan de System.Web.UI.WebControls.WebParts.Webpart.

En el primer caso tenemos una función, ToolPane.GetShowExtensibleToolPaneEvent, que pasándole el id de nuestra webpart nos devuelve un string con la llamada javascritpt que queremos realizar, por lo que el código quedaría algo como:


public class WPPrueba : Microsoft.SharePoint.WebPartPages.WebPart
{
#region Variables
private string _propiedad;
#endregion
#region Propieades
[Browsable(true),
Category("Mis Propiedades"),
DefaultValue(""),
WebPartStorage(Storage.Shared),
FriendlyName("Propiedad"),
Description("Propiedad.")]
public string Propiedad
{
get { return _propiedad; }
set { _propiedad = value; }
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
try
{
if (Propiedad == null Propiedad == "")
{
string funcionJavaScript = ToolPane.GetShowExtensibleToolPaneEvent(this.ID);
string html = "abra el panel de herramientas";
writer.Write(html);
}
else
{
writer.Write("hello world");
}
}
catch(Exception ex)
{
}
}
#endregion
}



En el segundo caso no podemos hacer esto por lo que añadiremos el código javascript a mano:



string html = " abra el panel de herramientas";

sábado, 31 de enero de 2009

MCPD Web Developer

El próximo viernes rendiré el examen que me puede acreditar como MCPD (Microsoft Certified Professional Developer): Web Developer. Este exámen es el 70-547: PRO: Designing and Developing Web Applications by Using the Microsoft .NET Framework.

Para preparar esta certificación he estado leyendo el libro: MCPD Self-Paced Training Trainink Kit (Exam 70-547), este libro contiene los siguientes capítulos:

Chapter 1: Application Requirements and Design
Chapter 2: Decompose Specifications for Developers
Chapter 3: Design Evaluation
Chapter 4: Creating a User Interface
Chapter 5: Creating and Choosing Controls
Chapter 6: Data Validation
Chapter 7: Delivering Multimedia
Chapter 8: Component Design
Chapter 9: Component Development
Chapter 10: Reusable Software Components
Chapter 11: Application Logic Layer
Chapter 12: Logging and Monitoring
Chapter 13: Application Configuration
Chapter 14: Define and Evaluate a Testing Strategy
Chapter 15: Creating Development Tests
Chapter 16: Deploying an Application
Chapter 17: Supporting an Application

Para más información:

http://www.microsoft.com/learning/mcp/mcpd/webdev/default.mspx

viernes, 30 de enero de 2009

Logger to SharePoint Logs

Hola actualmente hemos creada una pequeña utilidad que nos permite serializar los logs utilizando el mismo sistema que utilidad Windows SharePoint Services. Lo que hemos hecho es crear una clase TraceProvider igual que nos indican en las MSDN:

http://msdn.microsoft.com/en-us/library/aa979522.aspx

Luego hemos creado una classe estática logger con un conjunto de métodos estáticos que nos facilitan a la hora de guardar los logs.


public static class Logger
{
public static void LogUnassigned(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Unassigned, ProductName, Title, Message);
}
public static void LogCriticalEvent(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.CriticalEvent, ProductName, Title, Message);
}
public static void LogWarningEvent(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.WarningEvent, ProductName, Title, Message);
}
public static void LogInformationEvent(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.InformationEvent, ProductName, Title, Message);
}
public static void LogException(Exception e)
{
Log(TraceProvider.TraceSeverity.Exception, e.Source, e.TargetSite.ToString(), e.ToString());
}
public static void LogAssert(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Assert, ProductName, Title, Message);
}
public static void LogUnexpected(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Unexpected, ProductName, Title, Message);
}
public static void LogMonitorable(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Monitorable, ProductName, Title, Message);
}
public static void LogHigh(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.High, ProductName, Title, Message);
}

public static void LogMedium(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Medium, ProductName, Title, Message);
}
public static void LogVerbose(string ProductName, string Title, string Message)
{
Log(TraceProvider.TraceSeverity.Verbose, ProductName, Title, Message);
}
private static void Log(TraceProvider.TraceSeverity type, string ProductName, string Title, string Message)
{
TraceProvider.RegisterTraceProvider();
TraceProvider.WriteTrace(0, type, Guid.NewGuid(), ProductName, ProductName, Title, Message);
TraceProvider.UnregisterTraceProvider();
}
}

jueves, 29 de enero de 2009

Navegación fuera de contexto en sharepoint (moss 2007)

Una forma de recorrer una jerarquia de sites de publicación en MOSS 2007 desde fuera del contexto web (es decir desde una aplicación de consola, aplicación windows, etc. ) seria utilizando las propiedades CurrentNavigationNodes y GlobalNavigationNodes de la clase PublishingWeb.

De esta forma podemos obtener la navegación de los usuarios respetando cosas como pueden ser el orden que se ha establecido o si queremos mostrar subsites, páginas, etc.

Aquí añado un poco de código donde se muestran un uso de estas propiedades:

NOTA: Es necesario referencial la dll Microsoft.SharePoint.Publishing.dll


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Navigation;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
try
{
using (SPSite siteCollection = new SPSite("http://localhost"))
{
using (SPWeb site = siteCollection.OpenWeb())
{
PublishingWeb pw = PublishingWeb.GetPublishingWeb(site);
Console.WriteLine("Navegación actual de la site Nombre: {0}, Url: {1} ",site.Title,site.Url);
foreach (SPNavigationNode nodo in pw.CurrentNavigationNodes)
{
Console.WriteLine("Titulo: {0}, Url: {1}", nodo.Title, nodo.Url);
}
Console.WriteLine("Navegación global de la site Nombre: {0}, Url: {1} ", site.Title, site.Url);
foreach (SPNavigationNode nodo in pw.GlobalNavigationNodes)
{
Console.WriteLine("Titulo: {0}, Url: {1}", nodo.Title, nodo.Url);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}

miércoles, 28 de enero de 2009

Acceder al QueryString con JavaScript

Este post lo hago simplemente para recordarlo, que siempre se me olvida y me vuelvo loco buscando un ejemplo:

//Devuelve un array con la coleccion de parametros del queryString
function queryString() {

var qsParm = new Array();
var query = window.location.search.substring(1);
var parms = query.split('&');

for (var i=0; i<parms.length; i++) {

var pos = parms[i].indexOf('=');

if (pos > 0) {

var key = parms[i].substring(0,pos);
var val = parms[i].substring(pos+1);
qsParm[key] = val;

}
}

return qsParm;
}

ConnectionString en SharePoint (MOSS 2007)

La cadena de conexión para la base de datos de configuración es persistida en una clave de registro, consultando la clave específica podemos obtener la cadena de conexión.

1. Inicio --> Ejecutar: regedit
2. Navega por: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\configdb

Trozo de código para obtenerla:

RegistryKey key = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\configdb");

DSN = (string) key.GetValue("DSN");

SQLServerInstance = new string[]{this.DSN.Split(new char[] { '=', ';' })[1]};

key.Close();

Deshabilitar Mi Sitio y Mis vínculos en SharePoint (MOSS 2007)

Para poder configurar quien puede ver Mi Sitio y Mis Vínculos en Microsoft Office SharePoint Server 2007 tendremos que seguir los siguientes pasos:

1. Ir a la Administración Central
2. In a la Administración de los Servicios Compartidos
3. Permisos de servicios de personalización
4. Agregarmos el grupo al que queremos dar permisos para crear sitios personales y marcamos los checkbox:
- Crear un sitio personal
- Utilizar características personales
5. Eliminamos el grupo NTAuthority\Authenticated Users que es el que viene por defecto.

De esta forma ya habremos limitado esta características a un grupo de directorio activo particular.

Si lo que queremos es simpelemente que ningún usuario vea estos links lo que podemos hacer es desactivar la feature que nos muestra estos links, en la master.page tenemos un sharepoint delegate control que se complementa con un user control a partir de una feature que está activada en el ámbito de toda la granja de servidores. Para deshabilitar esta feature:

C:\Program Files\Microsoft Shared\web server extensions\12\bin\>stsadm -o deactivatefeature -name MySite

Información extraida de:
http://geekswithblogs.net/RogueCoder/archive/2006/11/01/95766.aspx