miércoles, 12 de septiembre de 2012

Error Access Denied al modificar el web.config programaticamente o al intentar hacer acciones Administrativas

Problema:
En Sharepoint 2010 al tratar de modificar programaticamente el web.config desde un evento de feature o script de acciones Administrativas, usando el SPWebConfigModification sale un error muy descriptivo "Access Denied". Que majo! Que descriptivo!
Probamos con privilegios elevados.... y "Access Denied"


Solución:
Estas accediendo usando LOCALHOST o por la IP? Lo lógico es entrar usando el nombre del servidor o la ruta de acceso alternativa que este registrada.
No obstante si quieres que funcione ..... en Sharepoint 2010 se ha incorporado un interruptor de seguridad a nivel de Servicio para bloquear modificaciones remotas, tendremos que desactivarla....

# Con este script de PowerSell desactivamos la caracteristica de seguridad RemoteAdministratorAccessDenied
# Autor:Roberto Marcos

#Añadimos el snapin de SharePoint PowerShell 
 if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
$servicio = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$servicio.RemoteAdministratorAccessDenied = $false
$servicio.Update()


lunes, 10 de septiembre de 2012

Obtener el Strong Name del ensamblado desde Visual Studio

Este truco de Powershell facilita la extracción de Nombre del ensamblado (StrongName) desde Visual Studio.
Funciona en Sharepoint 2010 y Sharepoint 2007

  1. En Visual Studio, ir hasta Tools –>  External Tools.
  2. Hacemos Click en "Add" para agregar una herramienta nueva, y rellenar los siguiente valores:
    1. Title: S&trong Name
    2. Command: Powershell.exe
    3. Arguments: -command "[System.Reflection.AssemblyName]::GetAssemblyName(\"$(TargetPath)\").FullName"
    4. Marcar "Use Output Window"
    5. Desmarcar cualquier otra opción

3. Ya esta, ahora en el proyecto , vamos hasta Tools –> Strong Name, y en la ventana de Salida obtendremos el Nombre de ensamblado

Ejemplo:ProyectoEjemplo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=64a818baff89cafc

lunes, 25 de junio de 2012

Background Folder Copy for Sharepoint 2010

He decidido subir a codeplex este proyecto que hice hace muchísimo tiempo, creo que aun puede ser de utilidad a mucha gente, lastima no haberlo hecho antes.

http://foldercopysp2010.codeplex.com

Se trata de un pequeño proyecto de un servicio windows que revisa constantemente una carpeta en busca de ficheros nuevos o modificados, para subirlos a una librería documental en Sharepoint 2010.

Es algo parecido a una sincronización ... pero unidireccional (del Pc a Sharepoint 2010)

He usado el Client Runtime de Sharepoint 2010 para una correcta implementación.


Copia de Forma automática los ficheros depositados en una carpeta windows a una Librería de documentos en Sharepoint 2010.

Se han combinado un servicio windows FileSystemWatcher (se incluye instalador) con las rutinas necesarias para copiar ficheros (binarios) a una Libreria de documentos en sharepoint 2010.

Lo que conseguimos es subir y actualizar ficheros de una manera mas comoda.

Los usos que yo le he dado:

  • Subida de ficheros escaneados de forma automática.
  • Interface con un Ftp al usar la misma carpeta.

En la carpeta donde se instala la dll (con el proyecto de instalación) hay un fichero app.config

<?xml version="1.0"?><configuration><startup><supportedRuntime version="v2.0.50727"/></startup><appSettings><add key="RutaCopiado" value="C:\FolderToSharepointCopier\RutaCopiado"/><add key="RutaSharepoint" value="http://servidor/LibreriaDocumentos/"/> </appSettings></configuration
 
En este fichero se definen las carpetas de origen y destino (por defecto sobreescribe)
Al instalar te pide un usuario de dominio (que tiene que tener permiso en Moss2010) con el que se copian los ficheros.



Espero que os resulte educativo y útil.
Roberto Marcos.

martes, 19 de junio de 2012

Actualizar el InfoPath de edición de ListItems en Sharepoint 2010

