Category Archives: SP2010 Customization

We all know what IsDlg does… wait, what the…??

Here’s a little nugget I came across, on SharePoint 2010 and 2013 (including O365), and was exceptionally frustrated with. There’s a difference between IsDlg and isdlg. See it? Case sensitivity. Come to find out that means the world to SharePoint.

I was tasked to setup a basic Page Viewer web part to consume a page from another web. Simple enough. I threw the URL into the web part, including IsDlg=1, and blamo, I got my page. However, I couldn’t scroll the web part. The page I was using was longer than the web part, which would normally cause the page to scroll, but alas, not in this web part.

Enter the IsDlg parameter. Don’t know what IsDlg is? It’s a little query string parameter that SharePoint uses to hide elements on your page using CSS. Have you noticed that if you enable the dialog on a list, and the forms then appear in the dialog window, that the header is missing? The left nav too?

Dialog Window in Use

It’s the same page as if you didn’t use the dialog setting, Microsoft isn’t crazy enough to have two versions of a page that does the same thing. Instead when the IsDlg parameter is sent to the page as a parameter in the query string, it hides everything on the page with a CSS class of s4-notdlg.

Here’s what the URL may look like:


/sites/lozzi/Lists/Events/DispForm.aspx?ID=4&IsDlg=1

Note the IsDlg=1 at the end. I’m a big fan in questioning everything, so let’s try it out for ourselves.

Go to a display form of a list item, which is not in a dialog.

A Normal Display Form

and add &IsDlg=1 (case sensitive) to the end of the URL, hit enter.

Display Form with IsDlg=1

See how we lost the top header and left nav? Looks like the dialog up above, right? Pretty cool eh? So, as you may be thinking, “this is awesome, I can use this in the page viewer web part too”. You would be correct in your thinking! Here’s where things get a little fishy.

When you use IsDlg (again case sensitive), SharePoint does NOT scroll the window if the window requires it. Again, give it a whirl. Take your window you used above, and resize it so it’s nice and small, put the Close button below the bottom of the window.

IsDlg missing scroll bars

See? The close button is below the window, but there’s no scroll bars. If you change the IsDlg to isdlg, then you get a happier page.

Scroll bars come with isdlg

Whew, there’s our trusty little scroll bar, now I can press Close.

Let’s dig deeper: core.js

Why is this happening? Well I dove into the JavaScript that handles it, in the core.js file, there are several lines of code which look similar to:


isdlg = (ajaxNavigate.get_search()).match(new RegExp("[?&]IsDlg=1"));

JavaScript is very case sensitive, so it’s actually, and quite specifically, looking for IsDlg, not isdlg. Weird right? So if you send the page IsDlg, SharePoint forces the size of the window and disables the scrolling. If you send isdlg, SharePoint does nothing with it except for hiding the CSS elements (as we discussed earlier). Your browser’s native capabilities kick in and scrolling occurs.

This is great for the dialog interface SharePoint is use to as they will size with the page. Now the user is scrolling the entire page and not just the page within the dialog. This is annoying for us if we want to use IsDlg feature elsewhere and we want to scroll just inside the web part. We have to use the isdlg option instead.

Oh yeah, I confirmed this occurs in Internet Explorer, FireFox and Chrome too ;)

‘Til next time, Happy SharePointing!

Advertisements

Adding a lookup field to your SharePoint solution: declaratively or programmatically?

Big thanks to Chris Barbin on pointing me in the right direction for this one!

Including a lookup in your project can be tricky, and I find that most developers revert to doing it programmatically instead of declaratively. It’s easier to copy and paste some C# code than chase down issues around CAML. It can be incredibly aggravating and time consuming chasing down these little annoyances with SharePoint. Anywoo, I really wanted to make sure my project did it the right way, and with Chris’s help, I was successful!

Why bother trying declaratively?

Using SharePoint’s CAML/XML, the field is easier managed, SharePoint has to think a little less as it just reads and parses an XML file, which, if you explore the hive you’ll see virtually EVERYTHING is setup that way. Also, in SharePoint 2013, sandbox solutions only support declarative customizations, it’s true, no more codeSoooo I think SharePoint has made it rather clear, and creating it correctly now will get you in the right mindset as well as help when upgrading into SharePoint 2013.

As a quick disclaimer: obviously, if you’re not defining your list and you need to add a lookup to an existing list, it’s going to be easiest to add it to the existing list programatically. I don’t think it’s possible delcaratively.

For my scenario, let’s add 2 new lookup columns to a document library, so we can use SPServices for cascading some categories.

Doing this programmatically would look something like:


SPList SharedDocumentslist = web.Lists.TryGetList("Shared Documents");

