Dec 16, 2013

SharePoint 2010 Search show default display form instead of custom display form

I had a problem where SharePoint Foundation 2010 Search was returning the DispForm.aspx in the search results instead of the CustomForm.aspx that was configured as the default display form.

Solutions (I like option 3 best):
  1. Configure the search engine, but with Foundation Search, there's hardly anything to configure.  
  2. Eliminate or hide the DispForm.aspx, but I learned in SharePoint 2007 that doing anything drastic to the default DispForm.aspx can break a list in strange, un-repairable ways.
  3. Use the following JavaScript redirect in a content editor webpart in the DispForm.aspx:
    <script type="text/javascript">
    
    function redirectForm() {
    var pagemode = GetUrlKeyValue('DisplayMode', false);
    
    if(pagemode == null || pagemode == "")
     {
             var hrefCurrent = window.location.href;
             var hrefNew = hrefCurrent.replace("DispForm.aspx","CustomForm.aspx");    
        window.location.href = hrefNew;
     }
    }
    ExecuteOrDelayUntilScriptLoaded(redirectForm, "sp.js");</script>
    

Nov 27, 2013

InfoPath 2010 Form WebPart doesn't redirect after close

I ran into the problem where an InfoPath form displayed in an InfoPath 2010 Form WebPart wouldn't redirect to the Source URL parameter, instead simply showed an ugly "Form closed" dialog.

I found the solution here: http://shareapointkiran.blogspot.com.es/2011/12/infopath-form-redirect-to-any-page-once.html

1. Add a content editor with the following Javascript to your page with the Form WebPart.

I've refined the JavaScript a bit from the original solution:

<script type="text/javascript">
function ClosedFormRedirect() {
  var dialogMessageElement = document.getElementById("DialogFinalMessage");
 if(dialogMessageElement != null && dialogMessageElement.children.length > 0)
 {
    var sourceURL = GetUrlKeyValue('source', false);
    window.location.href = sourceURL;
 }
}

_spBodyOnLoadFunctionNames.push("ClosedFormRedirect");

</script>

Oct 17, 2013

Create groups with JavaScript in SharePoint 2010

All credit goes to Yesh Agrawal for this: http://yeshagrawal.blogspot.com.es/2013/05/create-sp-group-and-assign-permission.html

