Thursday, January 25, 2018

Date Range Comparisons within Nintex Workflow

I recently was tasked to amend a document submission/review workflow, where documents could only be submitted (via changing a status to 'Ready for Review') during a particular window. Typically the window was only for a certain day each month, between 7am and 7pm.

I first started by creating a separate list, which would contain a single list item that had a 'Date From' and 'Date To' field. I then added a query list action with a CAML query of:

<Query>
  <Lists>
    <List ID="{D5DA2C78-F280-4A30-9D9D-FD43997B5B08}"/>
  </Lists>
  <ViewFields>
    <FieldRef Name="ID">
  </ViewFields>
  <Where>
    <DateRangesOverlap>
      <FieldRef Name="DateFrom" />
      <FieldRef Name="DateTo" />
      <Value IncludeTimeValue="TRUE" Type="DateTime">
        <Today/>
      </Value>
    </DateRangesOverlap>
  <Where>
<Query>

Note: I substituted <Now/> for <Today/> and also a Workflow variable for the current date time (derived from a Calculate date action) to see if it made any difference.

I found that if the submission date was on a different day to today, the logic would work fine and not return any results (which is what I was checking for after the query list action). However, if the current datetime was outside of the window (which was today), it would return a result, even if the time was outside of the window.

I found the article https://community.nintex.com/thread/3213, where Paul Svetleachni said:
"The time is used to calculate if one period of time turns into next day or not. Thus only calculated based on actual day and hours are used if it is next day or not. So, filtering by specific hour/min/second is not possible, it is only used to determine if next day is added or subtracted based on calculation of date." So I tried various attempts using a set a condition action (based on https://community.nintex.com/thread/10160?commentID=32146#comment-32146), which didn't work for me. I amended this logic to the following, but still no luck:

Condition: If any value equals value
Where: "Workflow Data" - "Current Datetime"
is greater than "Workflow Data" - "DateTo"

OR

Condition: If any value equals value
Where: "Workflow Data" - "Current Datetime"
is less than "Workflow Data" - "DateFrom"

In the end I decided to convert the dates into numbers, then do my comparison that way, which WORKED!

Steps to reproduce:
1. Build String to populate Workflow variable "CurrentTimeAsNumberString". The formula for this was:
fn-FormateDate({Common:CurrentDate},"yyyyMMdd")fn-FormatDate({Common:CurrentTime},"HHmm")

This produced a string that looked like "201801250915"

2. Convert the CurrentTimeAsNumberString workflow variable to CurrentTimeAsNumber, using the Convert value action (where "Input" is CurrentTImeAsNumberString and "Store result in" is CurrentTimeAsNumber).

3. Repeat the Build String action, this time to populate the "DateFromAsNumberString" using the formula:
fn-FormateDate({WorkflowVariable:DateFrom},"yyyyMMdd")fn-FormatDate({WorkflowVariable:DateFrom},"HHmm")

4. Convert DateFromAsNumberString to DateFromAsNumber using the convert value action.

5. Repeat the Build String action, this time to populate the "DateToAsNumberString" using the formula:
fn-FormateDate({WorkflowVariable:DateTo},"yyyyMMdd")fn-FormatDate({WorkflowVariable:DateTo},"HHmm")

6. Convert DateToAsNumberString to DateToAsNumber using the convert value action.

7. Update the set condition action shown above to use the numbers instead of dates in the comparison:

Condition: If any value equals value
Where: "Workflow Data" - "CurrentTimeAsNumber"
is greater than "Workflow Data" - "DateToAsNumber"

OR

Condition: If any value equals value
Where: "Workflow Data" - "CurrentTimeAsNumber"
is less than "Workflow Data" - "DateFromAsNumber"


I hope this helps someone, as I was pulling my hair out trying to figure out why such a simple thing such as a data range comparison doesn't work naturally within SharePoint (and therefore Nintex Workflow).

Thursday, February 23, 2017

Installing SharePoint 2013 on Windows 2012 R2 with .NET 4.6.1

I ran into an issue today whereby my SharePoint installer would crash after all of the prerequisites were installed and I tried to run setup.exe. After delving a little deeper I found the article https://support.microsoft.com/en-us/help/3087184/sharepoint-2013-or-project-server-2013-setup-error-if-the-.net-framework-4.6-is-installed.

Microsoft's original response to this problem was to postpone the installation of .NET 4.6.1 until after you've installed SharePoint - well that doesn't help me when the server image already has it baked in.
Then Microsoft came out with a fix, which required a DLL file to be copied into the updates directory of the installation media (i.e. copy everything to a local directory, then copy in the dll file). See https://download.microsoft.com/download/3/6/2/362c4a9c-4afe-425e-825f-369d34d64f4e/svrsetup_15-0-4709-1000_x64.zip for SharePoint 2013 patch file.

In my case it still wasn't working, but I later discovered that it was because I was also trying to slipstream a Cumulative Update into the installation (which I normally do to speed up the installation process and remove the need for multiple PSConfig operations).

So the morale to the story - if you want to use this fix, you can't slipstream any updates into the installation also.

FYI, to find out what version of .NET is actually installed on a Windows 2012 Server, follow the steps at https://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#net_d.

Wednesday, November 16, 2016

Displaying a random SharePoint Search result using Display Templates

On a recent project I had the requirement to randomly display a staff profile on the home page of the Intranet. The staff profile would provide some basic details of the person (with a head shot), then on clicking the item the user would be redirected to a page that displayed a bunch of fun questions and the person's response to each question. The web page used its own custom page layout and associated content type to store all of the answers.

Now we've probably all seen various custom developed web parts in the past, utilising calls to the User Profile Service or some other list via CAML queries. We've probably also seen Content Query Web Part solutions. For this project I was determined to leverage search as much as possible, reducing the continual queries to the backend data source. So my technical challenge was not in getting the results (as search query rules/result sources do a good job of that); but rather randomly selecting an item to display from the result pool.

So in order to make this magic happen, I went about with the idea of creating a custom group display template (i.e. to generate the seed) and a custom item template, whose job was to render the item if it was told to do so. So how do you do this? Quite simply, you add your own variables to the Search context!

In your control template, you put the following :

<!--#_
        var randomItem = 0;  // default to first item returned from search
        try {
            var totalResults = ctx.ListData.ResultTables[0].ResultRows.length; //get the total number of results returned
            if (!isNaN(totalResults))
            {
                randomItem = Math.floor(Math.random() * totalResults);
            }
            ctx.RandomItemNumber = randomItem;  // prime our own variable for the Item display template
        }
        catch (err) {
            console.log(err);
        }
_#-->

Then in your item template, you put the following around the outer part of the display template:

<!--#_
  var showItem = true;  // default to showing the item
  try
  {
       if (ctx.CurrentItemIdx != ctx.RandomItemNumber) {
                showItem = false;
       }
  }
  catch (err)
  {
       console.log(err);
  }
  if (showItem)
  {
 _#-->
...Your Item output rendering goes here...
<!--#_
  }
_#-->

I hope this helps someone - it took a little bit of playing around to discover how to get this working, but in the end it was really quite simple.

Friday, March 27, 2015

Get all Office 365 Video Channels, Groups and Delve Boards with REST

​source: http://www.vrdmn.com/2015/01/get-all-office-365-video-channels.html​

Office 365 has introduced 3 new portals recently: Videos, Groups and Delve. Behind the scenes, the architecture of Videos and Groups is such that each Video channel is a site collection and so is each Group. For Delve boards, each board is saved as a Tag and when you add a document to a board, the document is tagged with the name of the board.

If you are working on a solution for Office 365 and want to integrate Videos, Groups or Delve, here is how you can get a list of all of them using the SharePoint REST API:

1) Get all Office 365 Video Channels with REST API:
https://siteurl.sharepoint.com/_api/search/query?querytext='contentclass:sts_site WebTemplate:POINTPUBLISHINGTOPIC'&SelectProperties='WebTemplate,Title,Path'&rowlimit=50

