Showing posts with label sharepoint. Show all posts
Showing posts with label sharepoint. Show all posts

Thursday, May 6, 2010

SPServices Error With GetListItems Call

If you get an error similar to "Guid should contain 32 digits with 4 dashes" when making an SPServices Web Service call to the GetListItems operation, check the premissions on the list you are querying - I got this error returned from the call, and found that the list permissions were set so that the current user did not have read permissions.

Granting read access to the user instantly removed the issue :)

Wednesday, March 17, 2010

SharePoint Error for One User - The File Exists

Today had an interesting support issue to investigate. Of all the users on a SharePoint intranet, one person was unable to upload documents - each attempt to upload a document to a document library resulted in the "Access Denied" page. And the ULS log revealed little of value even in verbose mode.

The other strange thing was that this user is configured as the site collection owner, and was able to create document libraries fine. But then could not add a document to the document library he had just created!

Finally got some clues when I removed that account from the site owner SharePoint group (just in case that group was causing the behaviour). When I tried to add the account back to that group, an error page displayed with the useful(?) message "The file exists (Exception from HRESULT: 0x80070050)". At least that helped to confirm that the issue was in the user account.

A bit of research revealed this post on the subject - seems likely that the SID for the account has changed at some point. The described fix is to modify the SID in one of the SharePoint database tables. That's a bit of a worry, given that fiddling with the database content is like confusing a Kiwi accent for an Australian - to be avoided. But it may be the only approach available. Let me know if you try it out and it works for ya.

Tuesday, March 2, 2010

Roadblocks in the Install of Server 2008 R2 and SharePoint 2010 Beta

First up in my path to getting SharePoint 2010 running in a Hyper-V VM was the case of the missing roles in Windows Server 2008 R2, caused by a Windows update that failed to install.

For some unpleasant reason, KB971468 had failed to install correctly, following the cancellation of the install of KB890830 (the windows update panel had stopped mid-install, and the progress bar just stayed where it was). The outcome of this was that no roles displayed in the Server Manager - instead just an error message.

With the help of some worried searching, I came across this article by Glafkos Charalambous explaining the fix - use the System Update Readiness Tool from Microsoft to discover the faulty update files, then manually extract those files from the relevant update packages and copy them to the updates folder.

Once I had replaced the bad MUM files (wonder if there are DAD files anywhere in the system, too??), then I couls rerun the other updates that were marked as failed in the Windows Update History.

Hey, honey, I got my roles back!

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...

Wednesday, November 26, 2008

jQuery in a SharePoint Custom Field

One way of gaining new SharePoint functionality without "disturbing" the structure or content of lists and document libraries is to create custom fields that use jQuery to perform client-side processing.

AJAX calls to the built-in web services are simple once the SOAP message structure has been determined. The data returned by the call can then be made to interact with and modify controls on the page using the powerful selector mechanism in jQuery.

In this post I'll cover a few tips I learnt whilst creating one such custom field.

The Ajax Call

In a previous post I covered the structure of a jQuery AJAX call to a SharePoint web service. But how to derive the actual SOAP body for that call? One way I found to do this which removed lots of the guess work was as follows:
  1. Create a simple console application in Visual Studio and add a reference to the web service you want to call

  2. Write code in that console application to call the required method on that web service. Begin by using the simplest set of parameters in the call and making sure they return data as expected before adding additional parameters. For example, in my calls to the Lists web service (lists.asmx) my first tests used empty elements for the query, viewfields and queryoptions arguments in the GetListItems() call

  3. Start up Fiddler (or your favourite HTTP traffic logging tool). Ensure that the "Capture Traffic" option is ticked in the file menu.

  4. Run your console application, which will make the web service call.

  5. In Fiddler, view the HTTP call that was made by the console app by clicking on that listed call - it will be to the URL you set in the console app, and will have a result of '200'.

If the right-hand pane of Fiddler is showing the Statistics tab, click on the Inspectors tab - this displays the outgoing message from your console app in the top half, and the return message in the bottom half. Open the Raw tab within each half of the right-hand pane, and you will see the soap envelope that was sent in the web service call in the top pane. That is the XML to be used in the data parameter of the jQuery AJAX call.