Cuando customizamos los formularios de alta y modificacion de un listItem una de las opciones mas cómodas es hacerlo usando Infopath 2010.

Todo es muy bonito al desarrollarlo y ponerlo en un entorno de pruebas, cuando lo ponemos en producción es fácil, hacemos un paquete STP de la lista customizada, desplegamos y listo, tenemos una lista Vacia preparada para comenzar a recoger elementos a traves del Infopath.

Los problemas aparecen cuando tras ponerlo en producción ... pasa el tiempo y claro ahora la lista esta repleta de datos, entonces es cuando las modificaciones se convierten en un "dolor" ya que no podemos borrarla y desplegar de nuevo el STP actualizado.

A simple vista y desde Infopath no se puede modificar la URL de la lista a la que esta modificando los formularios de  View y Edit
Un usuario avispado puede rebuscar un poco en el apartado conexiones de datos, pero sin éxito ya que todo apunta a que por aquí tendría que estar la cosa, pero va a ser que no.

Problema:
Sustituir/Actualizar el formulario de edición de List Items de una lista customizada con Infopath 2010.

Solución:
1º Si hemos creado campos nuevos en Desarrollo los creamos también en producción.
2º Abrimos 2 Sharepoint designer uno contra Desarrollo y otra contra Producción. 
3º Mediante Sharepoint Designer 2010 podemos acceder a la lista en cuestión en ambos entornos.
4º Abrimos la carpeta "Item" en la que podemos ver un fichero llamado template.xsn
5º Con hacer un COPY-PASTE de este fichero desde Desarrollo a Produccion y ya esta todo listo.


Solución Pro:
Creamos un modulo de aprovisionamiento que despliegue (y reemplace) el fichero en cuestión con nuestro paquete WSP
Recuerda marcarlo con GhostableInLibrary para evitar problemas.
image
Si hay que crear campos nuevos... en el feature activate es donde tendríamos que hacerlo.


Nota:
Los infopath tienen una importante limitación cuando los usamos para customizar listas...no pueden usar grupos (o parent child con otras listas) a grandes rasgos solo pueden crear/editar elementos en una sola lista a la vez.

Mis Agradecimientos a Lynton Titus que me ilustró en este tema.
Esperamos que os sea de ayuda.


jueves, 1 de marzo de 2012

Publicar de forma externa Infopath 2010 Forms Services con un Proxy Inverso (TMG)

Problema:
Publicar en internet correctamente los Form Services de Infopath 2010 a través de https y el TMG usando la funcionalidad de proxy inverso.

Análisis:
Tras analizar las peticiones que hace un ordenador externo a la red, hemos podido observar que el Explorador hace llamadas a la URL interna a pesar que hemos dado de alta la URL externa como Ruta alternativa de Sharepoint.