2) Get all Office 365 Groups with REST API:
https://siteurl.sharepoint.com/_api/search/query?querytext='contentclass:sts_site WebTemplate:Group'&SelectProperties='WebTemplate,Title,Path'&rowlimit=50

3) Get all Delve Boards with REST API:
https://siteurl.sharepoint.com/_api/search/query?querytext='(Path:"TAG://PUBLIC/?NAME=*")'&Properties='IncludeExternalContent:true'&selectproperties='Path,Title'&rowlimit=50

SharePoint 2010 Public Site Navigation not working on latest version of Safari for the Mac

The link below provides the fix, which is an update to the compat.browser file.
http://blog.sharepointexperience.com/2014/10/did-safari-or-ios-8-break-your-sharepoint-2010-site/

Wednesday, October 24, 2012

onclick attributes do not fire on custom search XSLT on a Publishing page

I had a requirement for a client this week to create a search solution for a custom list (for Contractors). Basically the client wanted to use SharePoint Search and a custom XSLT to render the list items. The two challenges I faced (without reverting to full on development):
  1. Make the results sort by the Company name. Note that OOB, the Core Search Results web part does not support this. It only supports sorting by date or relevance (which is the default).
  2. Open the results in a modal popup window, rather than redirecting the whole page to the list item.

To solve point 1, I found the blog entry http://ddkonline.blogspot.com.au/2011/12/sharepoint-2010-modifying-core-search.html which allowed me to change the sort order to company.
The second problem was more frustrating to solve. I thought that within the XSLT I would add the onclick element, calling a function to load the given URL into a SharePoint modal window. But every time I tried, the onclick event just wouldn't fire. I went back to simple onclick="alert('hello')" and it still didn't work.  Within the body of the page I added straight links, switched to HTML mode and added the onclick events there, but every time I did this, SharePoint would strip it out. The blog entry at http://blog.mastykarz.nl/tracking-links-google-analytics-sharepoint-2010-mavention-google-analytics-links-tracking/ then gave me a clue. Basically SharePoint was blocking basic onclick events from firing or removing them all together. So to solve the problem, I removed the onclick attributes, then embedded a jquery function within the XSLT to iterate through all links and add the onclick event. This time it worked. FYI, below is the XSLT block:   

