Tuesday, November 24, 2009

SmallSearchInputBox Ignores Search Scope Settings

About the only nice thing about spending hours tracing a seemingly-incomprehensible issue is the satisfaction of cracking the bugger!

I have just beaten a particularly annoying SharePoint problem, which with hindsight shouldn't have been too hard at all to trace. But only going through the steps that lead to discovering the cause was I able to reach the final "why didn't I think of that before?" realisation.

The SmallSearchInputBox search box on a standard SharePoint site was ignoring the search scopes, and only ever displaying contextual scopes. These are the steps I followed:
  1. Looked at all the enabled features at all scopes (farm, web application, site and web), but nothing unusual was visible there
  2. Tried creating a new search box feature with lower sequence numbers than the normal "25" used by the enhanced search feature. Still no change. Tried activating this feature at all scopes, but still no effect
  3. Created a new content placeholder in the master page, set the visibility to false for the existing content holder that holds the "SmallSearchInputBox" delegate control. Added a delegate control with a new ID to the new content placeholder, and tried activating a feature adding a search box to that new custom placeholder. The new search box always repeated the behaviour of the now-hidden one (i.e. still ignored the search scopes
Finally I wrote a small console application that lists the delegate controls that are activated at all scopes in the farm. There I found that some non-standard feature was adding the SmallSearchInputBox with the lowest sequence number. Here's a screenshot of the control delegates from my custom application (minus the offending instance, as I took this after the fix!):



Phew, at last some headway. I could then do a text search in the Features folder of the 12 hive to find the XML file that set the sequence number for a SmallSearchInputBox control to "10". Found the feature, that happened to be configured as "hidden", uninstalled that feature, and at last the search is good again.

Now time to relax!

Tuesday, May 19, 2009

WSPBuilder Fails to Copy Assembly to GAC

Small tip I just found when having an issue using the WSPBuilder "Copy to GAC" option.

For this one particular web part assembly, when selecting that WSPBuilder "Copy to GAC" option from the context menu in Visual Studio, the copy operation failed with the following message:

System.BadImageFormatException: The module was expected to contain an assembly manifest

Examining the references in that Visual Studio project, I found that one reference for some reason had the "Copy Local" property set to True. By setting this to False (and thereby matching the settings for all the other references), the assembly successfully copies to the GAC using WSPBuilder

Thursday, April 30, 2009

And I Quote, “Problem I Find With SharePoint…”

Just been reading a newsgroup posting in which the author recommends to “avoid SharePoint” as a CMS and to “build your own so it can adapt and fit 100% into your business”.

Why that suggestion? Judging from the rest of the post it seems to boil down to this - “Problem I find with SharePoint is it allows everyone to become a publisher and sites quickly get out of control with everything being dumped on it”.

Could rephrase that sentiment as “problem I find with a hammer is that it allows everyone to knock in screws and so splits the timber”. SharePoint is but a tool. And as a tool, it needs policies and procedures in place to give guidance and control over its usage.

SharePoint doesn’t allow everyone to be a publisher – the IT department who deploys it or the power users who configure it are the ones who can decide to allow everyone to become a publisher.

It’s the same with any corporate software tool or platform; governance and planning are vital from the very start of the implementation cycle. As is gaining a thorough understanding of the tool.

Tuesday, April 7, 2009

SharePoint Custom Field - jQuery Script in a List Form

I have just released on CodePlex the installable WSP solution files and the source code for this custom SharePoint field that enables jQuery to be added to list forms. It comes with two WSP files, one that deploys jQuery and one that does not (for farms that already have jQuery installed somewhere)

Useful for making minor rendering changes to controls and text in the new item, edit item or display item forms in a SharePoint list. The field has a custom editor which accepts the script to render on selected forms. Here's a screenshot of the field editor



As an example of its usage, I needed to enlarge the select lists in a multi-value lookup field in a particular list. To achieve that, I added an instance of this new type of field to the list, and configured the field with the following JavaScript:


var $multiselectLookup = $("td>button:contains('Add >')", $("table.ms-long")).parent('td');
$multiselectLookup
.siblings(':first')
.children('div')
.css('width','250px')
.children('select')
.css('width','300px');
$multiselectLookup
.siblings(':last')
.children('div')
.css('width','250px')
.children('select')
.css('width','300px');

Thursday, April 2, 2009

The "Open With Windows Explorer" Action Fails - Nothing Happens!

If the "Open with Windows Explorer" action on a list or document library in SharePoint (WSS or MOSS) fails to open a view of the folder in Windows Explorer, check that the WebClient service is running on the machine on which you are viewing the site.

This occurred for me when attempting to view documents in a Windows Explorer view on a Server 2003 box - the Explorer view simply would not open even though the option was available. Temporarily enabling and starting th WebClient service (using the Services administrative tool) instantly cured the problem. I stopped the service on completion of the use of Explorer, as it may present a potential security vulnerability.

The Webclient service not running also prevents you from adding a Network Drive mapping to SharePoint lists and libraries through the Add Network Place Wizard.

Thanks to a posting from Mart Muller for this fix.

Thursday, March 26, 2009

Missing Page Editing Toolbar? Breadcrumb Links Wrong?

If a SharePoint site is being accessed through a router or a proxy server, a missing Alternate Access Mapping (AAM) setting can cause all sorts of strange and seemingly unrelated issues. Here are a few symptoms that I experienced recently when viewing a SharePoint publishing site exposed through a router:
  • The Page Editing toolbar was missing on publishing pages. The toolbar did not display when the "Edit Page" option was selected on the site actions button
  • The "Show Page Editing Toolbar" option was greyed out (disabled) in the Site Actions menu
  • The links to the site home page in the breadcrumb trail and from the tab in the top navigation bar failed to open a page (page not found)
These were all caused by the fact that the URL used to request the pages through the router differed from the URL passed to the SharePoint server by the router. And that is the purpose of the Alternate Access Mappings. Adding the appropriate AAM values cured the problems, and also improved the rendering speed of pages in the site.

For more on AAM, see the excellent series of articles named "What every SharePoint administrator needs to know about Alternate Access Mappings" on the Microsoft SharePoint Team Blog:
  1. Part 1
  2. Part 2
  3. Part 3

Thursday, March 5, 2009

Building a Query Web Service Call Declaratively in a Data Source

Experimenting with ways of retrieving data from the search query web service for rendering in data view web parts.

Couldn't seem to get any way of injecting search text inside a CONTAINS or FREETEXT predicate within the SQL search statement being sent to the query web service. Finally opted for a string construction technique - defining the start and the end of the SQL search statement in parameters in the datasource, and gluing these together around the search text term inside the selectcommand node. Surely their is a cleaner way than this - I must be missing something obvious here?


<DataSources>
  <SharePoint:SoapDataSource runat="server" SelectUrl="http://someserver/sites/DC/_vti_bin/search.asmx" SelectAction="http://microsoft.com/webservices/OfficeServer/QueryService/QueryEx" SelectPort="QueryServiceSoap" SelectServiceName="QueryService" AuthType="Windows" WsdlPath="http://ssc-moss:200/sites/test/_vti_bin/search.asmx?WSDL" XPath="" ID="SoapDataSource2">
  <SelectCommand>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <QueryEx xmlns="http://microsoft.com/webservices/OfficeServer/QueryService">
          <queryXml>{queryXmlStart}"{QueryText}*"{queryXmlEnd}< / queryXml>
/QueryEx>
      /soap:Body>
   < /soap:Envelope>
  < /SelectCommand>
  <SelectParameters>
    <WebPartPages:DataFormParameter Name="queryXmlStart" ParameterKey="queryXmlStart" PropertyName="ParameterValues" DefaultValue="&lt;QueryPacket xmlns=&quot;urn:Microsoft.Search.Query&quot;&gt;&lt;Query&gt;&lt;SupportedFormats&gt;&lt;Format&gt;urn:Microsoft.Search.Response.Document:Document&lt;/Format&gt;&lt;/SupportedFormats&gt;  &lt;Context&gt;   &lt;QueryText type=&quot;MSSQLFT&quot; language=&quot;en-us&quot;&gt;select preferredname, firstname, lastname, workemail, workphone, homephone, Title, Path, OfficeNumber, ActiveAsText, jobtitle, pictureurl, description, write, rank, size from scope() where &quot;scope&quot;= 'People' and CONTAINS(DefaultProperties,'"/>
    <WebPartPages:DataFormParameter Name="queryXmlEnd" ParameterKey="queryXmlEnd" PropertyName="ParameterValues" DefaultValue="') and firstname != '' order by preferredname asc&lt;/QueryText&gt;  &lt;/Context&gt;  &lt;Range&gt;   &lt;StartAt&gt;1&lt;/StartAt&gt;   &lt;Count&gt;1000&lt;/Count&gt;   &lt;/Range&gt;  &lt;EnableStemming&gt;true&lt;/EnableStemming&gt;   &lt;TrimDuplicates&gt;true&lt;/TrimDuplicates&gt;   &lt;IgnoreAllNoiseQuery&gt;true&lt;/IgnoreAllNoiseQuery&gt;   &lt;ImplicitAndBehavior&gt;true&lt;/ImplicitAndBehavior&gt;   &lt;IncludeRelevanceResults&gt;true&lt;/IncludeRelevanceResults&gt;   &lt;IncludeSpecialTermResults&gt;true&lt;/IncludeSpecialTermResults&gt;   &lt;IncludeHighConfidenceResults&gt;true&lt;/IncludeHighConfidenceResults&gt; &lt;/Query&gt;&lt;/QueryPacket&gt;"/>
    <WebPartPages:DataFormParameter Name="QueryText" ParameterKey="QueryText" PropertyName="ParameterValues" />
  < /SelectParameters>
  < /SharePoint:SoapDataSource> 

Note that the QueryText parameter is added as a Parameterbinding with location of "QueryString(qt)", meaning that the datasource will use any text in the querystring parameter named "qt" as the search text submitted to the query service.

The drawback of this is that if no default value is defined for the QueryText parameter then this dataview will show an error when no qt querystring value is supplied.

The benefit of this technique, however, is the ability to add quotes and wildcard search characters to the queryXML string in order to achieve the required search SQL

Sunday, February 15, 2009

Note Field Removes Script (and Inline Style) When Saving

I have been using a "Multiple lines of text" field (a note field) to store some HTML. Testing revealed that the HTML in the field was not quite the same as intended.

Further investigation showed that when a list item with note fields is saved, any blocks of text that resemble script are removed from the rich text in the note fields. This occurs either when saving the item through a form in the browser, or when using the "update()" API method.

The process that removes script sometimes gets too eager to "cleanse" the HTML - I found that it was removing the following inline style (not my HTML design, I hasten to add!):

BACKGROUND-IMAGE: url(http://some.site/info/communications/PublishingImages/box_1024.gif);

I didn't experiment further with this style to see whether adjusting the exact text (for example, adding quotes) might mean it was retained.

Thursday, February 12, 2009

Unlearning My Development Process

Two years - that's about the length of time I have been developing on the SharePoint 2007 platform. Over that time I have been using roughly the same development process and tools, with a few additions to my "tool-belt", and a few tweaks to the steps in the process. But no radical shifts.

Must admit I have always developed from the point of view of creating a deployable solution first, and adding functionality to that solution as I go along. Means that when the functionality is working and tested then I have a package ready to ship. Rather that than developing the packaging as a final task, and then needing to retest the deployment outcomes.

And I like CONTROL over the solution, so I have been hand-crafting some of the solution files. You know the ones - the ones that WSPBuilder hides away. I did try that tool some time ago, but had problems with certain complexities in solutions, so returned to my old ways.

Well, it's time to kick that habit and learn something new! So now that I am starting a major new project I am also trying out a new approach to my day-to-day SharePoint development. First change is to incorporate SPVisualDev from the very start, and I must say I am really impressed with the ease it offers in adding fields, content types and lists to features. It even makes adding resource files nice n'easy, so encourages me to build the features ready for localisation if required in future (I know, I know, "YAGNI", but given that the tool makes this capability easy I'm all for its inclusion).

Then I am using WSPBuilder to create the WSP files (though am disabling the "Cleanup" operation so I can still get to the manifest.xml and ddf files if necessary!).

So far so good. Now all I need is a tool to generate a batch file with the appropriate stsadm commands for deploying a set of WSP files, and I'll be "sweet as, bro" (it's a kiwi thing!)

I'll let you know what else I change for the better.

Thursday, January 29, 2009

More SharePoint Rounded Corners

Late last year I posted an item on rounding the corners of the SharePoint quick launch menu using jQuery. Recently I have completed a project where rounded corners were required on outlined blocks of information rendered in a custom web part, so I thought I'd share with you another way of using the jCorner library.

The requirement was for the web part to render a series of information blocks. The design I adopted was to drive the displayed information blocks from two lists:
  • Heading list - a custom list to define the heading and icon for each block
  • Links list - a custom list with a lookup field (pointing back to the heading list) and a URL field

Each item in the heading list is rendered as a block in the custom web part, and each block contains a heading, an icon and a series of links (coming from the links list). An example of the styling of one of the blocks is shown below:



The HTML markup that produces this box is as follows (not very semantic, unfortunately, and a few too many tags, but I needed it to get it to work within a deadline!) :

<div class='infoblocks'>
  <div class='infoblocksinglecontainer'>
    <div class='infoblockouterforcorner'>
      <div class='infoblock'>
        <div class='infoblockicon'>
          <img alt='Insurance' src='http://.../Umbrella_Icon.jpg' /><div>
          <div class='infoblocktitle'>Insurance</div>
          <div class='infoblocklinks'>
            <a class='infoblocklink' href='http://...' title='Get quotes'>Get quotes< /a>
          </div>
      </div>
    </div>
  </div>
<div>
 
The custom web part retrieves the list items from the headings list, and outputs a "infoblocksinglecontainer" DIV element for each list item, populating the various contained DIVs with the data from the list item.

The round corners are then formed with a combination of CSS and JavaScript calling methods in the jQuery.corner library.

The JavaScript is contained in a JS file deployed to the LAYOUTS folder in the 12 hive and then referenced by a SCRIPT tag output by the web part used on the page. One point to notice about the jQuery selector is the use of a locally-scoped context to improve performance. I picked up this tip from the great article entitled "Improve your jQuery - 25 Excellent Tips" by Jon Hobbs-Smith - if you use jQuery then I thoroughly recommend you have a read of that one!


$(document).ready(function() {
  AddRoundCorners();
});
function AddRoundCorners() {
  var blocksContext = $('.ms-WPBody');
  $('.infoblockouterforcorner', blocksContext).corner('9px');
  $('.infoblock', blocksContext).corner('7px');
}

The CSS is as follows:

.infoblocks {
    width:250px;
}
.infoblocks-twocolumns {
    width:520px;
}
.infoblocksinglecontainer {
    float:left;
    display:inline;
    height:1px; /* Switches on layout for this element, thereby forcing the element to contain the image */
    margin:5px;
    width:1px;
}
.infoblockouterforcorner {
    background-color:#b9d5f1;
    display:inline;
    height:1px; /* Switches on layout for this element, thereby forcing the element to contain the image */
    padding:1px 2px 1px 2px;
}
.infoblock {
    background-color:white;
    border:solid 1px #000;
    display:inline;
    height:1px; /* Switches on layout for this element, thereby forcing the element to contain the image */
    margin:2px -2px 2px 2px;
    padding:2px 2px 2px 2px;
    width:245px;
}
.infoblocktitle {
    height:1px; /* Switches on layout for this element, thereby forcing the element to contain the image */
    color:#06c;
    font-size:10pt;
    font-weight:bold;
    padding-top:3px;
    margin:0 0 0 70px;
}
.infoblockicon {
    display:inline;
    float:left;
    width:65px;
    height:100%;
}
.infoblockicon img{
    vertical-align:middle;
}
.infoblocklinks {
    margin:3px 5px 3px 70px;
    display:block;
}
.infoblocklinks a:link,.infoblocklinks a:visited{
  font-size:8pt;
  font-weight:normal;
}

Note that this CSS assumes that the icon in a block will be 60x60 in size. The CSS handles any number of infoblocklink elements correctly by allowing the block to grow in depth (not width). And to finish, here's another useful behaviour of these blocks; by setting the width of the "infoblocks" class to 520px you can have two columns of blocks rather than one.

Wednesday, January 14, 2009

Missing Buttons and Link on the Add Web Part Page

Beware when tweaking page widths in SharePoint! I was attempting to set the page width to a set size by applying a width style to the ms-main CSS class in a SharePoint site. Easy-peasy, job done.

But wait! When I went to add a web part to a page in this site by clicking on the "Add a Web Part" link at the top of a web part zone, the resulting popup page had no buttons!

First task - look at the source of that page. But which page creates that popup? By running Fiddler and clicking on a link to open that popup, the capture in Fiddler showed the page name to be "webpartgallerypickerpage.aspx" in the LAYOUTS folder in the 12 hive.

Even better it meant I could open that page in Firefox by pasting in the complete URL as recorded by Fiddler. Which in turn meant I could use Firebug to spot from where the classes in the page are sourced.

It turns out that this popup page uses the site master page and stylesheet, causing the main table in the popup window to be set to my fixed width - a width greater that the set width of the popup window. The popup is configured to show no scroll bars - hence the disappearing content!

Luckily the content table in the "webpartgallerypickerpage.aspx" has an ID of "mainTable" which is not applied to the table in other SharePoint pages. Therfore I could apply a special width in CSS to the table appearing in this popup.

Problem solved for that page, now I'd better check whether that table ID is used elsewhere...