SPList categoryList = web.Lists.TryGetList("Categories");
 SPFieldLookup categoryField = (SPFieldLookup)SharedDocumentslist
    .Fields.CreateNewField(SPFieldType.Lookup.ToString(), "Category");
 categoryField.LookupList = categoryList.ID.ToString("B");
 categoryField.LookupField = "Title";
 SharedDocumentslist.Fields.AddFieldAsXml(categoryField.SchemaXml,
    true, SPAddFieldOptions.AddToAllContentTypes);

SPList subCategoryList = web.Lists.TryGetList("Sub Categories");
 SPFieldLookup subCategoryField = (SPFieldLookup)SharedDocumentslist
    .Fields.CreateNewField(SPFieldType.Lookup.ToString(), "SubCategory");
 subCategoryField.LookupList = subCategoryList.ID.ToString("B");
 subCategoryField.LookupField = "Title";
 SharedDocumentslist.Fields.AddFieldAsXml(subCategoryField.SchemaXml,
    true, SPAddFieldOptions.AddToAllContentTypes);

SharedDocumentslist.Update();

Meh, 15+lines of code (you better have some error handling, and the trigger to run this code, so you’re probably looking at close to 40+ lines and a few different files). That’s a lot of overhead for something that can be done in a couple of lines declaratively. Check this out:

In my library’s list schema, I want to add two field tags, to the <Fields element:

<Field ID="{dbd44429-8804-458d-b86f-122141fb1070}"
Name="MyCategory"
StaticName="MyCategory"
DisplayName="Category"
Type="Lookup"
Required="TRUE"
List="Lists/Categories"
ShowField="Title"/>
<Field ID="{8C8CD65E-F2BB-45B7-8835-CC2BF97C9D5E}"
Type="Lookup"
Name="MySubCategory"
DisplayName="SubCategory"
StaticName="MySubCategory"
Required="TRUE"
List="Lists/Sub Categories"
ShowField="Title" />

Easy enough right? 2 lines (I know above shows many more, but it can be formatted for 2 lines ;) of manageable XML vs 40+ lines of C# code… tell me which one is best.

Big Note: These field elements are added to the list’s schema, these are not site columns. Something goes terribly awry making these site columns first. I’ve read others making them site columns and then using some code to handle it at the list. If you require site columns for content aggregation go for it. If I’m missing something and you can get this to work with site columns, please share!

Til next time, Happy SharePointing!

Calendar Overlays: Keeping events in the same window.

Previously, I shared about how to create a color coded calendar, and then taking that calendar a little further and jazzing it up with some color. One issue that has been identified with this method, and using calendar overlays in general, is the fact that the events open in a whole new window. Not cool.

I’ve toyed around with some custom JavaScript that can fix this, but haven’t found a solution, until now. Actually, until Kevin commented on my post and pointed me over to this MSDN forum thread.

After reading through the thread, I’ve compiled the follow block of code. You can throw this in the same web part that contains your CSS.

NOTE A few people have reported this doesn’t work in SharePoint 2013. I’ll find a solution. ;)

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
// load our function to the delayed load list
_spBodyOnLoadFunctionNames.push('calendarEventLinkIntercept');

// hook into the existing SharePoint calendar load function.
function calendarEventLinkIntercept()
{
if (SP.UI.ApplicationPages.CalendarNotify.$4a)
{
var OldCalendarNotify = SP.UI.ApplicationPages.CalendarNotify.$4a;
SP.UI.ApplicationPages.CalendarNotify.$4a = function ()
{
OldCalendarNotify();
bindEventClickHandler();
}
}
if (SP.UI.ApplicationPages.CalendarNotify.$4b)
{
var OldCalendarNotify = SP.UI.ApplicationPages.CalendarNotify.$4b;
SP.UI.ApplicationPages.CalendarNotify.$4b = function ()
{
OldCalendarNotify();
bindEventClickHandler();

}
}
// future service pack change may go here!
// if (SP.UI.ApplicationPages.CalendarNotify.???)
}

function bindEventClickHandler() {
$('.ms-acal-mdiv a, .ms-acal-ddiv a, .ms-acal-sdiv a').click(function(){EditLink2(this,'WPQ2');return false;});
$('a.ms-cal-nav').attr("href", "javascript:bindEventClickHandler(); void(0);");
}
</script>

Click here to download the script.

NOTE: Update the first line to your jQuery file if you have it somewhere on your network/site already.

UPDATE: 10/22/2013 – Updated the code above to work on Week and Day views as well.

Happy SharePointing!

Accessing the Secure Store from a Timer Job, more than once.

A neat feature within SharePoint is the Secure Store Service. It gives us the opportunity to store account credentials for external systems without having to worry about encryption, where to store it securely, etc. SharePoint handles it all for us. Another plus for SharePoint development.

Another neat feature is the Timer Service. This concept of recurring jobs is not new, as Windows Tasks can do it, and everything since the 80s has it.