<xsl:text disable-output-escaping="yes">  
<![CDATA[  
<script type="text/javascript" src="/_layouts/Inflow/cqwp.js"></script>  
<script type="text/javascript">  
$(document).ready(function () {    
  if ($("#results-table").length) {  
    $("#results-table a").click(function() {   
      openDialog($(this).attr('href'));   
      return false;  
    });    
  }  
});  
</script>  
]]>  
</xsl:text>  
<div class="srch-results" accesskey="W">  
<table id="results-table" border="1"> <tr><th>Company</th><th>Active / Inactive</th><th>Full Name</th></tr>  
<xsl:apply-templates select="All_Results/Result">    
<!-- The xsl-sort needs operate upon a single field - it doesn't work if the sort has to evaluate child nodes-->    
<xsl:sort select="company" />  
</xsl:apply-templates>  
</table>  
</div>  
<xsl:call-template name="DisplayMoreResultsAnchor" /> </xsl:template>
<!-- This template is called for each result -->
<xsl:template match="Result">  
<xsl:variable name="id" select="id"/>  
<xsl:variable name="currentId" select="concat($IdPrefix,$id)"/>
<xsl:variable name="url" select="url"/>
<tr><td>
<a id="{concat($currentId,'_Title')}">          
<xsl:attribute name="href">            
<xsl:value-of select="$url"/>          
</xsl:attribute>          
<xsl:attribute name="title">            
<xsl:value-of select="company"/>          
</xsl:attribute>         
<xsl:value-of select="company"/>        
</a>
</td>
<td><xsl:value-of select="activeinactive" /></td>
<td><xsl:value-of select="fullname" /></td>
</tr>
</xsl:template>
 

Monday, September 24, 2012

How to fix “Add Host to Workflow Farm problem” when installing Windows Azure Workflow in SharePoint2013 Preview

I had this issue today when installing Windows Azure Workflow in my SharePoint 2013 environment.

In my case, the error message was as follows:
System.Management.Automation.CmdletInvocationException: Could not successfully create management Service Bus entity 'WF_Management/WFTOPIC' with multiple retries within timespan of 00:02:05.5769235. ---> System.TimeoutException: Could not successfully create management Service Bus entity 'WF_Management/WFTOPIC' with multiple retries within timespan of 00:02:05.5769235. ---> System.UnauthorizedAccessException: The remote server returned an error: (401) Unauthorized. Authorization failed for specified action: Manage..TrackingId:3e0f0351-14d0-4620-b80e-c506156b6f7a,TimeStamp:9/24/2012 1:15:07 AM ---> System.Net.WebException: The remote server returned an error: (401) Unauthorized.

Other posts talk about ensuring your RunAs account uses a FQDN. E.g. sp_content@wingtip.com, which I had been doing. Instructions from CriticalPathTraining also say that this account should have dbcreator and securityadmin rights at the SQL level, that TCPIP should be enabled through SQL Configuration Manager, and that the RunAs account is a member of the local administrator group (which it was).