Here's the code (edited to only create the group, not assign permissions):
<script >
function createSPGroups()
{
var grupos = new Array("group1",
"group2");

//Load new Site
   var currentCTX = new SP.ClientContext();
   var currentWEB  = currentCTX.get_web();

   //Get all groups in site
   var groupCollection = currentWEB.get_siteGroups();

  for(var i = 0; i < grupos.length; i++) { 
   var grupo_nombre = grupos[i];
   var grupo_desc = "Description for "+grupos[i]; 
   // Create Group information for Group
var membersGRP = new SP.GroupCreationInformation();
membersGRP.set_title(grupo_nombre);
membersGRP.set_description(grupo_desc);


//add group
oMembersGRP = currentWEB.get_siteGroups().add(membersGRP);



currentCTX.load(oMembersGRP);
}

//Execute Query
currentCTX.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded() {
            alert("Done");
}

function onQueryFailed(sender, args) {
            alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
</script>
  
<button onclick="createSPGroups(); return false;">Crear grupo</button>

Passing URL parameter to InfoPath and InfoPath rules timing problem

I had this issue trying to pass a URL parameter to a lookup column in InfoPath. Lookup columns are not available as connection targets from the Query webpart so I had to do some roundabout engineering (quite common with InfoPath).

  1. Create a visible field on the form for your incoming parameter (textbox, calculated value, etc). 
  2. Create an action rule that fires when the value of this field changes to do whatever you need to get done (set dropdown or other field). 
  3. Create a formatting rule that always hides this field.

Jul 5, 2013

Best way to hide columns in SharePoint 2010 for Display form, Edit form or New form

I always thought that if you need to hide a column in SharePoint 2010, you only had several options, each of which has limitations:
  1.   Set the column to Hidden in the content type - Hides the column in all forms
  2.  Customize each form page with JavaScript to hide the columns of interest - Very tedious and error prone
  3. Use InfoPath to customize the forms - Lots of compatibility problems and not available in SharePoint Foundation
But the other day it dawned on me that a great fourth solution exists: change the FIELD SCHEMA!

I saw the field schema had more granular properties of ShowInDisplayForm, ShowInEditForm, ShowInNewForm, etc.  You can edit these via code, SharePoint Manager or PowerShell.

I even implemented a page that can change these properties entirely via the Client Object Model for those using SharePoint Online (Office 365) or who simply don't have root access to the server.

Here's the HTML:



<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js">
</script>
<script type="text/javascript">


ExecuteOrDelayUntilScriptLoaded(buildListMenu, "sp.js");

function buildListMenu() {
  this.clientContext = new SP.ClientContext.get_current();
  var oListCollection = this.clientContext.get_web().get_lists();
  
  this.listCollection = clientContext.loadQuery(oListCollection);
   clientContext.executeQueryAsync(Function.createDelegate(this, this.onSiteQuerySucceeded), Function.createDelegate(this, this.onSiteQueryFailed));
  this.listCollectionNotifyId = SP.UI.Notify.addNotification("Cargando listas",true);
}

function onSiteQuerySucceeded() {
   SP.UI.Notify.removeNotification(this.listCollectionNotifyId);
 for(var i = 0; i < this.listCollection.length; i++) {
    var list = listCollection[i];
    jQuery('#listMenu').append("<option value='"+list.get_id().toString()+"'>"+list.get_title()+"</option>");
      
 }
 
 changeList(document.getElementById("listMenu"));
 
}

function onSiteQueryFailed() {
SP.UI.Notify.removeNotification(this.listCollectionNotifyId);
 alert("Error cargando listas");
}

function changeList(selectMenu) {
   var listId = selectMenu.value;
   retrieveListColumns(listId);
  
}

function retrieveListColumns(listId) {
    this.clientContext = new SP.ClientContext.get_current();
    var oWebsite = clientContext.get_web();
    var oList = oWebsite.get_lists().getById(listId).get_fields();

    this.fieldCollection = clientContext.loadQuery(oList);

    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded() {

    var listInfo = '';
    this.fieldArray = new Array();
    this.attributeArray = ["ShowInDisplayForm",
          "ShowInNewForm",
          "ShowInEditForm",
          "ShowInListSettings",
          "ShowInVersionHistory",
          "ShowInViewForms"]; 
                    
    

 clearColumnsTable();
    
    // for every field
    for (var i = 0; i < this.fieldCollection.length; i++) {
        var oField = this.fieldCollection[i];
        
        // only interested in visible fields
        if(!oField.get_hidden()) {
        this.fieldArray[oField.get_id().toString()] = new Array();
        var currentAttributeArray = new Array();
        
          this.fieldArray[oField.get_id().toString()]["SPObject"] = oField;
           
           var xmlDoc = parseSchemaXml(oField.get_schemaXml());
           this.fieldArray[oField.get_id().toString()]["xmldoc"] = xmlDoc;
           
           // create row for checkboxes 
           var newRow = jQuery('<tr/>');
           newRow.append("<td>"+oField.get_title()+" ("+oField.get_internalName()+")</td>");
            
           // create checkboxes
           for(var j = 0; j < this.attributeArray.length; j++) {
              var attribute = this.attributeArray[j];
              currentAttributeArray[attribute] =  xmlDoc.documentElement.getAttribute(attribute) == null ? "TRUE": xmlDoc.documentElement.getAttribute(attribute);
          
           var newCell = jQuery("<td/>" , { 'class': 'ms-vb2' });
           var newCheckbox =  jQuery("<input "+convertirTextToChecked(currentAttributeArray[attribute])+" type='checkbox' id='"+oField.get_id().toString()+"' schemaattribute='"+attribute+"'/>").click(function(){
          checkboxChange(this.id,this.getAttribute('schemaattribute'),this.checked);
          });
          newCell.append(newCheckbox);
    newRow.append(newCell);
   }

             
    newRow.appendTo("#columnsTableBody");
        
        }
    }      
}

function onQueryFailed(sender, args) {
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
function onUpdateSucceeded() {

     SP.UI.Notify.removeNotification(this.fieldUpdateNotifyId);
}

function clearColumnsTable() {

  jQuery("#columnsTableBody").empty();

}


function checkboxChange(fieldId,schemaattribute,value) {
 
 var xmlDoc = this.fieldArray[fieldId]["xmldoc"];
 xmlDoc.documentElement.setAttribute(schemaattribute,value.toString().toUpperCase());
  xmlDoc.documentElement.removeAttribute("Version");
 this.fieldArray[fieldId]["xmldoc"] = xmlDoc;
 
 var oField = this.fieldArray[fieldId]["SPObject"];
 
 if(xmlDoc.documentElement.outerHTML) {
    oField.set_schemaXml(xmlDoc.documentElement.outerHTML);  // Most browsers
 } else {
     oField.set_schemaXml(xmlDoc.documentElement.xml);  // IE
 }
 
    oField.update();
   this.fieldArray[fieldId]["SPObject"] = oField;
      
 clientContext.executeQueryAsync(Function.createDelegate(this, this.onUpdateSucceeded), Function.createDelegate(this, this.onUpdateFailed));
    this.fieldUpdateNotifyId = SP.UI.Notify.addNotification("Actualizando columna",true);
            
}
function convertirTextToChecked(text) {
  if(text == "TRUE" || text == "true") {
   return "checked='checked'";
  }
  else {
   return "";
  }
}
function onUpdateFailed(sender, args) {
 SP.UI.Notify.removeNotification(this.fieldUpdateNotifyId);
    alert('Update failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

function parseSchemaXml(txt) {
 var xmlDoc = null;
if (window.DOMParser)
  {
  parser=new DOMParser();
  xmlDoc=parser.parseFromString(txt,"text/xml");
  }
else // Internet Explorer
  {
  xmlDoc=new ActiveXObject("msxml2.DOMDocument.6.0");
  xmlDoc.async=false;
  xmlDoc.loadXML(txt);
  } 
  return xmlDoc;
}
</script>
<div>
Lista:<select id="listMenu" onchange="changeList(this)"></select>
</div>
<table id="columnsTable">
<thead>
<tr><td colSpan='6'>Visible en</td></tr>
<tr class="ms-vh2"><td>Columna (nombreInterna)</td>
<td>Display Form</td>
<td>New Form</td>
<td>Edit Form</td>
<td>List Settings</td>
<td>Version History</td>
<td>View Forms</td>
<tbody id="columnsTableBody">
</tbody>
</table>


Jun 27, 2013

Customize the Document Information Panel for Word in SharePoint 2010

Sometimes you need the fields in the Document Information Panel in Word to be read-only or even hidden.

With InfoPath 2010, you can edit the Document Information Panel for a particular content type in a SharePoint Document Library.

Here are instructions:  http://msdn.microsoft.com/en-us/library/ms563688.aspx

As a side note, you can't make Quick Parts read-only, but you can make a section read-only in Word: http://nerdsrealm.blogspot.com.es/2013/03/how-to-restrict-editing-in-word-2010.html

Jun 11, 2013

Publish SharePoint 2010 workflows from Powershell

I came across this problem when we moved a site collection from one domain to another and all the workflows needed to be republished.

The Powershell script is pretty straightforward, except for one gotcha:  when setting the URI for a webservice proxy in Powershell, the webservice URL is always reset to the root webservice.

 Eg. http://server/subsite/_vti_bin/webpartpages.asmx  becomes http://server/_vti_bin/webpartpages.asmx.  You can later change the proxy object url to the correct URL.

 This showed up in the SharePoint logs as "SOAP exception: System.IO.FileNotFoundException: The system cannot find the specified file." and the webservice would only return a generic SOAPException. 

Here's the Powershell script:

$site = Get-SPSite http://server
$username = "domain\admin"
$password = "apassword"
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
$URI = "http://server/_vti_bin/webpartpages.asmx"
$proxy = New-WebServiceProxy -Uri $URI -Credential $cred


foreach($web in $site.AllWebs) {
    Try {
        $subFolders = $web.Folders["Workflows"].SubFolders;
       
        foreach($folder in $subFolders) {
            if($folder.Name -ne "Forms") {
                $xomlConfigFile = $folder.Files[$folder.Name+".xoml.wfconfig.xml"]
                Write-Host $xomlConfigFile.Name $xomlConfigFile.UIVersionLabel
                # The web service proxy sets the URL to root, so we reset it to the subsite
                $proxy.Url = $web.Url+"/_vti_bin/webpartpages.asmx"
                $result = $proxy.AssociateWorkflowMarkup($xomlConfigFile.Url,"V"+$xomlConfigFile.UIVersionLabel)
                Write-Host $result
            }
        }
    } Catch {
   
    }

        $web.Dispose()

}

$site.Dispose()

Jun 3, 2013

Variable Excel cell references

I just learned a new Excel function today: the INDIRECT function.

It allows you to put together a cell reference from text strings.

Examples

  =INDIRECT("sheet1!"&variableColumn&"2)  If there is a cell named 'variableColumn' with the text 'A', then this will return the value in "sheet1!A2"

=INDIRECT(A3&"2)  If A3 has the text 'B', then this will return the value in B2




May 29, 2013

SQL Database in use: How to find and close existing database connections

This is useful in many situations (SharePoint restores, etc):

In SQL Management Studio:
1. exec sp_who2; 
  This returns a list of all current sessions.  You will normally be interested in the sessions with SPID 50 or higher.
2. kill blockingSPID
  Try to close the connection from the client side, but if that doesn't work, you can kill the session from SQL Server.


Many client applications will quickly try to restore a connection, so to block access:

1. Go to the Properties > Options of the database in question.
2. In the State section, change the Restricted Access from MULTI_USER to RESTRICTED_USER to allow only database owners to connect. 
3. Take the database offline via Tasks > Take offline


May 17, 2013

Identity XSL for SharePoint XSLT Webpart

When you get started with XSL transforms with the SharePoint XSLT Webpart, a visual identity XSL transform is often helpful for getting started.

Here's one I made:

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
  xmlns:ddwrt2="urn:frontpage:internal">
<xsl:output method="html" />
<xsl:template match='dsQueryResponse'>
   <fieldset>
   <legend><xsl:text>dsQueryResponse</xsl:text></legend>
   <dl title="dsQueryResponse">
           <xsl:for-each select="@*">
    <dt><xsl:value-of select="name()"/></dt>
      <dd><xsl:value-of select="."/></dd>
  </xsl:for-each>
  </dl>
 


<fieldset>
   <legend><xsl:text>Rows</xsl:text></legend>

    

     <xsl:apply-templates select='Rows/Row'/>
     </fieldset>
    
     </fieldset>

  
  </xsl:template>

 
  <xsl:template match='Row'>
<fieldset>
   <legend><xsl:text>Row</xsl:text></legend>

  <dl>
             <xsl:for-each select="@*">
   <dt><xsl:value-of select="name()"/></dt>   
   <dd><xsl:value-of select="."/></dd>
  </xsl:for-each>
  </dl>
  </fieldset>
    
  </xsl:template>
 

</xsl:stylesheet>

May 16, 2013

"The workbook can not be opened" error in SharePoint 2010 with Excel

I got this error "The workbook can not be opened" in Excel Services or with the Office Excel Web App with SharePoint 2010. 

It occurs when you have a separate service user account for your service applications (which you should do to follow the best practices for permissions in SharePoint).

The fix is simple:
  1. Open Powershell for SharePoint 2010
  2. Run "$webapp = Get-SPWebApplication http://webapplicationurl"
  3. Then "$webapp.GrantAccessToProcessIdentity("domain\serviceusername")"

That's all.

Thanks to this blog for the solution:
http://wellytonian.com/2011/10/excel-workbook-cannot-be-opened-error-%E2%80%93-sharepoint-2010/

Apr 26, 2013

SharePoint 2010 Search and Search Server Express: slow first search

We had a problem where the initial search with Search Server Express was extremely slow, but subsequent searches were much faster.

The problem was that SharePoint would try to retrieve a root certificate from the internet for the first search from a user.

Here's the fix: http://support.microsoft.com/kb/2639348

Apr 17, 2013

Passing parameters to InfoPath forms in SharePoint 2010

Until SharePoint 2010,  I used two methods to pass parameters to forms in SharePoint:

1. Using Javascript/JQuery to read a URL Query parameter and assign it to the HTML control.  This was error prone due to the difficulty of finding the right control via the "title" attribute and then assigning the value to all the different types of control, especially lookup fields with more than 20 elements.

2.  Using Managed code in InfoPath to read a URL Query parameter and assign it to a node in the InfoPath data tree.  This required publishing the InfoPath form via Central Administration and was rather inflexible.

Thankfully, the InfoPath/SharePoint teams have given us a third option in SharePoint 2010:
  Connecting the Query parameter filter webpart with the InfoPath form webpart. 

This video by Igor Jericevich gives the details: http://youtu.be/tskm5mQ3W3g

Apr 12, 2013

Restoring a SharePoint 2010 content database to a different domain

Much has been said about how to backup and restore a content database to another domain, but I'd like to post my version here to avoid a few gotchas I've come across.


  1. Take original content database offline in Central Administration > Application Management > Manage Content Databases
  2. Connect to SQL Server and make a backup of the original content database.
  3. Put the original content database back online via Central Administration.
  4. Move the database backup file to the new SQL Server.
  5. Restore the database backup file to the new SQL Server via SQL Management Studio.  You can change the database name at this time if you'd like.
  6. In SQL Management Studio > Security > Logins, go to the Properties of the new farm account login (domain\sp_farm or similar) and in the User Assignment panel, give the farm account the "db_owner" role for the new database.
  7. Connect to the Central Administration for the new server farm and create a new web application.
  8. You may create a new site collection and content database if you'd like to check that the new web application is working before restoring the original content database.
  9. If the new web application has any content databases that have site collections in the same URL path as the original content database (usually /), you must go to Central Administration > Application Management > Manage Content Databases in the new farm and remove that database from the web application (not just take offline, but remove).  It's important to remove the old content databases before adding the new content database in order for SharePoint to properly detect the site collection entry points.
  10. Add the restored content database to the new web application.  DO NOT connect to the new web application yet via web or you'll risk a conflict in the user migration steps.
  11. Open the SharePoint PowerShell Administration Console as administrator.
  12. Execute "Get-SPUser -Web http://newWebapplicationUrl" to get a list of all logins for the new web application
  13.  For each login shown that has the "olddomain", execute "Move-SPUser -Identity "olddomain\olduser" -NewAlias "newdomain\newuser". 
  14. Connect to the new web application to validate the restored content.
  15. If a new content database was created with the new web application, you may delete it now via SQL Management Studio.  Be careful not to delete your restored content database.

Apr 9, 2013

SharePoint 2007 and 2010 problems in Internet Explorer 10

Due to a bug in the browser detection files of .NET 2.0 and 3.5, SharePoint doesn't detect IE 10 properly and doesn't activate the necessary scripts and ActiveX.

This Microsoft hotfix (KB 2600100) updates the browser detection files and solves the problem.

These two blogs have more detailed information on the problem and its solution:
http://www.hanselman.com/blog/BugAndFixASPNETFailsToDetectIE10CausingDoPostBackIsUndefinedJavaScriptErrorOrMaintainFF5ScrollbarPosition.aspx

http://gigajim.com/blog/2012/12/11/sharepoint-2010-and-ie10-incompatibility

Feb 13, 2013

Office Web Apps problems with 100% CPU

I had a problem trying to use Office Web Apps with SharePoint 2010 on a virtual machine demo.  On a simple Word document, the AppServerHost.exe service would jump to 100% and render the system unusable.

The problem is that Office Web Apps was not designed to be installed on a system that is also a domain controller (the case with my all-in-one virtual machine).


http://blogs.msdn.com/b/scicoria/archive/2011/04/14/getting-office-web-apps-to-run-on-a-domain-controller-appserverhost-exe.aspx

http://blogs.msdn.com/b/opal/archive/2010/04/25/faq-sharepoint-2010-rtm-installation.aspx

Here are the steps to fix the configuration:

Open SharePoint 2010 Management Shell, then run:

#Enable Word Web App (you can search by TypeName - language dependent or DisplayName - as it appears in SharePoint Central Administration)

$e = Get-SPServiceApplication | where {$_.TypeName.Equals("Word Viewing Service Application")}
$e.WordServerIsSandboxed = $false
$e.WordServerIsSandboxed

#Enable PowerPoint Web App - you need to answer "Y" for each command:
Get-SPPowerPointServiceApplication | Set-SPPowerPointServiceApplication -EnableSandboxedViewing $false

Get-SPPowerPointServiceApplication | Set-SPPowerPointServiceApplication -EnableSandboxedEditing $false

On the server, use Notepad to open c:\windows\system32\inetsrv\config\applicationHost.config.  Add the line below at the end of the dynamicTypes section.
<add mimeType="application/zip" enabled="false" />

Feb 10, 2013

Empy SharePoint Search results

We had this problem using the combination of SharePoint Foundation 2010 with Search Server Express 2010 where the results would come back empty.

The logs showed some errors with a SOAP request including an exception in: Microsoft.SharePoint.SPUser.get_Sid()

I remembered that we had moved this SharePoint installation from one domain to another and had to do some database tinkering of the UserInfo table (not recommended) after SP-MoveUser didn't quite get the job done. So I did the following:
  1. A quick listing of logins and their SID via PowerShell of all users of the root SPWeb object. Sure enough, the problematic migrated users showed a null SID. 
  2. A listing with the RawSID field showed that these users had an SID, but its format was very different from other valid SID's.
  3. I edited the UserInfo table (not recommended as it got us here in the first place) and updated the t_SystemId field to a valid SID with "Update UserInfo t_SystemId = CONVERT(VarBinary(512),"0x150049595959994939")) where t_Login = "domain\baduser". It's important to insert a unique SID (copied and modified from another user) as the t_SystemId column has a Unique index on it.
  4. After that, I did a full crawl and it seemed to find results in the search administration page. BUT THE RESULTS IN SHAREPOINT WERE STILL EMPTY!
  5. A Google Search found a tip (http://go4answers.webhost4life.com/Example/foundation-server-2010-search-returning-30817.aspx) : the URL's in the search content source must be the same as the default site URL (not the intranet, public or external URL's).
  6. Changing the content source URL fixed the problem.