One other item to note from the Raw content of the outgoing HTTP call is the SOAPAction, as that value needs to be used to set the soapaction in the beforesend anonymous function in the jQuery AJAX call.


Communicating from SharePoint to jQuery
One important point I have not yet covered was the fact that it is possible for a custom field to render in a SharePoint form, and yet to have no displayed elements. This is one way to use jQuery to modify controls and data in the form. My custom field outputs only script tags plus an empty span. It is still necessary to hide the table row in the form that contains this custom field, as the standard SharePoint rendering of a field in the new/edit list item form shows the field title in the left column and the field content in the right.

Hiding of the row is achieved by giving the empty span a class (for example 'hiddenfield'), and then rendering in the custom field some jQuery script that hides table rows that contain spans with the 'hiddenfield' class.

For the custom field to interact with data in other fields using jQuery, data needs to be passed from the server side into the client-side script in the custom field. AJAX web service calls can be used for this once the form has rendered, but what if you want your client-side script to have knowledge about the particular controls it is required to modify?

The first step is to ensure that the page on which the custom field will be rendered references the jQuery library. My approach is to add script elements to the RenderingTemplate in the custom field ASCX user control (which is deployed to the CONTROLTEMPLATES 12 hive folder), and to deploy the jQuery library to the LAYOUTS 12 hive folder.

Then, to enable the code in the custom field to set parameters in the client-side script (for example, to set the name of another field in the new/edit form that the script is to interact with), I added in the RenderingTemplate another script tag that contains an asp:Literal server control. Custom field code in the BaseFieldControl subclass can then output script into this literal control.

I created a custom field editor which derives the information to be rendered into that Literal control, and save this information in a custom property of my custom field. The technique for storing custom properties is explained well by Anton Myslevich in this forum thread (thanks, Anton!), particularly the difficulty in saving properties for a new instance of a custom field.

To summarise, the SharePoint list administrator adds a new field of type "MyCustomjQueryField" to a list and applies some custom configuration settings in the "Create New Field" form. These settings are processed and stored as custom properties of the custom field instance. When a list item is create or edited by a list user, the new or edit form is displayed, and the custom field uses its saved custom properties to render JavaScript in a hidden table row in that form. The JavaScript then uses the power of jQuery to interact with other fields in that form.

Displaying a Message During the AJAX Call

One small use of jQuery was to show a message in the new/edit form whilst a web service call initiated by the custom field was occuring. This is important given the asynchronous nature of the call and the fact that it is possible for the call to take a noticeable time to complete.

This was achieved using the 'after()' jQuery manipulation method to add a span directly after the HTML element in the form that was being modified by the web service call. Before the jQuery AJAX call is made, I add this span, and then remove the span in the event handlers for the error and success events triggered on completion of the AJAX call.

One small but pleasing touch was to fade out the message using the 'fadeOut()' jQuery method. Unnecessary perhaps, but less jarring to the eye than the text just disappearing instantly.

Wednesday, November 19, 2008

Silverlight in a Content Editor Web Part

First trial with Silverlight - getting Scott Gu's Silverlight DIGG client running in a SharePoint web part page with the simplest possible implementation.

The steps:
  1. Created a web part page document library
  2. Uploaded DiggSample.xap into the library
  3. Create a new web part page in the library
  4. Added a content editor web part to the page.
  5. Added an object tag pointing to the XAP file in the source code of the content editor web part

And, voila.... nothing displayed! Played with adding the Silverlight.js file to the library, and various other techniques. Finally found the key was the interaction of the object tag width and height with the rendered dimensions of the web part.


Solved by either:

  • Setting a fixed height and width in the web part appearance properties, and giving the object tag a height and width of 100%, OR
  • Setting an absolute height and width for the object tag and leaving the web part with non-fixed height and width

The former approaches works better as the latter approaches results in the Silverlight element's UI not matching the web part's width.


The content of the object tag is as shown below:

Using jQuery for AJAX User Profile Queries in SharePoint