To fix this, I first went to each of the workflow databases and explicitly set the membership for the sp_content database so that it had db_owner and also was a member of the respective application roles that had been created for each database. E.g. Store.Operators and Store.Administrators in the SbManagementDB database. I then logged into the SharePoint server as the sp_content account and re-ran the Workflow configuration to join the existing farm. This time it worked!

Sunday, July 24, 2011

SharePoint Search Exception: The server did not provide a meaningful reply

Recently I had to configure search sometime after the initial build, which was done using AutoSPInstaller. To ensure consistency of approach, I created a slightly modified version of AutoSPInstallerMain.ps1 and also XML AutoSPInstallerInput files. The only real difference in the XML file was that I was instructing AutoSPInstaller to provision search, and also that I was instructing it to not provision Central Admin (as this was already provisioned and I have experienced the script failing when CA had already been created). The main difference in the AutoSPInstallerMain.ps1 was that I commented out a lot of the entries in the Setup-Services function (i.e. to only run StartSearchQueryAndSiteSettingsService and CreateEnterpriseSearchServiceApp).

Shortly after the installation/configuration of Search, users were experiencing a correlation exception when performing searches. The ULS log returned the error 'Internal server error exception: System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.'. Subsequent messages also included 'A runtime exception was detected. Details follow. Message: Thread was being aborted'.

To fix this issue I had to do the following:
1) On each SharePoint Server, modify the permissions to the C:\Windows\Temp directory to ensure that the WSS_WPG group had both read and write access (by default after the Search installation, this group only had read access)
2) Restart the 'SharePoint Server Search 14' search on each server.

Once this was done, search began to work. Hope this helps someone.

Saturday, May 07, 2011

Upgrading a Custom Search Results XSLT from 2007 to 2010

Whilst upgrading a public web site that I had previously created in SharePoint 2007, during testing I noticed this strange number appearing at the end of my upgraded search results page. After the last result I was seeing a number like 68050. This number changed as I performed more searches.
I hadn't touched my custom search results XSLT at all during the upgrade, so I was wondering why this number started appearing. To troubleshoot, I first edited the search results page and changed the XSLT so that I could see the Search Results XML. The XSLT looked like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xmp><xsl:copy-of select="*"/></xmp>
</xsl:template>
</xsl:stylesheet>

This revealed to me 2 new elements right before the end of the </all_results> tag. The extra elements were 'totalresults' and 'numberofresults'.

As SharePoint 2010 now uses Federated Search Locations with custom XSLT allocated to each location, I decided to look to see what the OOB 'Local Search Results' location was doing. Turns out that it now includes a couple of extra XSLT templates:

<xsl:template match="TotalResults">
</xsl:template>
<xsl:template match="NumberOfResults">
</xsl:template>

So I added these to extra XSLT templates into my custom XSLT and voila! No more strange number at the end of my search results.

Wednesday, April 20, 2011

What to do when Content Deployment fails

I had to help out some guys from Accenture today with the setup of content deployment for a global SharePoint 2010 Internet site. Basically the first content deployment from the Authoring to Production environment didn't send across all of the published pages. As a result, settings like 'Welcome Page' were lost. Further content deployments did not seem to help or fix what was previously broken. As SharePoint's content deployment is incremental by default (UI) I created a full content deployment job through PowerShell, but then we started seeing the error 'Unable to import folder _catalogs/masterpage/Forms/Page Layout. There is already an object with the Id in the database from another site collection'.

I used my friend Google and found this, which helped me solve the problem.

So it seems that information about what has been deployed before is not contained within the Authoring environment, but the destination environment, and deleting the site collection and re-creating it with an empty template doesn't help. In my case I deleted the web application entirely and re-created it through script. In the article, you can also detach and re-attach the content database through Central Admin.

Tuesday, March 29, 2011

Fixing the Health Analyzer SPTraceV4 issue

Within a SharePoint farm configuration (multiple servers), the SharePoint Health Analyzer will first complain that the SPTraceV4 account should not be running as a local service. If you manually fix this by setting a domain account for the trace service (on each server) and restarting the service, sometimes you will still get the message 'Built-in accounts are used as application pool or service identities' within the Health Analyzer. To fix this, do the following:

1) Create the Trace Account (e.g. DOMAIN\svc-SP_TRC) as a managed account.
2) Start up the SharePoint 2010 Management Shell in administrator mode
3) Type the following Powershell script:

