Recently for a client engagement I used a Custom Field Iterator to control the edit and display form for a content type (more on this in an upcoming post). The requirement was for different fields to be made editable based on the state the list item was in. After deployment, the client requested that an enhancement be made such that the records could not be saved unless at least one of a series of checkboxes was checked.I wanted to use client side validation rather than handling the updating event. After digging around I found http://edinkapic.blogspot.com/2007/10/add-javascript-date-validation-into.html - which led me to PreSaveAction(). The PreSaveAction function is called from PreSaveItem(), which is defined in form.js which is loaded on SharePoint pages. If the PreSaveAction Method returns TRUE then the OnSubmit handler is called otherwise the submit action is cancelled. Unfortunately, there can be only one PreSaveAction method per page, and so co-ordination is required if multiple controls require its use.To add my script file to the page,
I used the ASP.Net Client Script Manager. I added the script from the OnPreRenderMethod() of my custom form iterator using:
if (ControlMode == SPControlMode.Edit)
{
// if this is an edit page add script file for client side checking.
this.Page.ClientScript.RegisterClientScriptInclude("syrinxeditjsfile", "/_layouts/syrinx/syrinxtaskedit.js");
}The Javascript file was then deployed to the 12\TEMPLATES\LAYOUTS\syrinx folder using a feature.
In this part of the series we will take a look at setting up authoritative pages, which allows influencing the order of results coming from different content sources. You can specify which pages have higher or lower priority when appearing in the search results by providing a rank for pages or sites.
To add a site/page to Authoritative (or non-authoritative - demoted) pages:
- Go to Sharepoint Administration Site and click on the link under "Shared Services Administration" in the "Quick Launch" panel to open the Shared Services web site.
- Open the Search Settings page by clicking on the Search Settings link in the Search section of Shared Services site
- In the Authoritative Pages section follow the Specify Authoritative Pages link
- Fill out the Specify Authoritative Pages form providing the URL's for sites/pages having most and least influence on the order of search results.
- Most Authoritative sites: http://mysite.com/most_important_site
- Least Authoritative sites: http://mysite.com/dont_care_about_search.aspx
- Press "Ok" to save results.
Last week I was working on a simple web part that would get come data from an MS SQL database and show in a grid. Per client's infrastructure requirements the connection to the sql server might only be done using integrated security, as sql users are not allowed to be created/used. By default, if you specify "Trusted_Connection = true", one of three things would happen: if the user is accessing the web site locally (the same server the MOSS WFE is running), her credentials will be used to connect to the SQL server; if the user is accessing the web site from the remote computer then depending on whether KerberOS authentication is turned on on the network either the user credentials or NT AUTHORITY/ANONIMOUS USER will be used for connection. Neither would not work in our case as the network team refused to enable KerberOS; after some research and with a hint from our inhouse MOSS guru Ryan Thomas I found that MOSS API gives you a way of executing a piece of code as the user running MOSS application pool; the way of doing it is in fact as easy as invoking a delegate;
you need to pass a delegate to a method containing your code to SPSecurity.RunWithElevatedPrivileges() method; it's even easier when using anonymous delegates feature of C# 2.0:
SPSecurity
.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(delegate() {
... your code to connect to SQL server ...
}));
And all you have left to do is to configure appropriate permissions for the App Pool user on the SQL server.
So after much googling and trying all the suggestions at
http://forums.msdn.microsoft.com/en-US/sharepointworkflow/thread/e8891d49-1d82-422e-8c7f-eb99326ec14c
We found the way to select from a list where the Assigned To user is the current user by building a view that has a filter criteria of Assigned To = [Me]. From this we got a reference to the SPView object for that view and then looked at the .Query specified:
Our resulting code:
SPQuery qry;
qry.Query = "<Where><Eq><FieldRef Name=\"AssignedTo\" /><Value Type=\"Integer\"><UserID Type=\"Integer\" /></Value></Eq></Where>";
Hope this helps someone else save the hours we spent.
Recently I was working on a utility that would query a MOSS list for items with update_timestamp field within a certain range. I used CAML queries for the task and this is when I found out that for dates in CAML query filter the time part gets omitted, i.e. following two CAML queries will return the same results even though time part of the date is different:
<Query>
<Where>
<Geq>
<FieldRef Name='Update_Timestamp' />
<Value Type='DateTime'>2008-07-14T00:00:00Z</Value>
</Geq>
</Where>
</Query>
----- and ----
<Query>
<Where>
<Geq>
<FieldRef Name='Update_Timestamp' />
<Value Type='DateTime'>2008-07-14T08:31:00Z</Value>
</Geq>
</Where>
</Query>
After some googling I found that the solution for this problem is to add "IncludeTimeValue" argument:
<Value Type='DateTime' IncludeTimeValue='TRUE' >2008-07-14T08:31:00Z</Value>
Another problem I have been facing is how to include a dynamic date filter that is evaluated at runtime instead of hardcoding the dates, e.g. I want to include items with update_timestamp 5 days back or newer, something like Today() - 5. As I discovered, there is a CAML operator that does just that:
Instead of specifying a date you can do following:
<Query>
<Where>
<Geq>
<FieldRef Name="Update_Timestamp" />
<Value Type="DateTime">
<Today OffsetDays="-5" />
</Value>
</Geq>
</Where>
</Query>
And a closing tip: to create a sharepoint-formatted date string from a DateTime object you can use SPUtility.CreateISO8601DateTimeFromSystemDateTime() method.
Last month Andrew Gelina (Syrinx's CEO) and myself had a nice conversation with Gayle Rodcay of Windows IT Pro magazine. She asked a lot of great questions about the top features/functionality we have seen requested in the SharePoint space. Her write-up of the interview went online and is available for everyone at:
http://windowsitpro.com/article/articleid/99317/betting-on-sharepoint.html
Let me know what you think!
-Ryan
I just wanted to pass along a recent article I wrote for Redmond Channel Partner magazine for the May issue. They wanted the article written to help the community with the common problem many users of SharePoint are faced with: What do I do with it? How can it help my company?
The full article can be found here:
http://rcpmag.com/columns/article.aspx?editorialsid=2584
-Ryan
I got back home to Boston after having a great week at Tech-Ed. It really was a good show. I met a lot of new people as well as catch up with some friends in the industry. There really was a lot of great content and dialogue around SharePoint to help all of us in our quest to keep learning more and more to help our clients get the best possible results. Thanks again to Tom Robbins, Chris Bowen, and Bob Familiar from Microsoft for taking us to dinner, introducing us to some great people, and really going the extra mile.
I learned about a couple tools used by the presenters to make our lives easier and wanted to pass those along to everyone:
Expresso: A regular expression builder was very interesting and will come in handy when the federated search components are added to MOSS very soon.
Fiddler: A http traffic sniffer to help you look at the breakdown of items loading on your pages. Thank you to Andrew Connell for showing this off. It was eye-opening to say the least watching him run this tool on a SharePoint page.
Speaking of Andrew Connell, I just wanted to point out what a great session he did on the last day about building your SharePoint applications with performance in mind. Not only was the content excellent, but as usual, his presentation style was engaging and interesting.
I hope to see everyone again next year,
-Ryan
Day two was better than the first day in terms of session content and interest, at least from my perspective. I took pictures, but decided it was better to get the post up than get the pictures ready.
XMOL / XAML
John Holliday gave an in-depth coding example about how you can build your own XOML/XAML editor that sits between Visual Studio and SharePoint Designer, allowing you load, modify, and build custom Workflows. Although I'm not sure I'd ever use such a tool, the exercise in seeing it built and operate shed even more light on WF.
Federated Search in MOSS 2007
Michal Gideoni from Microsoft presented a session on the new abilities of SharePoint for Search 2008 and therefore the new patch being release within a month that will add these features to existing MOSS implementations.
She demonstrated some very powerful and easy methods to build federated search web parts that basically connect to anything! Ok, maybe not anything, but just about anything that you can call via code to search and return data. Her solution was to convert almost any foreign search component's return data into an RSS format so the local federated web parts can use this for display purposes. This included Google, SQL Server, Mapping tools, etc. With some clever regular expression manipulation she was able to allow for custom search queries to look for specific items that certain searches are more qualified to return.
Finally, Michal mentioned that some new protocols for Documentum and FileNet have been released for MOSS to search and index the content. She also informed us that the new federated search web parts are no longer sealed, thus allowing us to inherit from them and begin extending their functionality. I sure hope Microsoft follows suit on this new direction with their other web parts as well!
Making SharePoint Development Better
Stacy Draper hosted a really great Birds of a Feather session for SharePoint developers. Essentially it was an open forum for those of us working in this genre to ask questions, express ideas, discuss current issues, offer solutions to problems, and generally hear about how other people are doing. It was very informative and good to hear that a lot of people experience the same issues in these environments. We met some talented developers genuinely there to learn, collaborate, and share knowledge. Thanks again Stacy!
Content Types for Document Management
Another session by John Holliday. I seem gravitate towards the types of sessions he covers. This session was a deep-dive coding example about creating Content Types in code and how you can use reflection to make this process much easier. His session is probably the "deepest" I've seen someone get into when it comes to Content Types via the object model.
One thing he mentioned that I hadn't thought of before is the ability to send documents with any/all Content Types into a more generic rules engine if you choose to validate documents/items entering your applications. Right now, you can attach Information Management Policies or Workflows to Content Types, or you can write custom code that can look at Content Types on various events (adding, updating, etc) to validate, but each of these methods is fairly specific to a certain type of document, Content Typde or validation rule. I liked the idea of building these validators that could apply to all Content Types, but your code is essentially a rules engine that looks at all the metadata for each Content Type, along with other conditions, to determine certain allowable actions during certain events. You could get clever with this approach and build a nice front-end for your rules data.
That's it for day 2!
-Ryan
Everyone knows how painful it is to try reaching a MOSS page after an IIS reset or a server reboot; what happens is ASP.NET performing page and resource compilation and caching. You can help your users save frustration by simply touching every MOSS web application once after a cold restart. Of course you would prefer to automate such a task instead of firing up a browser every time your server restarts. Isn't it easier to click on an icon on your desktop and warm-up all those 20 SharePoint servers at once?
To create a warm up script we can use Microsoft's own XMLHTTP object from XML DOM suit and Windows Scripting. The simplest VB script to touch a web page would look like:
' Check if a site url specified
if wscript.Arguments.Count <> 1 Then
wscript.Echo "Please specify a url!"
wscript.Quit
end If
Dim xmlHttp
Set xmlHttp = CreateObject("Msxml2.ServerXMLHTTP.3.0")
If Err.Number <> 0 Then Set xmlHttp = CreateObject("Microsoft.XMLHTTP")
If Err.Number <> 0 Then wscript.Quit
wscript.Echo "Touching " & CStr(wscript.Arguments(0))
xmlHttp.Open "GET", CStr(wscript.Arguments(0)), False
xmlHttp.Send
And you can call this script from a batch file using wscript.exe or cscript.exe tools which run the script in the Windows script Host:
wscript c:\warmup.vbs "http://moss.com/site"
So just add such a command for all your moss sites to a batch file and run after each cold restart.
I just wanted to do a quick write-up from day one at Tech-Ed 2008 Developer in Orlando Florida. The opening Keynote address by Bill Gates was very good. He touched upon his upcoming departure about Microsoft, but not without also leaving us with a nice picture of where he feels the technology of Microsoft is heading. The big pieces were the demonstrations about presentation with the upcoming release of SilverLight 2 Beta 2, development and middle-tier efforts with Visual Studio 2008; including some of the interesting architecture, diagram, and pattern enforcement tools. He ended with discussing and demonstrating the back-end with SQL Server 2008's ability to do spatial queries and the addition of more platform services.
Visual Studio 2008 Development for SharePoint
I also attended some interesting sessions around SharePoint technologies. There will be a 1.2 release of Visual Studio extensions for Windows SharePoint Services very soon that will support Visual Studio 2008. There will be no added functionality to the extensions, but this is still a useful milestone for moving forward with some of the new development tools. Thank you to Paul Andrew for giving us a walk-through.
SQL Server Reporting Services and SharePoint
There was a very interesting session by Prash Shirolkar on the some of the great integration features between SSRS (SQL Server Reporting Services) and MOSS. He demonstrated some great examples of SSRS reporting on information in the WSS Content Databases, building and publishing reports from SSRS to SharePoint libraries, and some of the new Web Parts available to view very comprehensive reports within SharePoint. He also gave a quick overview on an SSRS report, running in a Web Part, that was able write data back to a local SharePoint list. Interesting stuff!
Custom Routers in SharePoint Records Center
John Holliday gave a nice overview in a smaller, informal venue about building custom routers in Records Center. Building these routers is actually fairly straightforward, and there seems to be some value to this in the ability to have more granular control via code over the filtering, authenticating, and management of documents that enter your repository. Andrew Connell was sitting on the session and a very thought-provoking conversation sparked up about backing up / managing / viewing snapshots of content managed publishing sites in SharePoint. It seems like we've all been getting requests for this type of management and control over SharePoint lately.
Overall it's been a great time and has been a lot of fun talking to like-minded people working on SharePoint in the industry! More to come...
-Ryan
A comment I frequently hear is: SharePoint tabs don't always highlight properly. As it turns out, if you are in the habit of doing things a certain way, they always work.
First, make sure you are turning on "Show Pages" and/or "Show subsites". You can find this in Site Settings by clicking on Navigation.
Second, when you are adding sites and pages make sure their names don't have spaces. See my blog on best practices for working with column names for a deeper discussion; I generally extend this to everything I create in SharePoint by habit now and as it turns out why I never run into this problem.
Third, when adding pages or sites to your navigation, always browse to the intended destination; this will ensure your tabs will work properly. In fact if you compare the resulting URL after you fix a broken tab this way, you will see that the URL must be relative, not absolute.
Following these best practices should prevent the problem where SharePoint (MOSS or WSS) tabs are not highlighting.
-Joe
I'm going to write a bit about a problem I ran into when I wrote a bulk upload application for one of my SharePoint clients. I found that when I was importing a document and applying a Content Type to it I needed to find the Lookup list values for one of the import fields. In looking for a list of lookup values I started with this code:
public static SPList GetListByName(SPWeb web, string listName)
{
SPList retVal = null;
foreach (SPList list in web.Lists)
{
if (list.Title.ToLower().Equals(listName.ToLower()))
{
retVal = list;
}
}
return retVal;
}
That is, I passed in the current web and the list name that I was looking for and iterated the lists in the web looking for a name match. That code quickly became this code:
public static SPList GetListByName(SPWeb web, string listName)
{
return web.Lists[listName.ToLower()];
}
In this code I let the object's enumerator handle the iteration for me. So quick and easy and I'm done - but not so fast. The only problem was that it didn't work. The first time I looked for a Lookup Column that wasn't in the current web an Exception was thrown. Almost as soon as I started importing I needed a Lookup Column that was a site column and the document was being uploaded into the Documents folder in a sub-site, in this particular case, two sites down from the root.
Once I realized that the List existed but in a different context than that in which I was looking, I figured all I would need to do was get to the root and start looking using recursion, but instead of starting at the top and searching down, I thought that starting from where I was and moving up was a better approach. In the instance where the List is in the parent web it is easier and faster to move up using web.ParentWeb from where you are, rather than iterating down through all of the child webs using web.Webs from the root. That got me to this point:
public static SPList GetListByName(SPWeb web, string listName)
{
SPList retVal = web.Lists[listName.ToLower()];
if (retVal == null) // recurse up
{
if (web.ParentWeb != null)
{
retVal = GetListByName(web.ParentWeb, listName);
}
}
return retVal;
}
But as you can see I forgot about the Exception being thrown, so I put the recursive call into a catch block and ended up with this code:
public static SPList GetListByName(SPWeb web, string listName)
{
try
{
SPList retVal = web.Lists[listName.ToLower()];
}
catch
{
if (retVal == null) // recurse up
{
if (web.ParentWeb != null)
{
retVal = GetListByName(web.ParentWeb, listName);
}
}
}
return retVal;
}
This approach was the one that finally worked. If it didn't find the list by name in the current lists for the web it would throw an Exception and the catch block would then check to make sure that the retVal was still null, on the chance that the exception was thrown after it was set. If it was null, it looked to see if the current web had a parent web and if so used that web to recursively call the method. The two base cases in this recursion are:
- 1. The code in the try block does not throw an error, in which case the list was found, or
- 2. The web.ParentWeb is null, which means we are at the root of the site and the list does not exist.
That wraps it up for this time, hope it made your life a little easier.
In this part of the series you will learn how to set up Search Scopes. Search scopes allow to create specially defined groupings of searchable content, which can be chosen by users when executing a search.
- Go to the Sharepoint 3.0 Central Administration site
- Click on the Search Settings link in the Search section:
- Click View Scopes in the Scopes section:
- Click New Scope link in the toolbar:
- In the Create Scope wizard you can specify title, description and target search page for the new scope.
- After pressing Ok button you get back to the View Scopes page and now you see your scope in the list:
- Now click on "Add rules" link to start a New Scope Rule wizard. Four types of scope rules are supported: Web Address, Property Query, Content Source and All Content. In our example we want the scope to include MOSS site users, so we choose Property Query scope rule type, and as a property restriction we use contentclass, specifying following query: urn:content-class:SPSPeople
- Then we choose how the rule will be applied to the overall scope: whether any item that matches the rule will be included in the scope, or excluded from the scope, or any result in the scope has to match this rule. In our example we choose "Include". Then click "OK" to apply the rule.
- Now, if you want this search scope to be available before the next scheduled update, you can force update by going back to Search Settings page in Shared Services site (use site breadcrumb
) and clicking "Start update now" link:
- New search copes are not added to the existing site collections automatically. We need to perform the following steps to make the scope show in the search drop down:
- On the portal home page select Site Actions -> Site Settings -> Modify All Site Settings
- Click Search Scopes in the Site Collection Administration section
- Click Search Dropdown link
- In the scopes section choose the scopes you want to show up the search drop down, then press Ok to apply changes.
- Now wait until the scheduled update completes and you should see your new search scope among those in the search drop down list.
More Posts
Next page »