Can’t persist changes to SummaryLinkWebPart

Apparently there is a bug when trying to programmatically change the links in the Summary Link Web Part.  I tried checking out the page first ( pageFile.CheckOut(); ).  I tried allowing unsafe updates (spWeb.AllowUnsafeUpdates = true; ) and I also tried updating, checkingIn, and publishing:

pageFile.Update();
pageFile.CheckIn(“Updated by LinkUpdateTool”);
pageFile.Publish(“Updated by LinkUpdateTool”);

None of which worked…

I was adding the links using:  <> where linkItem is a SummaryLink object.

I looked up a bunch of code examples online but couldn’t find anything else to try so I started playing around with ordering of the function calls to no avail. I then dug deep into a google search and found a single line of code which resolves an apparent bug in the API:

linkWP.SummaryLinkValue = linkWP.SummaryLinkValue;

Do this after you add your new links but before so save the WebPartManager.  Ie…

wpm.SaveChanges(linkWP);

Frustrating but so relieved I got it working!!

List Item Ranking – Order your list items just like ordering columns in your list!

I wrote this code a while ago.  It took a lot of debugging and its not very elegant but I was pretty pressed for time.  If you have any suggestions on how to streamline, I’d like to hear them.

I also wrote code to attach to all custom lists via a feature.  This makes it easy to enable and disable.  Also, this has a transaction log functionality built to to archive changes in another list.  Ranks are 1-5 and are based on a field ‘category’.  For example you can have 1-5 fruits, and 1-5 in category vegetables, so there are seperate 1-5 threads going on in the list.  This can be disabled by removing the category checks.

I can show the list attaching code as well if requested, I think its pretty useful.  This post is already long enough.

public override void ItemAdding(SPItemEventProperties properties)
{
try
{
this.DisableEventFiring();
SPSite siteCollection = new SPSite(properties.WebUrl);
SPWeb srcSite = siteCollection.OpenWeb();
SPList srcList = srcSite.Lists[properties.ListId];
//SPListItem srcItem = properties.ListItem;
string myRank = properties.AfterProperties["Rank"].ToString();
string myCategory = properties.AfterProperties["Category"].ToString();

if (int.Parse(myRank) < 5)
{
IncrementRanks(srcList, myRank, myCategory, properties);
}

siteCollection.Dispose();
srcSite.Dispose();
}
catch (Exception ex)
{
properties.ErrorMessage = ex.Message + " - " + ex.Data + " + " + ex.StackTrace;
properties.Cancel = true;
}
finally
{
this.EnableEventFiring();
}
//base.ItemAdding(properties);
}

public override void ItemUpdated(SPItemEventProperties properties)
{
base.ItemUpdated(properties);
}

public override void ItemUpdating(SPItemEventProperties properties)
{
try
{
this.DisableEventFiring();
SPSite siteCollection = new SPSite(properties.WebUrl);
SPWeb srcSite = siteCollection.OpenWeb();
SPList srcList = srcSite.Lists[properties.ListId];
//SPListItem srcItem = properties.ListItem;
string myRank = properties.AfterProperties["Rank"].ToString();
string myCategory = properties.AfterProperties["Category"].ToString();

if (int.Parse(myRank) < 5 & !myRank.Equals(properties.ListItem["Rank"].ToString()))
{
IncrementRanks(srcList, myRank, myCategory, properties);
}

siteCollection.Dispose();
srcSite.Dispose();
}
catch (Exception ex)
{
properties.ErrorMessage = ex.Message + " + " + ex.Data + " + " + ex.StackTrace;
properties.Cancel = true;
}

try
{
//create transaction
SPListItem newItem = properties.OpenWeb().Lists["Transaction Log"].Items.Add();

foreach (SPField spf in properties.ListItem.Fields)
{
if (newItem.Fields.ContainsField(spf.Title) & !spf.Hidden & !spf.ReadOnlyField &
!String.IsNullOrEmpty(properties.ListItem.GetFormattedValue(spf.Title)))
{
if (!spf.Title.Equals("Attachments"))
{
if (newItem.Fields.GetField(spf.Title).GetType() == spf.GetType())
{
newItem[spf.Title] = properties.ListItem[spf.Title];
}
else
{
newItem[spf.Title] = properties.ListItem.GetFormattedValue(spf.Title);
}
}
}
}
newItem.Update();
}
catch (Exception ex)
{
properties.ErrorMessage = ex.Message + " - " + ex.Data + " - " + ex.StackTrace;
properties.Cancel = true;
}
finally
{
this.EnableEventFiring();
}
}