$servicename = "SPTraceV4"
$managedaccountname = "DOMAIN\svc-SP_TRC"
$farm = Get-SPFarm
$SPTimerv4 = $farm.Services | Where {$_.Name -eq $servicename}
$SPTimerV4NewAccount = Get-SPManagedAccount $managedaccountname
$SPTimerv4.ProcessIdentity.CurrentIdentityType = "SpecificUser"
$SPTimerv4.ProcessIdentity.ManagedAccount = $SPTimerV4NewAccount
$SPTimerv4.ProcessIdentity.Update()

4) Goto to each server in the farm and restart the tracing service. Then do an IISRESET /noforce on each SharePoint Server
5) Open up Central Administration, view the problem report and re-analyze the built-in accounts Health Analyzer message.
6) Close the report message and refresh the page. The error should now be gone.
7) If you have moved your logs to another (e.g. non-System) drive, on the directory where logs are to be written to, ensure that the nominated domain Trace account has full read/write permissions to it. You may have to restart the trace service and do an IISRESET again to notice that activity is now being written to the LOGS directory. Note that this approach also fixes the issue where you see lots of log file entries, all 0KB in length.

Thursday, September 23, 2010

SharePoint 2010 Branding Frustrations

This is not a solution to anything, just a place to vent my anger. I hate it how SharePoint 2010 page layouts go against basic CSS best practices. The biggest issue for me at the moment is that the page layouts are rendered once again in nested tables and that nearly every web part zone has an ID of "_invisibleIfEmpty". Come on guys! It is not that hard!

Wednesday, July 21, 2010

Search Delegate Control in SharePoint 2010

Currently I am in the process of upgrading a SharePoint 2007 Internet site to 2010. One of the things that had me stuck initially was that after I did the upgrade and created a 2010 specific master page, my search delegate control was not showing up. I finally figured out that search delegate controls must have the Web Application feature scope in order to work. For my 2007 site, it was using Site Collection level scope.

Monday, May 24, 2010

Prerequisite install gotchas when installing with no Internet connectivity

I am installing a SharePoint 2010 environment at the moment for a client where Internet access from the SharePoint box is blocked. I have found that even though I installed the compulsory prereq components, unless you install all of the prereqs, the prerequisite install function fails (e.g even though the speech components are optional, the prereq installer will fail if it doesn’t find them already installed). A bigger issue is that Microsoft currently has 3 versions of the prerequisite item 'SQL 2008 R2 Analysis Services ADOMD.NET' available, where only the one at http://go.microrosft.com/fwlink/?LinkID=160390 will work because of the registry settings it makes (which the prereq installer looks for). The correct version of SQLSERVER2008_ASADOMD10.msi is 6924KB. The versions that don’t work are http://go.microsoft.com/fwlink/?LinkID=188442&clcid=0x409 (SQL 2008 R2, which is 7099KB) or http://go.microsoft.com/fwlink/?LinkId=130651 (SQL 2008, which is 4416KB).

Thursday, May 28, 2009

Performing Long Running Tasks in SharePoint

Occasionally you may have a custom action that takes a while to run. On one of my recent projects we had just this need. Basically we wanted to convert an ordinary out of the box document library into a 'Projects' Document library. This involved setting the properties of the library (e.g. versioning, content types, enforce checkout etc), adding new content types to the library, setting up workflows for the library and applying event handlers.
The way that we did this was to create an Application page (under layouts), which got called by the custom action. Within the OnLoad of this page, we had the following:

using (SPLongOperation operation = new SPLongOperation(this.Page))
{
try
{
operation.LeadingHTML = "Convert to Project Documents library";
operation.TrailingHTML = "Please wait while your document library is being prepared for project use.";
operation.Begin();

// your code goes here

operation.End(string.Format("{0}&ConversionStatus={1}", this.CurrentRequestUrlAndQuery, FormSubmitStatus.Success.ToString()));
}
catch (Exception ex)
{
// record the error
operation.End(string.Format("{0}&ConversionStatus={1}", this.CurrentRequestUrlAndQuery, FormSubmitStatus.Fail.ToString()));
}
}

How easy is that? SharePoint Rocks!

Creating SharePoint Timer Jobs

In my current project, I have been developing a bunch of functionality around project space provisioning. As part of this, I decided that as there were a lot of activities to be performed when creating and configuring the new site, developing a SharePoint Timer Job was the best option.