Continuing on my vein of experimenting with customisation of SharePoint using jQuery, I wanted to see if I could use the AJAX methods in jQuery to call SharePoint web services. And it just so happens a suitable requirement arose - the need to use details from the profile for the current logged-in user of a web part page.

A data view web part was required to display staff located in the same office as the current logged-in user of the web part page. The office name for each staff member was being stored in the user profiles.

Of course this functionality could have been developed in a custom web part, but I am growing to like the rapidity of creating business functionality with data view web parts. Combined with jQuery, they offer a suprising amount of flexibility.

In SharePoint designer I created a data source pointing at the user profiles. Then I created a data form web part using this data source to display a table of information on each user profile. Also included in the web part was a hidden text field that contained the login name of the current user. The purpose of this hidden value was to provide the search text for the AJAX call to the profile web service, so that I could retrieve the details for the current user.

I needed the help of Fiddler and the SharePoint Search Service Tool to help craft the necessary QueryPacket XML. Once I had the working XML, I placed it in a JavaScript file referenced within the data form web part.

The jQuery went something like this:

$(document).ready(function() {
    startAjaxOfficeSearch();
});

function startAjaxOfficeSearch() {
    var userlogin = $("#CurrentUserLogin").text();
    if (userlogin != '') {
      var queryXml = 'urn:Microsoft.Search.Response.Document:Documentselect accountname, preferredname, firstname, lastname, workemail, workphone, Title, Path, pictureurl, description, write, rank, size, OfficeNumber from scope() where "scope"= \'People\' and accountname = \'' + userlogin + '\' order by preferredname asc';
      var queryXmlOptions = '110truetruetruetruetruetruetrue';
      var completeQueryXml = queryXml + queryXmlOptions;

      var regex = new RegExp(String.fromCharCode(60), 'g');
      queryXml = completeQueryXml.replace(/</G, '&lt;').replace(/>/g, '&gt;');

      var soapMessage = String.fromCharCode(60) + '?xml version="1.0" encoding="utf-8"?>' + queryXml + '';
      $.ajax({ type: "POST",
            url: "/_vti_bin/search.asmx",
            contentType: "text/xml",
            dataType: "xml",
            data: soapMessage,
            processData: false,
            beforeSend: function(req) {
              req.setRequestHeader("X-Vermeer-Content-Type", "text/xml; charset=utf-8");
              req.setRequestHeader("soapaction", http://microsoft.com/webservices/OfficeServer/QueryService/QueryEx);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) { ajaxError(XMLHttpRequest,textStatus, errorThrown); },
            success: function(xml) { ajaxFinish(xml); }
      });
    }
}

function ajaxFinish(xml) {
  $("RelevantResults", xml).each(function() {
    var selectedOfficeValue = $("OFFICENUMBER", this).text();
    //Do other stuff with this office value
  });
}   

function ajaxError(xmlObj,textStatus,errorThrown)   {
    alert("(Ajax error: "+textStatus+")");
    alert(xmlObj.statusText);
}

The login of the current user is contained in the hidden text box with ID of 'CurrentUserLogin', as created by the data form web part. The derived query seeks exact matches for that user login against the accountname property in the user profile records - you may find this needs to query against the preferredname property instead of accountname.

The ajaxfinish method gets all the XML returned from the web service call, and uses jQuery parsing to extract the office name from the returned profile.

Note that the url parameter in the ajax call really needs to be replaced by a variable that adjusts to the actual site collection root, and that the ajaxerror function is not production ready!

I've missed out a lot of detail in this post (for instance, how to create a data source in SharePoint designer that points at the user profile store), but hopefully have given a taster of another use for jQuery, and another way of approaching SharePoint customisations.

One other tip - to find out the property names to use in the query, go to the Shared Services Administration page for your farm, open the search settings page, and then open the Metadata property mappings page. That is where you will discover those strange names (such as OFFICENUMBER)!

Wednesday, November 12, 2008

jQuery in SharePoint Example - Rounded Corners

A recent project involved the branding of MOSS to incorporate custom design elements as supplied in a PhotoShop page mockup. One of these design requirements was rounding the external corners of the quick launch menu.

Hmmm, what to do. There are plenty of techniques discussed on the web for achieving this outcome, but often the basis for these techniques is to start with clean, web-standards compliant HTML. That's certainly not what SharePoint provides - I needed to work with the HTML that is created by the mixture of master pages and user controls, and did not want to override standard elements for this styling exercise. One aim was to minimize the changes, if any, to the master page.

Many of the rounded corner approaches use JavaScript. And I was, at the time of this project, starting to see the potential of jQuery combined with SharePoint. So a little more research located a neat way forward - the jQuery.Round plug-in.

After a few minutes (hours?) experimentation I derived the jQuery statements to apply rounded corners to the menu. This of course also required adding references to the jQuery and jQuery.round libraries in the master page - so it was necessary after all to change that page!

The statements were:

function AddQuickLaunchCorners()
{
    $(".ms-quicklaunchouter").corner("10px");
    $("div.ms-quickLaunch h3.ms-standardheader span div.ms-quicklaunchheader").css("background-color", "#c1ecff").corner("top 7px");
    $("table.ms-recyclebin:last td").corner("bottom 7px");
}

Note the assumption in the last JavaScript statement that the recycle bin will be the bottom row in the quick launch menu.

These statements, together with modifications to some of the other CSS styles, resulted in the following look for the quick launch menu:




But then I came across a very annoying behaviour. Imagine the following scenario:

  • I view a page containing this menu in one tab in IE7
  • Then I open another tab in IE7 and navigate around any page in that second tab

Not an unusual behaviour really. But on returning to the first tab displaying the SharePoint page with menu, this is what the menu then looked like:


Yeuch! More "minutes" of investigation lead to the inclusion of the following code in the JavaScript file applying the dynamic styling to the page:

window.onfocus = ReapplyQuickLaunchCorners;

function ReapplyQuickLaunchCorners()
{
    RemoveQuickLaunchCorners();
    AddQuickLaunchCorners();
}

function RemoveQuickLaunchCorners()
{
    $("div.ms-quicklaunchouter>div:not(.ms-quickLaunch)").remove();
    $("div.ms-quickLaunch h3.ms-standardheader span div.ms-quicklaunchheader>div").remove();
    $("table.ms-recyclebin:last td>div").remove();
}

Not a nice solution (OK, it's a hack!) but it works and I ran out of time.

So where am I leading with this post - well, just to illustrate the great things you can do with jQuery to mould SharePoint. Sometime I'll blog about using jQuery for AJAX calls to the MOSS profile search web services, but that's for another time.....

Wednesday, October 1, 2008

Custom Web Part Pages Template Woes

Seems to have been plenty of people that have tried to extend the list Web Part Page templates made available on the spcf.aspx page. And it seems that the approach outlined in the Microsoft article "Creating Custom Web Part Page Templates for Microsoft SharePoint Products and Technologies" just doesn't work with SharePoint v3.

The approach of adding an additional option to the list of templates in a custom custspcf.aspx page seems to fail (with the "Invalid URL Parameter" message) for the following reason: according to the WSS SDK, the NewWebPage RPC method accepts a value for the WebPartPageTemplate of between 1 and 8.

That must explain why adding a ninth template to the list fails - the call to the NewWebPage method OWSSVR.DLL doesn't expect a number above 8. For the background, here is a discussion on OWSSVR by Joel Oleson.

So I guess the options if different Web Part Page templates are required are to either
  • Replace the standard ones that are located in 12\TEMPLATE\1033\STS\DOCTEMP\SMARTPGS, or to
  • Create a completely custom template selection page - but I am finding that there are some real oddities in the names of pages that appear in search results when web part pages are created in a custom manner.

Another area that needs attention in vNext!

Tuesday, August 5, 2008

Adding Default Content to a SharePoint Wiki

If you create a new Wiki through a list instance in a feature, through a site definition or programmatically, the default How To and Home page will not appear in that Wiki.

To add those page, the Microsoft.SharePoint.Utilities.SPUtility class offers the following public static method:

AddDefaultWikiContent(SPList wikiList)

Call this method to add those default pages. Or if you want more control over the content of the provisioned pages, use Reflector to disassemble the following DLL and have a look at the code in that method to see how it is done:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI\Microsoft.SharePoint.dll