private static void IncrementRanks(SPList srcList, string myRank, string category, SPItemEventProperties properties)
{
bool rankExists = false;
//check if there exists a duplicate rank
SPListItem sameLiRank = null;
foreach (SPListItem spli in srcList.Items)
{
string itemRank = spli["Rank"].ToString();
if (itemRank == myRank & spli["Category"].ToString().Equals(category))
{
rankExists = true;
sameLiRank = spli;
break;
}
}

int myRankint = int.Parse(myRank);

int existingItemRank = 0;
int myOldRank = 0;
if(sameLiRank != null)
existingItemRank = int.Parse(sameLiRank["Rank"].ToString());

if (properties.ListItem != null)
myOldRank = int.Parse(properties.ListItem["Rank"].ToString());

if (rankExists & properties.ListItem != null & (myOldRank == existingItemRank - 1 | myOldRank == existingItemRank + 1))
{
sameLiRank["Rank"] = myOldRank.ToString();
sameLiRank.SystemUpdate();
}
else if (rankExists)
{
List gList = new List();
List rankedItems = new List();

foreach (SPListItem spli in srcList.Items)
{
//set up our array of ranks
int liRank = int.Parse(spli["Rank"].ToString());
if (liRank < 5 & spli["Category"].ToString().Equals(category)) // filter category here
rankedItems.Add(spli);
}

//sort the items
rankedItems.Sort(delegate(SPListItem li1, SPListItem li2) {
return li1["Rank"].ToString().CompareTo(li2["Rank"].ToString());
});

foreach (SPListItem spli in rankedItems)
{
bool doIt = false;
if(object.Equals(properties.ListItem, null))
{
doIt = true;
}
else if(properties.ListItem.UniqueId != spli.UniqueId)
{
doIt = true;
}
if (!string.IsNullOrEmpty(spli["Rank"].ToString()) &
int.Parse(spli["Rank"].ToString()) >= myRankint & doIt) //fix issue so item shouldnt modify itself
{ //check for +1 and exit loop if there isnt

spli["Rank"] = int.Parse(spli["Rank"].ToString()) + 1;
spli.SystemUpdate();
}
}
}
}

Javascript errors – Object Expected, ‘_spBodyOnLoadFunctionNames’, Expected ‘;’ init.js

Another day of Sharepoint, another error.  I’m REALLY looking forward to upgrading to 2010.  Even if I have to rebuild these sites from scratch it will be worth it, because we have so much archaic code in our site that it could crash at any time and no one would know why (I’ve been here 6 months and all the consultants that used to work here quit/were fired/ran out of money).

Well these error have been plaguing the site intermittently for months. The first symptoms were being unable to click the site actions button (well you could click it but nothing would pop up except a javascript error), and missing site images/silverlight which would come up as red X’s or just be missing completely.  Sounds like permissions, I know, but it wasn’t in this case.  By the time I put a few hours in trying to diagnose this, it would “work itself out”.  Also, while some people were having the problem, other could use the site fine.

And even better: the site works fine in Firefox!

Mystifying I know.  The first fix I implemented was adding this

/_layouts/1033/core.js?rev=CNBZRdV1h3pKuA7LsMXf3w%3D%3D;

/_layouts/1033/init.js?rev=VhAxGc3rkK79RM90tibDzw%3D%3D;

/_layouts/1033/ie55up.js?rev=VhAxGc3rkK79RM90tibDzw%3D%3D;

/_layouts/1033/ie55up.js?rev=VhAxGc3rkK79RM90tibDzw%3D%3D;

core.js was being added through a Sharepoint include, but the others weren’t although they appeared in the source HTML. But strangely enough after adding those to the head tag, the errors cleared up.  Note the semi colon at the end of the source. That is needed.  I’m not sure why, but it is.

Then, of course, my application pages were working so I needed to add that the to application.master as well.  But it didn’t work at first. Hmmm…. well turns out they were unghosted on the server so I had to go in into 12/Layouts/Templates and modify them on each WFE. Cool!  But still another error.  Oh wait, we used jquery for something (god knows what), and that was missing from the app.master so better add that as well.  Strange though that sharepoint only complained about it every month or so, for maybe 1/100 users. So I added to each WFE app.master:

/_scripts/jquery-1.3.1.js

Now the last errors to fix: _spBodyOnLoadFunctionNames.push(‘enableFlyoutsAfterDelay’); and ‘_spBodyOnLoadFunctionNames’ is undefined.  The former caused by the latter.  To fix this I found a link to this article which I used method 2 to set things right. http://support.microsoft.com/kb/931509