Solución:
Tirando del hilo hemos encontrado documentación precisa de cómo hay que hacerlo correctamente ya que el producto tiene algunas salvedades como que Infopath hace envió (postback) SIEMPRE a la dirección interna principal (http://direccioninterna.miservidor.com) de forma que no existe manera de que infopath atienda por 2 urls, por lo que la única vía es usar la funcionalidad  de "link translation" del TMG.

Reeplazos de URL que hay que añadir en la parte de link translation (sin las comillas):

"direccioninterna.miservidor.com" >> "direccionexterna.midominioexterno.com"
"http:" >> "https:"

Nota: No incluir el http en las urls, y el reemplado http tiene los dos puntos ":" de forma intencionada para que no ocurra reemplazos tipo https>>httpss que serian erróneos .

No olvidarse de incluir la ruta de acceso alternativa configurada como "Internet" en la administración central.

La documentación paso a paso de cómo hacer esta configuración en el TMG la tenemos en este link http://technet.microsoft.com/en-us/library/cc984424.aspx


lunes, 20 de febrero de 2012

Ocultar el mensaje en PeopleSearchResultWebpart : No hay resultados disponibles...

Problema:
Si no se especifica ninguna consulta el People Search Core Result Webpart muestra el mensaje...

En Inglés 
"No results are available. Either no query is specified, or the query came from advanced search (Federated Webparts do not support Advanced Search queries)."

En Español
"No hay resultados disponibles. O bien no se especificó ninguna consulta o la consulta provino de una búsqueda avanzada (los elementos web federados no son compatibles con las consultas de búsqueda avanzada)."

Pensé que podría ocultarlo cambiando el literal de $NoPeopleResults por XSLT pero por desgracia no se trata de este literal, es el literal de error por no especificar consulta y no podemos cambiarlo facilmente.

"Solución":
Crear un editor de contenido en la pagina, entrar en el editor html y pegar este Script.
No olvides configurar el webpart para que no muestre el titulo.
No es que sea una maravilla de solución pero... funciona.
He usado el selector con el modificador de inicio del literal (^) ya que el ID del elemento DIV tiene un GUID cambia en cada webpart.

<script type="text/javascript">
$(document).ready(function() { $('div[id^="SRW_Error"]').hide(); })
</script>


Nota: Requiere tener una referencia al fichero javascript de Jquery 

martes, 31 de enero de 2012

Extraer información de Perfiles de usuario de Sharepoint 2010

Podemos extraer toda la información que deseemos de la base de datos de perfiles de sharepoint.

Como nuestros amigos de Microsoft lo llaman "sincronización de perfiles" cuando realmente es un volcado de datos, a veces se queda mucha basura y perfiles con sitios creados, que son difíciles de localizar para eliminarlos.


Aunque también podemos crear las que necesitemos y obtenerlas como muestro en el script de ejemplo.

Con este script powershell podemos hacer la tarea de buscar basura y poner orden un poco mas facil.

TRUCO1: Podemos hacer un backup alternativo en excel de las propiedades de los perfiles, con este sistema (descomentar la parte final)
TRUCO2: Con otro Powershell podriamos cargar la información desde un Excel, en lugar de usar el pesado proceso de sicronización.

# List All User Profiles - SharePoint 2010- PowerShell Script (info@robertomarcos.com) 31-1-2012
#Add SharePoint PowerShell SnapIn if not already added
 if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

function GetAllUserProfiles(){

#Get ServiceContext from associated site
$site = new-object Microsoft.SharePoint.SPSite("http://servidor_de_ejemplo");
$ServiceContext = [Microsoft.SharePoint.SPServiceContext]::GetContext($site);  

#Get UserProfileManager from the My Site Host Site context
$ProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
$AllProfiles = $ProfileManager.GetEnumerator()  

#foreach($profile in $AllProfiles|Where-Object { $_.[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::AccountName].Value  -match "el filtro que quieras"})
foreach($profile in $AllProfiles)
{
    $DisplayName = $profile.DisplayName
    $AccountName = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::AccountName].Value  
    $Firstname = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::FirstName].Value  
    $Lastname = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::LastName].Value  
    $JobTitle=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::JobTitle].Value  
    $Title=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Title].Value  
    $PictureUrl=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PictureUrl].Value 
    $Office=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Office].Value 
    $WorkPhone=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::WorkPhone].Value 
    $WorkEmail=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::WorkEmail].Value 
    $Departamento=$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Department].Value;
    $MiCampoPersonalizado=$profile["MiCampoPersonalizado"].Value 
     
    
    #$User = $ProfileManager.GetUserProfile();
$data = @{
        "DisplayName"=$DisplayName;
"AccountName"=$AccountName;     
        "FirstName"=$Firstname;
        "Lastname"=$Lastname;
        "JobTitle"=$JobTitle;
        "Puesto"=$Title;
        "PictureUrl"=$PictureUrl;
        "Office"=$Office;
        "WorkPhone"=$WorkPhone;
        "WorkEmail"=$WorkEmail;
"Departamento"=$Departamento;
"MiCampoPersonalizado"=$MiCampoPersonalizado;
        
}
New-Object PSObject -Property $data   
write-Host  "Listando usuario..  $AccountName"
}
write-host "Finalizado."
$site.Dispose()
 
 }

#$csvFilePath = "C:\PerfilesDeUsuario.csv"
#GetAllUserProfiles| Export-Csv -NoTypeInformation -Path $csvFilePath

GetAllUserProfiles | Out-GridView