I ran into an issue combining the two however. It appears as though the two don’t play well together.

A little background: we have data in SharePoint that is uploaded to a FTP site (using FTP credentials), then we call a web service (using API credentials) to say “Hey, the file is there”. Then we periodically check the status of the data (API Credentials), and when it’s completed, we download a load of data it back into SharePoint.

We decided to use a timer to post the item, then check the status, and download. This way the user could upload and move on, set it and forget it.

Since there is this neat service called Secure Store that I previously mentioned, we decided to store the FTP and API credentials in there.

When the check status timer job ran, it worked. When the upload timer ran, it would error (below), and then both timer jobs would error after that until we were able to restart the timer service.

System.ServiceModel.EndpointNotFoundException: Secure Store Service did not performed the operation.

Yes, that is a typo, and no, not from me, that’s from Microsoft.

To make matters worse, everything worked fine when running the same code from a web part. So frustrating…

So our fix? We had to null out our objects. See code below:

public static UserAccount GetSecureStoreAccount(SPSite site, string secureStoreAppID)
        {
            UserAccount secureUser = new UserAccount();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite elevatedSite = new SPSite(site.ID))
                {
                    SPServiceContext context = SPServiceContext.GetContext(site);
                    SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
                    ISecureStore iss = ssp.GetSecureStore(context);
                    using (SecureStoreCredentialCollection myCreds = iss.GetCredentials(secureStoreAppID))
                    {
                        foreach (SecureStoreCredential cred in myCreds)
                        {
                            switch (cred.CredentialType)
                            {
                                case SecureStoreCredentialType.UserName:
                                    secureUser.UserName = ReadSecureString(cred.Credential);
                                    break;
                                case SecureStoreCredentialType.Password:
                                    secureUser.Password = ReadSecureString(cred.Credential);
                                    break;
                            }
                            cred.Dispose();
                        }
                    }
                    context = null;
                    ssp = null;
                    iss = null;
                }
            });
            return secureUser;
        }

I added the cred.Dispose() (though the using statement on the collection disposes of each cred within), and then I nulled out each object when I was done. I don’t know if they all have to be nulled, I didn’t test each one individually, but this works.

Hope this helps. Happy SharePointing!

Creating an item detail dashboard view of your joined lists in SharePoint 2010

Thanks to Stan for suggesting the idea for this post, by leaving a comment here.

Our goal here is to take your joined lists and display them on a single page, so you can effectively review an item and its associated information. This is a useful task for creating views of projects with many tasks, milestones, documents, or customers with contacts, documents, or products with updates, documentation, downloads. Really, anywhere a list that is linked (via a lookup field) to another list, this can be applied.

First, let’s assume the following: we have a parent list, and a few daughter lists which have lookup fields to the parent list.

Parent and Daughter Lists

Simple enough. In my example here, I am going to use a project theme. I have a Projects list, a Tasks list, and an Issues list.

Starting with the parent list, first let’s disable the popup dialog option. We can’t edit the pages in a dialog window, plus you’ll want a nice full page to view all your info. Go to the parent list, click List in ribbon > List Settings > Advanced settings. At the bottom of the page, Select No under Launch Forms in Dialog. Click OK.

Browse back to your project list and select one of your projects. You should see the nice generic page view.

Press Site Actions in the top right, then select Edit Page. The page will switch to edit mode. Now go ahead and add your lists you wish to join. In my case, I am going to add Tasks and Issues. Your new web parts will be added to the top of the page. Make sure to drag your web parts down so your project details remains at the top.

Adding web parts to project display page

Once your web parts are added, edit each web part and set the Chrome Type (under Appearance) to Title and Border or Title Only, your choice. Make sure to click OK at the bottom of the edit web part panel to save your changes.

While we’re still in edit page mode, click the web part edit menu for your first web part (same place you went to edit the web part) and select Connections > Get Filter Values From > Projects.

Add the connection for the web part

A new dialog will appear.

Web part connections dialog

For the Provider Field Name, select ID. For the Consumer Field Name, select the  lookup field, Project. Click Finish.

Create the web part connection

Your web part will be reloaded and filtered based on your project.

Filtered connection web part

Perform these same connection steps for the other web parts on your page.

When you’re done, press Page ribbon, then Stop Editing. That’s it!

As you click and view each project, your tasks and issues will automatically filter for the selected project. Pretty sweet eh?

.

Take it further!

  • Edit the display form in SharePoint designer to add 2 columns to your page, allowing you to put additional web parts to the right of the details as well.
  • Using color coded calendars, see my post here, you can create a calendar view of your tasks and projects, color coded based on status. Create a calendar view in each list, then when creating the color coded calendar, use these lists and views!
  • Create a calendar for your tasks, color coded based on status, and add another web part to the above page to include the calendar!

Happy SharePointing!