Everything appears to be working now!!!  I’m kind of relieved but at the same time I’m worried it will come back (as it has before).  I’ll be sure to update if it rears its ugly head once again.

AJAX and UpdatePanels and SpGridViews and Paging! O MY!!!

The hoops we jump through for usability sometimes. Its crazy!  I mean, how important can it be, that a product is convenient, fast and useful?  Why can’t it just be pretty?   /sarcasm

Ok Ive been working on this specific project for over a week now.  One of the requirements was to take two (long) lists of users, and find the difference between the two, then display them in a webpart on a sharepoint site.  Obviously SPGridView comes to mind.

And since I’m a fancy pants, I also wanted to add Paging, a progress bar, and AJAX it! 

I probably should’ve done it in that order.  But I AJAX’d it, added a progress bar, then finally paging.  I figured that’d be hardest to easiest but turns out it may have been the reverse (paging being the most time consuming).

Now I want to mention some key points to my project, that were in place to get everything working correctly.  Obviously I needed AJAX install on the server.  I downloaded the VNTG.WebConfigModification and enabled that on my farm after adding System.Web.Ajax and AjaxControlToolkit to the GAC.  This let me use the UpdatePanel effectively, as I was having trouble with postbacks even with the ScriptManager and UpdatePanel on the page.  Make sure to have a reference to System.Web.Extensions in your project and to USE System.Web.UI.

Some of these steps *might* be able to be omitted but I am not exactly sure which yet so I will list them all.  If anyone knows which ones are extraneous, please let me know and Ill make note of that.

So lets continue with some code now, shall we?  First make sure you ovveride the OnInit event in your webpart and add your ScriptManager to the page there.  You also want it to be the first control on the page, so your update panels work correctly.  To do that try this:

ScriptManager sm = ScriptManager.GetCurrent(this.Page);
      if (sm == null)
      {
        sm = new ScriptManager();
        sm.EnablePartialRendering = true;
       
      }
      this.Page.Form.Controls.AddAt(0, sm);
      this.EnsureUpdatePanelFixups();
      if (this.spgv == null)
      {
        this.spgv = new SPGridView();
        this.spgv.AutoGenerateColumns = false;
      }
      base.OnInit(e);

I have sm and spgv as private variables in the class.  Ensurepanelfixups() is a routine which strips away an onload script that Sharepoint uses that interferes with UpdatePanel and postbacks.  You can google that and pick one as there are a lot of different versions out there.

Next would be the CreateChildControls() function that should be overridden.

base.CreateChildControls();

        this.up = new UpdatePanel();
        this.up.ID = “upPolicy”;
        this.up.UpdateMode = UpdatePanelUpdateMode.Conditional;

        this.upro = new UpdateProgress();
        this.upro.AssociatedUpdatePanelID = this.up.ClientID;
        this.upro.ProgressTemplate = new UpdateProTemplate(@”images/loading.gif”, “Loading…”);

        this.spgv.PageIndexChanging += new GridViewPageEventHandler(Grid_PageIndexChanging);
        this.spgv.PageSize = 30;
        this.spgv.AllowPaging = true;

        this.up.ContentTemplateContainer.Controls.Add(this.spgv);

        this.Controls.Add(upro);
        this.Controls.Add(up);

        this.spgv.PagerTemplate = null;
       
        if (this.Page.Cache[“dt”] != null)
          this.spgv.DataSource = this.Page.Cache[“dt”];
        this.spgv.DataBind();

Important things to note:  Make sure you set the PagerTemplate to null AFTER you add the control to your UpdatePanel.  Also, cacheing (and pulling from the cache) the DataTable was the crux of my success in getting the Pager portion working.  The UpdateProgress portion can be taken from here as that tutorial helped me out in getting that working.

One thing that differed a lot from other tutorials was that I was binding my SPGridView on Load (CreateChildControls) as well as on a Button click (because I wanted some user interaction).  This proved to be formidable because I needed to NOT wipe out my DataSet but I needed an empty DataSet when the paged loaded so I didnt get the lovely null reference exception.  Ill post that code so you can easily see whats going on in the button click:

SPSite sps = new SPSite(TARGET_SITE);
        SPWeb spw = sps.OpenWeb();
        SPList spl = spw.Lists[TARGET_LIST];
        List userList = this.GetAllUsersFromList(spl);
        List ADList = this.GetAllUsersEmailFromAD();
        List nonComplianceUsers = ADList.Except(userList).ToList();
        dt = this.MakeDataTable(nonComplianceUsers);

        this.spgv.Columns.Clear();

        SPBoundField bf = new SPBoundField();
        bf.HeaderText = “Email Address”;
        bf.DataField = “Email”;
        this.spgv.Columns.Add(bf);

        this.spgv.DataSource = dt.DefaultView;
        this.Page.Cache[“dt”] = dt.DefaultView;
        this.spgv.DataBind();

 So I make my DataTable in the first block and it gets stored in a Private DataTable: dt.  I clear my columns here so if you click twice, it doesnt duplicate all the columns.  Now I add my column (its just a unique field from our AD structure: Email)  🙂   And then I assign the datasource, cache it, and bind.  I’m using constants for my destinations but I may open them up as public properties later such as to be more customizable.  Its not really a part of the requirements so perhaps it will be in V2.0.

Before I wrap up this post there is one more piece of code that is useful to have.  Its the Event handler for the pager.

    private void Grid_PageIndexChanging(object sender, GridViewPageEventArgs e)
    {
      this.spgv.PageIndex = e.NewPageIndex;
      this.spgv.DataBind();
    }

 Easy enough.  And works for my project! The cacheing makes changing pages SUPER fast, really. Try it out, its real fast.   Please let me know if you see any extra code that can be stripped away as I feel I could probably optimize a little better (cant we all?).  Also if youre interested in my ActiveDirectory routine, let me know via comment or email and I could post that as well.

Take care.

"There was an error retrieving data to display in this Web Part." with CQWP

I’m back with a new error… I knew it wouldn’t be long.

This one is tricky because the error: “There was an error retrieving data to display in this Web Part” doesn’t really tell me much of anything.  I also went into the 12 HIVE/LOGS and found little success in there as well.  We have things output as Verbose, and running Sharepoint on 3 domains makes it tough to find what you need.

So I went back to google and searched a bit more and finally came across: http://blogs.msdn.com/ecm/archive/2006/10/25/configuring-and-customizing-the-content-query-web-part.aspx

I went through the comments and found a clue to what might be the problem: “Most likely the error is caused because the lookup column allows multi choices.  Multi choices is not supported by the Cross List Query [XLQ] which is the data source of the Content Query Web Part” – Adri

Thanks Adri, I saw this on another page as well but I really didn’t know where to look with an excess of 1000 sites being in existence.  I also noted, this error was hit or miss.  I would get it on certain page, or when I was filtering/grouping on certain criteria.  Most notably, filtering by ‘Assigned to = Current User’.

Then another comment on that page (actually it was preceding): “I have just found that by grouping the items by another lookup column on that content type, the Portfolio lookup is displayed. As soon as I take this grouping off I get the error message as above. How strange.” – Sam

That pretty much solved it for me.  Obviously, group on the filtered item that is causing the issue. And…. BOOM it worked.  Also to note, if you’re displaying that field, ‘assigned to’ in my case, then you won’t have an issue and won’t have to group on it.  This can be a problem in some SQL code too, which makes sense because Sharepoint [lists] is an abstraction of SQL (more or less right?).

This error was fairly hard to solve because you don’t have much info to work with.  I’m hoping someone else who happens to be blessed with this error can stumble across my blog!

One thing to note: My case is a more rare case.  The most typical cause of this error is that your XSL or outputted HTML is malformed and needs some debugging.  I knew that this had been working perfectly at one point, so there was something else going on. Definitely check the XSL first, though.

Branding and Sharepoint Permissions

Hello again!  I’m going to share with you a tricky little situation I got into today.  Maybe not a huge issue for a Sharepoint pro, but was very frightening for me. I’m going to preface this post with a little story to set the mood.  I’m not looking for a ton of sympathy but I just want to put the project I’m working on into perspective for my readers:

First off, I’m working in a LARGE corporate environment that is migrating all business practices to Sharepoint.  WOOHOO!  Easy right?  Well I guess that is why I have a job right now… no one else wants to do it, and we’ve had two people quit leading up to my hire (within months of eachother none the less).

This project is almost two years over due now, so no rush… we’ll be careful this time and try not to fall into the same holes as we have in the last two years.  I’ve only been here a few months but I can see the wreckage and leftovers from various consultants and ex employees…

Did I mention we don’t have a test environment?

Or that I’m the ONLY developer, .NET and/or Web?

So I’m working on Branding, which is how I spend most my days here.  That is, when I’m not being a Sharepoint admin, or publishing content, or running training seminars.  Not bad for a Junior C# developer.  And I’m branding still.  On 3 different MOSS web apps, with 3 different themes of branding.  Javascript running in masterpages doing god knows what?  Look at that!! I take it out and the page runs faster and smoother.  And I dont have any weird disappearing text.  Wonder why that was there in the first place?  I’ll never know though, because the amount of original developers still on the project is ZILCH.