So lesson learnt #1 - You can't programmatically create a timer job from a site collection feature. The reason for this is that typically the application pool that looks after the site does not have permission to write to the configuration database. Creating a Timer Job requires you to be able to write the timer job definition into the configuration database, so if your application pool doesn't have the rights, then the creation of the job will fail.

So basically you have to create a Web Application level feature (i.e. activated through Application Management tab of Central Admin) with a feature receiver method that creates the timer job. E.g.

<?xml version="1.0" encoding="utf-8"?>
<Feature Id="2DEA0BA9-A8D1-4475-AD0B-8FE25EBF65A2"
Title="Project Spaces Provisioning Timer Job"
Description="Add timer job required for project space provisioning."
Version="12.0.0.0"
Hidden="FALSE"
Scope="WebApplication"
DefaultResourceFile="core"
ReceiverAssembly="Collaboration.Projects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d76123a549458435"
ReceiverClass="Collaboration.Projects.FeatureCode.ProjectProvisioningTimerJob"
xmlns="http://schemas.microsoft.com/sharepoint/">
<elementmanifests>
</feature>

Creating the timer job in code is actually quite simple. Below is the feature activation/deactivation code:

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Collaboration.Projects.Provisioning;
namespace Collaboration.Projects.FeatureCode
{
///
/// Creates the timer job that runs the site creation process for confirmed requests in the provisioning request list
///

public class CollabProjectProvisioningTimerJob : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWebApplication application = properties.Feature.Parent as SPWebApplication;
DeleteProvisioningJob(application);
// install the job
ProjectSpaceCreationTimerJob provisioningJob = new ProjectSpaceCreationTimerJob(application);
SPMinuteSchedule schedule = new SPMinuteSchedule() { BeginSecond = 0, EndSecond = 59, Interval = 30 };
provisioningJob.Schedule = schedule;
provisioningJob.Update();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
DeleteProvisioningJob(properties.Feature.Parent as SPWebApplication);
}
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
}
///
/// Deletes the provisioning job if it exists
///

///
private void DeleteProvisioningJob(SPWebApplication application)
{
foreach (SPJobDefinition job in application.JobDefinitions)
{
if (job.Name == ProjectSpaceConstants.ProvisioningJobName)
job.Delete();
}
}
}
}


Now the ProjectSpaceCreationTimerJob class just inherits from SPJobDefinition. Below is a code snippet (note that the Execute method is the actually code that gets executed when the job runs).

using System;
using System.Net;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Collaboration.Projects.Resources;
using Collaboration.Provisioning;
using Collaboration.Common;
using System.Text;

namespace Collaboration.Projects.Provisioning
{
  public class ProjectSpaceCreationTimerJob : SPJobDefinition
  {
    public ProjectSpaceCreationTimerJob() : base() { }
    public ProjectSpaceCreationTimerJob(SPWebApplication web)
: base(ProjectSpaceConstants.ProvisioningJobName, web, null, SPJobLockType.Job)
    {
      this.Title = ProjectSpaceConstants.ProvisioningJobName;
    }

    public override void Execute(Guid targetInstanceId)
    {
      // your code goes here
      base.Execute(targetInstanceId);
    }
  }
}

Hope this helps. I've written it down here so that I remember it in the future.

Thursday, November 06, 2008

Access is denied. Check that the Default Content Access Account has access to this content.

I had the following error on an Extranet site that I setup recently:
The start address <https://abc.def.com> cannot be crawled.
Access is denied. Check that the Default Content Access Account has access to this content, or add a crawl rule to crawl this content. (0x80041205).

I had read on other blogs that the trick was to login to the Index Server as the MOSS Search account, check that you can access the site, and verify the proxy/exclusions had been setup correctly. This normally works, but it didn't for me this time. And it certainly wasn't permissions as the search account had full read access to the site through the Application Policy interface within Central Admin.

In the end, I found that a registry change and a reboot of the server was required to fix it. See http://support.microsoft.com/kb/896861. I used Method 1 (Disable loopback check) to fix the problem. That is:

1. Click Start, click Run, type regedit, and then click OK.
2. In Registry Editor, locate and then click the following registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
3. Right-click Lsa, point to New, and then click DWORD Value.
4. Type DisableLoopbackCheck, and then press ENTER.
5. Right-click DisableLoopbackCheck, and then click Modify.
6. In the Value data box, type 1, and then click OK.
7. Quit Registry Editor, and then restart your computer.

