CAML Query By User

CAML can be rather hairy, and it took me some digging to find out how to query a list for items assigned to a user (of the “User” type).  It turns out the best method to do that is to query by the user profile id.  This will pull you back items for just that specific user.

<where>
<eq>
<fieldref name=”PersonFieldName” LookupId=”TRUE”/>
<value type=”int”>UserID</value>
</eq>
</where>

Moving A SharePoint Discussion To Another List

In SharePoint there is no easy way to copy over a discussion board thread over to another board.  So for instance, if there is a thread that is in the wrong place, and needs to be moved over to a different discussion board that is of a different topic, you are fresh out of luck.  The tradition copy items through the “Manage Site Content and Structure” just doesn’t work.  The only way I found to do this while maintaining all the information is to copy it over programmatically.

In this example I created a drop down list of the the discussion boards on a site:

            foreach (SPList l in web.Lists)
            {
                if (l.BaseTemplate == SPListTemplateType.DiscussionBoard)
                {
                    ListItem entry = new ListItem();
                    entry.Text = l.Title;
                    entry.Value = web.Url + "/" + l.RootFolder;
                    ddl.Items.Add(entry);
                }
            }

Then I created a method to copy the discussion over once the source board, discussion, and destination board were set.

            string site = this.Page.Request.Url.ToString();
            using (SPSite spsSite = new SPSite(site))
            {
                using (SPWeb web = spsSite.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;

                    SPList splSource = web.GetList(ddlSourceList.SelectedItem.Value.ToString());
                    SPListItem spliDiscussion = web.GetListItem(ddlSourceDiscussion.SelectedItem.Value.ToString());
                    SPList splDestination = web.GetList(ddlDestinationList.SelectedItem.Value.ToString());

                    try
                    {
                        SPFolder fldrDiscussion = web.GetFolder(ddlSourceDiscussion.SelectedValue);
                        SPListItemCollection listCol = splDestination.Items;
                        SPListItem discussion = SPUtility.CreateNewDiscussion(listCol, spliDiscussion[SPBuiltInFieldId.Title].ToString());
                        discussion[SPBuiltInFieldId.Body] = spliDiscussion[SPBuiltInFieldId.Body];
                        discussion[SPBuiltInFieldId.Author] = spliDiscussion[SPBuiltInFieldId.Author];
                        discussion[SPBuiltInFieldId.Editor] = spliDiscussion[SPBuiltInFieldId.Editor];
                        discussion[SPBuiltInFieldId.Created] = spliDiscussion[SPBuiltInFieldId.Created];
                        discussion.Update();
                        CopyAttachments(spliDiscussion, discussion);
                        discussion.Update();
                        //copy replies
                        SPQuery q = new SPQuery();
                        q.Query = "<OrderBy><FieldRef Name='Title'/></OrderBy>";
                        q.Folder = fldrDiscussion;
                        SPListItemCollection flc = splSource.GetItems(q);
                        foreach (SPListItem li in flc)
                        {
                            SPListItem reply = SPUtility.CreateNewDiscussionReply(discussion);
                            reply[SPBuiltInFieldId.Body] = li[SPBuiltInFieldId.Body];
                            reply[SPBuiltInFieldId.Author] = li[SPBuiltInFieldId.Author];
                            reply[SPBuiltInFieldId.Editor] = li[SPBuiltInFieldId.Editor];
                            reply[SPBuiltInFieldId.Created] = li[SPBuiltInFieldId.Created];
                            reply.Update();
                            CopyAttachments(li, reply);
                            reply.Update();
                            discussion.Update();
                        }
                        //delete discussion
                        spliDiscussion.Delete();
                        this.Page.Response.Redirect(this.Page.Request.RawUrl);
                    }
                    catch { }
                    finally
                    {

                    }

                }
            }

Here is the method for copying the attachments over for the discussion as well:

        private void CopyAttachments(SPListItem spliDiscussion, SPListItem targetItem)
        {
            try
            {
                //get the folder with the attachments for the source item
                SPFolder fldrDiscAttachments = spliDiscussion.Web.Folders["Lists"].SubFolders[spliDiscussion.ParentList.Title].SubFolders["Attachments"].SubFolders[spliDiscussion.ID.ToString()];
                //Loop over the attachments, and add them to the target item
                foreach (SPFile file in fldrDiscAttachments.Files)
                {
                    byte[] binFile = file.OpenBinary();
                    targetItem.Attachments.AddNow(file.Name, binFile);
                }
            }
            catch { }
            finally
            {

            }
        }

There is one area where this is lacking, and that is the embedded content. (ie. if an image is attached, and then embedded with the full path URL, that will break, so you will either have to compensate for this in your code, or else just go into the posts and change the uploaded+embedded content manually.

Set Up – Web Config Changes For Customization

Alright, first post, but a rather necessary one. Every time I do a new SharePoint installation, I find myself constantly going straight to the web.config file to make a couple changes.  If you have spent any time with SharePoint you already know about these, but I find it’s always good for a reference.

I know that it’s not recommended, but I find creating an entry in the PageParserPath block to be invaluable for testing out some code against the API in a quick and dirty fashion. It beats having to pack up and deploy features every time I want to test a line of code.

1. First create a folder in your “Pages” directory (once you’ve turned publishing on)

2. Then, to add runnable ASP pages with custom code, modify the PageParserPaths block of the web config:

<PageParserPaths>
<PageParserPath VirtualPath=”/pages/asp/*” CompilationMode=”Always” AllowServerSideScript=”true” IncludeSubFolders=”true” />
</PageParserPaths>

The other thing I do right out of the gates is to change the error reporting over, so that I start getting valuable info, instead of the canned SharePoint error screens, which are seldomly informative.

<SafeMode MaxControls=“200“ CallStack=“false“…

to…

<SafeMode MaxControls=“200“ CallStack=“true“…

Also:

<customErrors mode=“On“/>

to….

<customErrors mode=“Off“/>

Deleting Erroring Web Parts

Have a web part on a page that is throwing an error, but unable to delete it using the “Edit Page” option?

Add:  ?contents=1 to the URL of the page. This will take you to a separate page, where it can be deleted.

Accessing SharePoint User Variables

string site = this.Page.Request.Url.ToString();
SPSite spsSite = new SPSite(site);
SPWeb spwWeb = spsSite.OpenWeb();string userEmail = spwWeb.CurrentUser.Email;
string userName = spwWeb.CurrentUser.Name;

etc.

Enabling SharePoint SRS2005 Report Builder For Access Via VPN

1. Make sure Reporting Services is up and running in Sharepoint Integration Mode
2. Enable Reporting Services for the site, test locally.
3. In IIS create a new Virtual Directory for the ReportBuilder at its default path (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\ReportBuilder).
4.  Click “Create” on the “Application Name” section.
5. Ensure anonymous access is checked for the application, and ensure that anonymous permissions have been granted on the folder. Unchecking “Integrated Windows Authentication” also may be a good idea.