Ok enough rambling.  Here is the educational portion of my post.

Everything I’ve been working on is fairly stable, looks a little better than myself with a hangover, and runs a little smoother than the original implementation of webparts, css, html, etc…

I’m pretty happy and proud of my work thus far.  We’re getting ready to permission about 2-3000 users when I get the bright idea to test these permissions with a view only permissioned account.  Brilliant! So I create the account in active directly, permission it up, and go to a landing page on one of our domains. EPIC FAIL.  My “Zone Tabs” web part is all F’d up.  My custom Content Query Web Parts are erroring out. Hmmm…  Wheres the nearest cliff in Manhattan?  Well I guess theres the A train.

I double check my permission 20 times.  Experiment readding webparts, pointing the CQWPs to lists directly.  Tweaking some other crap.

Then it hits me: Don’t you have to Publish Master pages in the Master Page Gallery, then Approve them before regular users can see the changes?  Well yea.  So I double check that, and among the 3 domains I missed approving one.  Well turns out that isn’t the problem anyway.  Turns out, my custom .XSL files for my CQWPs need to published as well.  As do a few CSS files I had laying around in my Styles Libraries.  GRRR

So after lots of frustration and near suicidal moments, I was able to remedy this.  (Note to readers:  I am not suicidal, but I am a binge eater, drinker, and sleeper.  So there may have been a bit of that.  I am fairly stable overall.)  Lastly, be sure to PUBLISH and then APPROVE everything in your base Galleries, ie MasterPage, CSS, XSL… It will save you a lot in the end!

Customizing MySite — not for the faint of heart

I’m not going to get too in-depth here because, to be honest, there are some decent guides out there already (or at least one).  My problem was finding them, rummaging through all the BS only to find that the solution won’t fit my scenario.  My scenario is/was enterprise based so I needed to have the ability to deploy to a farm.  Some other caveats of my situation:

  1. *Should* be supported.
  2. Already has a feature and stapler feature from a previous developer.(these are needed to hook into the MySite creation process).  Source code no where to be found.
  3. Custom webparts, custom master page, and custom CSS need to be deployed alongside.  Custom images and some compiled Silverlight also need to be deployed. I’m banking on fact that the .XAP file can just be copied over via the feature.  This is still waiting to be tested.

Now for the good part.  First your going to want to start with this blog post by Steve Peschka:
http://blogs.msdn.com/sharepoint/archive/2007/03/22/customizing-moss-2007-my-sites-within-the-enterprise.aspx

This outlines the procedure to do the customizations.  Its fairly complicated and being a reasonably smart person myself… I got lost along the way and was not able to complete it in one sitting.  This of course frustrates me, being the prideful person I am 🙂

So today I came across a Visual Studio solution to help accomplish this: http://www.codeplex.com/CKS/Release/ProjectReleases.aspx?ReleaseId=2824

The links from Steve’s blog didn’t seem to be working, so I was lucky to come across that link through another blog.  Thats a VS2005 solution but it converts nicely if you use 2008.  All in all, that is a great place to get started, and will help you visualize what Steve is talking about on his blog (and of course, because he made the project on Codeplex too).  If will also save you some time too, if you have deadlines and don’t want to decipher everything from the ground up.

Warning: The selected master page has no approved version. This site may appear to be broken to users without the view versions right in the Master Page Gallery.

This is a simple fix for a strange problem that I didn’t expect.  I was going to apply a new masterpage to a site and some subsites when this lovely red message came up:

Warning: The selected master page has no approved version. This site may appear to be broken to users without the view versions right in the Master Page Gallery.

As that was something that I didn’t want to risk I ended up deleting and readding the page to the masterpage gallery.  I also found these directions if you’re looking for a fool proof method:


Click Site Actions –> Site Settings –> Modify All Site Settings
Click Master pages and page layouts
Click the down arrow next to your .master file
If Check In is an option, click Check In
Click OK
Click the down arrow next to your .master file again
Click Publish a Major Version
Click OK
Click the Down arrow next to your .master file
Click Approve/reject
Click the Radial Button next to Approved.
Click OK.

Check your page out again.
If that did not work, click the down arrow next to your .master file for the fourth time.
Click Manage Permissions
Make sure that Portal Members have Read rights.
If they do not, Click New –> Add Users

Type Portal Members in the Users Section
Click OK
Click Read in the Give Permission Section.
Click OK.

This should address both permission issues and approved version issues.

http://thenewpaperclip.com/2007/02/10/create-a-master-page-in-microsoft-office-sharepoint-designer-2007/