I hope this helps. It frustrated the living daylights out of me at the time.

Monday, April 28, 2008

Fixing the Search Settings Forbidden (403) Error

Last week I installed hotfix 941653 and hotfix 950292 (post SP1) to a client site to fix the dreaded Error: 'length' is null or not an object that occurs with Office 2003 Standard edition and MOSS Content Types. Unfortunately, not only did the problem remain (i.e. the hotfix fixed nothing), but I started to get 403 (Forbidden) errors when I tried to navigate to Search Settings page.
To fix this problem, I had to do the following:
From the index server (I've got a front-end web server and a backend index server, both 64 bit):
stsadm -o search -action stop -f
then I did
stsadm -o search -action start -role indexquery
then I did a:
stsadm -o execadmsvcjobs
Then I went back to the front end web server, navigated to the Shared Services Admin page, selected the SSP site, then from its context menu selected 'Edit Properties'. Then from the screen I reselected what the Index server was. After this, on the front end server I did a stsadm -o execadmsvcjobs. Then search started working again (but as the index was blown away, I had to do a full reindex).

Friday, April 04, 2008

Fiddler2 and Pipeboost are your friend!

To my surprise this week, one of the sites that I am helping with went live (deployed by the customer). One of the comments from the customer was 'it is quite slow'. Well to help identify what was going on, I brought out my trusty friends - Fiddler2 and Pipeboost.

Fiddler2
Basically, Fiddler is an IE add-on that monitors what comes down the wire to the browser whenever you request a page. In includes all the details about what types of files are downloaded (jpg, swf, css, js etc) and how long it takes. It even provides a pie chart to show what file types are causing the biggest payload. My first test on the site was with all of my cache cleared (so that I measure the performance of the first time visitor to the site). Yes it was definitely slow. My second request was done without clearing the cache. In this report however, it showed that although the flash files were being cached, All of the images stored in the SiteCollectionImages library were not.

So Fiddler helped me to identify that the BlobCache had not been configured on the server. This is a setting in the Web.Config file. It helps IIS to store images that are stored in SharePoint (and therefore in the backend database) to the local file system. Thus making subsequent requests for those resources a whole lot quicker (because a SharePoint database request is not required). The syntax for BlobCache is as follows:


Pipeboost (www.pipeboost.com)
Pipeboost helped me to identify whether or not IIS Compression had been configured on the production server. As most of you would know, browsers such as IE can handle compression. There is a handshake that goes on between the server and the browser to see whether the browser supports compression. If it does, then IIS compresses the contents (thus reducing the payload) and sends it to the browser, where it is decompressed and displayed to the user. There are two types of content that can be compressed - Static (like images) and Dynamic (like ASPX pages). Static compressioncan compress the file as well as stores it in the Temporary Internet Files directory, so that it can be served up quicker next time. Dynamic waits for the ASPX to return its content, then it just sends that page compressed back to the browser (i.e. it doesn't store it in the Temporary Internet Files location).

There is a great article at http://www.dotnetjunkies.com/Article/16267D49-4C6E-4063-AB12-853761D31E66.dcik that explains how to setup IIS Compression. At the end of this article, it talks about Pipeboost, which can tell you whether or not the payload returned from the web server is compressed (i.e. via IIS Compression).

Strange Behaviour when Editing a SharePoint Search Resuls Page

As part of the the work that I do with my own company (MS-Advantage), I get to work on a lot of interesting projects, as well as work with a lot of interesting partner/development companies.
For example, currently I am doing some work on the side with a Graphic Design company who have been charged with developing the branding for a global web site. I basically provide them with guidance on how to best use and develop with SharePoint, while they do the leg work of developing the master pages and page layouts.

While I was working on the project yesterday, the customer indicated that the Search results page was returning sites and list information that they did not want to appear. To resolve the issue, I went into the search results page, edited the core results web part to add an additional query to the results (Query2 and isdocument:1, which basically says 'only show pages or documents').

Well I checked in a draft version of the search results page and started to test that it worked. As the results returned was exactly what I was after, I then published the page (so that it would get picked up in that night's content deployment job). After publishing the page however, every time I navigated to the search results page (even without providing a search keyword), the search results that I had previously used while testing were still being displayed. I tried to roll back to previous versions, but that didn't help. In the end I had to open up the page in SharePoint Designer, AND TO MY HORROR SharePoint had injected the results HTML into my web page. Once I deleted this and saved it, I was then able to re-publish the page.