Zac Mullett Engineering Services
Weblog

Source Code for Items Assigned To Me Web Part

September 14th, 2009

The Assigned To Me Web Part displays a summary list of any item currently assigned to you. This web part is useful in cases where your SharePoint site is divided into numerous sub-sites and lists. Items are grouped by list with a sum total of items assigned to you per list.

To download this web part as a compiled WSP package, or for more information, click here:

public class AssignedToMe : System.Web.UI.WebControls.WebParts.WebPart
{
    protected override void Render(System.Web.UI.HtmlTextWriter writer)
    {
        // Controls are made in Render() rather than 
        // CreateChildControls() because the TBODY
        // tag does not appear to exist in the Web.UI
        // namespace and it is required for the SharePoint
        // expander javascript to work

        SPWeb spWebThis = SPControl.GetContextWeb(this.Context);

        List<SPWeb> webs = new List<SPWeb>();
        PopulateWebCollectionRecursive(webs, spWebThis);

        #region Create table and header rows
        Table table = new Table();
        this.Controls.Add(table);
        table.Attributes["class"] = "ms-settingsframe";
        table.Width = Unit.Percentage(100);
        table.BorderStyle = BorderStyle.None;
        table.CellPadding = 1;
        table.CellSpacing = 0;
        table.Style.Add("border-top", "0px solid");

        TableRow rowHeader = new TableRow();
        table.Rows.Add(rowHeader);

        TableCell cellHeaderTitle = new TableCell();
        rowHeader.Cells.Add(cellHeaderTitle);
        cellHeaderTitle.Wrap = false;
        cellHeaderTitle.Attributes["class"] = "ms-vh2-nofilter";
        cellHeaderTitle.Attributes["scope"] = "col";
        cellHeaderTitle.Text = "Item";

        TableCell cellHeaderModified = new TableCell();
        rowHeader.Cells.Add(cellHeaderModified);
        cellHeaderModified.Wrap = false;
        cellHeaderModified.Attributes["class"] = "ms-vh2-nofilter";
        cellHeaderModified.Attributes["scope"] = "col";
        cellHeaderModified.Text = "Modified";

        TableCell cellHeaderModifiedBy = new TableCell();
        rowHeader.Cells.Add(cellHeaderModifiedBy);
        cellHeaderModifiedBy.Wrap = false;
        cellHeaderModifiedBy.Attributes["class"] = "ms-vh2-nofilter";
        cellHeaderModifiedBy.Attributes["scope"] = "col";
        cellHeaderModifiedBy.Text = "Modified By";

        TableCell cellHeaderList = new TableCell();
        rowHeader.Cells.Add(cellHeaderList);
        cellHeaderList.Wrap = false;
        cellHeaderList.Attributes["class"] = "ms-vh2-nofilter";
        cellHeaderList.Attributes["scope"] = "col";
        cellHeaderList.Text = "List";

        TableCell cellHeaderSite = new TableCell();
        rowHeader.Cells.Add(cellHeaderSite);
        cellHeaderSite.Wrap = false;
        cellHeaderSite.Attributes["class"] = "ms-vh2-nofilter";
        cellHeaderSite.Attributes["scope"] = "col";
        cellHeaderSite.Text = "Site";

        table.RenderBeginTag(writer);
        rowHeader.RenderControl(writer);
        #endregion

        int count = 0;
        string rowClass = "";

        int listIndex = 0;
        foreach (SPWeb spWeb in webs)
        {
            foreach (SPList spList in spWeb.Lists)
            {
                try
                {
                    SPField spFieldAssignedTo = spList.Fields.GetFieldByInternalName("AssignedTo");
                    if (spFieldAssignedTo == null)
                        throw new Exception();
                }
                catch
                {
                    continue;
                }

                SPQuery spQuery = new SPQuery();

                spQuery.Query = String.Format(
                    "<Where><Eq><FieldRef Name='{0}' LookupId='TRUE' /><Value Type='User'>{1}"
                    + "</Value></Eq></Where>",
                    "AssignedTo",
                    spWebThis.CurrentUser.ID.ToString());

                SPListItemCollection spListItemCollection = spList.GetItems(spQuery);

                if (spListItemCollection.Count > 0)
                {
                    listIndex++;

                    // This is a (messy) replication of the code used
                    // by SharePoint's expanding/collapsing list viewer 
                    LiteralControl headerLiteral = new LiteralControl(String.Format(
                        "<TBODY id=\"titl{0}_\" groupString=\"\"><TR><TD colspan=\"100\" class=\"ms-gb\""
                        + " style=\"border-bottom: 0; border-top: 0;\" nowrap>"
                        + "<img src=\"/_layouts/images/blank.gif\" alt=\"\" height=1 width=0>"
                        + "<a href=\"javascript:\" onclick=\"javascript:ExpCollGroup('{0}_','img_{0}_');"
                        + "return false;\">"
                        + "<img id=\"img_{0}_\" src=\"/_layouts/images/plus.gif\" alt=\"Expand/Collapse\""
                        + " border=\"0\"></a>&nbsp;"
                        + "<a href=\"javascript:\" onclick=\"javascript:ExpCollGroup('{0}_','img_{0}_');"
                        + "return false;\">"
                        + "{1}</a>&nbsp;({2})</TD></TR></TBODY><TBODY id=\"tbod{0}__\""
                        + " style=\"display: none;\" isLoaded=\"true\">",
                        "1-" + listIndex,
                        spList.Title,
                        spListItemCollection.Count));

                    headerLiteral.RenderControl(writer);

                    int item = 0;
                    foreach (SPListItem spListItem in spListItemCollection)
                    {
                        #region Create controls for basic item information
                        TableRow rowItem = new TableRow();
                        rowItem.Attributes["class"] = rowClass;
                        table.Rows.Add(rowItem);

                        TableCell cellItemTitle = new TableCell();
                        rowItem.Cells.Add(cellItemTitle);
                        cellItemTitle.Attributes["class"] = "ms-vb2";
                        cellItemTitle.VerticalAlign = VerticalAlign.Top;

                        HyperLink anchorItemTitle = new HyperLink();
                        cellItemTitle.Controls.Add(anchorItemTitle);
                        anchorItemTitle.NavigateUrl = spList.Forms[PAGETYPE.PAGE_DISPLAYFORM]
                            .ServerRelativeUrl + "?ID=" + spListItem.ID;
                        string strItemName = "";

                        SPField fieldTitle = spListItem.Fields.GetFieldByInternalName("Title");
                        strItemName = fieldTitle.GetFieldValueAsText(spListItem[fieldTitle.Id]);

                        if (strItemName == null || strItemName.Trim().Length == 0)
                            strItemName = "(No title)";
                        anchorItemTitle.Text = strItemName;

                        TableCell cellItemModified = new TableCell();
                        rowItem.Cells.Add(cellItemModified);
                        cellItemModified.Attributes["class"] = "ms-vb2";
                        cellItemModified.Wrap = false;
                        cellItemModified.VerticalAlign = VerticalAlign.Top;

                        SPField fieldModified = spListItem.Fields.GetFieldByInternalName("Modified");
                        cellItemModified.Text = fieldModified.GetFieldValueAsText(
                            spListItem[fieldModified.Id]);

                        TableCell cellItemModifiedBy = new TableCell();
                        rowItem.Cells.Add(cellItemModifiedBy);
                        cellItemModifiedBy.Attributes["class"] = "ms-vb2";
                        cellItemModifiedBy.Wrap = false;
                        cellItemModifiedBy.VerticalAlign = VerticalAlign.Top;

                        SPField fieldModifiedBy = spListItem.Fields.GetFieldByInternalName("Editor");
                        cellItemModifiedBy.Text = fieldModifiedBy.GetFieldValueAsHtml(
                            spListItem[fieldModifiedBy.Id]);

                        TableCell cellItemList = new TableCell();
                        rowItem.Cells.Add(cellItemList);
                        cellItemList.Attributes["class"] = "ms-vb2";
                        cellItemList.Wrap = false;
                        cellItemList.VerticalAlign = VerticalAlign.Top;

                        HyperLink anchorList = new HyperLink();
                        cellItemList.Controls.Add(anchorList);
                        anchorList.NavigateUrl = spList.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl;
                        anchorList.Text = spList.Title;

                        TableCell cellItemSite = new TableCell();
                        rowItem.Cells.Add(cellItemSite);
                        cellItemSite.Attributes["class"] = "ms-vb2";
                        cellItemSite.Wrap = false;
                        cellItemSite.VerticalAlign = VerticalAlign.Top;

                        HyperLink anchorWeb = new HyperLink();
                        cellItemSite.Controls.Add(anchorWeb);
                        anchorWeb.NavigateUrl = spWeb.ServerRelativeUrl;
                        anchorWeb.Text = spWeb.Title;

                        rowItem.RenderControl(writer);
                        #endregion

                        rowClass = (rowClass == "") ? "ms-alternating" : "";
                        count++;
                        item++;
                    }

                    TableRow rowSpacer = new TableRow();
                    table.Rows.Add(rowSpacer);

                    TableCell cellSpacer = new TableCell();
                    cellSpacer.ColumnSpan = table.Rows.Count;
                    cellSpacer.Text = "<img src=\"/_layouts/images/blank.gif\" alt=\"\" height=10 width=0>";
                    rowSpacer.Cells.Add(cellSpacer);

                    rowSpacer.RenderControl(writer);

                    LiteralControl footerLiteral = new LiteralControl(String.Format(
                        "</TBODY><TBODY id=\"foot{0}__\"></TBODY>",
                        "1-" + listIndex));

                    footerLiteral.RenderControl(writer);
                }
            }
        }

        if (count == 0)
        {
            #region Create controls for 'no items'
            TableRow rowItemNone = new TableRow();
            table.Rows.Add(rowItemNone);

            TableCell cellItemNone = new TableCell();
            rowItemNone.Cells.Add(cellItemNone);
            cellItemNone.ColumnSpan = table.Rows.Count;
            cellItemNone.Attributes["class"] = "ms-propertysheet";
            cellItemNone.VerticalAlign = VerticalAlign.Top;
            cellItemNone.Style.Add("padding-left", "5px");
            cellItemNone.Text = "No items";

            rowItemNone.RenderControl(writer);
            #endregion
        }

        table.RenderEndTag(writer);
    }

    private void PopulateWebCollectionRecursive(List<SPWeb> webs, SPWeb thisWeb)
    {
        webs.Add(thisWeb);

        // Important to use .GetSubwebsForCurrentUser() as opposed
        // to the .Webs collection, as the user may not be privileged
        // to certain webs
        foreach (SPWeb subWeb in thisWeb.GetSubwebsForCurrentUser())
            PopulateWebCollectionRecursive(webs, subWeb);
    }
}

Determining web part user permissions on a Web Part Page

March 23rd, 2009

A web part I’m creating must display an option to save settings, but only for designer (or administrator) users.

Web Part Page documents are SPListItem objects internally, even though SharePoint makes a distinction between document and list libraries in the user interface. I couldn’t find a path through the object model of finding this SPListItem that my web part instance was a part of. Instead I accessed the item via the current URL and a reference to the context’s SPWeb.

A typical problem with assessing permissions (or roles) for a non-administrative user is that SharePoint savagely denies access to it. Try/catch blocks can be ineffective here, with ASP.NET deciding to redirect to the ‘Access denied’ page. Depending on what you need to do, there are methods involving simple impersonation or more convoluted techniques.

Thankfully I found a peace-loving property .AllRolesForCurrentUser that accurately returns the item-level roles for the current user, regardless of their permissions level.

Read the rest of this entry »

DataFormWebPart’s sensitivity to XSLT parameter tag placement

February 23rd, 2009

While customising the XSLT of a DataFormWebPart I found a discrepancy between design-time and run-time interpretation of a template tag implementing param and variable tags. Placing the <xsl:param> tags first is important.

Read the rest of this entry »

Improving readability of multi-line text columns in display view

February 20th, 2009

SharePoint users have a tendency to create All Items views containing almost every column in their lists. In cases like these, the readability is often poor, and SharePoint gives users no ability to format the cells appropriately (such as text alignment, column width, etc). One case where this is most disrupting is for multi-line text columns. Single line text columns have a CSS width setting, but multi-line columns depend on the user including a sensible amount of columns to view. The image below illustrates this circumstance.

Squashed multi-line text column

Read the rest of this entry »

Consequences of the failure to incorporate negative externalities into energy prices

January 23rd, 2009

(A topical essay I wrote for an Energy Economics unit)

Cheap energy has been abundantly available for the last century. But is it really as cheap as it seems? Conventional energy trade carries external costs that neither the producer nor the consumer pays. Society at large must foot the bill for negative social and environmental by-products of energy production and consumption. This paper discusses the consequences of the failure to incorporate these externalities into the price of traded energy.

Externalities are defined as benefits or costs generated as an unintended by-product of an economic activity that do not accrue to the parties involved in the activity and where no compensation takes place[5]. In other words, any party (be it an individual, a group, a society or world-wide) external to an economic transaction may receive positive or negative impacts to which they had no intention or input in accruing.

Read the rest of this entry »

Custom rendering of a field’s value in list display mode

January 21st, 2009

In developing the Digital Signature Field for SharePoint I wanted to add a graphic to clearly display the current state of the signature. Is the signature old, new or invalid? A coloured tick can convey this intuitively while economising on size.

Controlling the rendering of a custom field’s value in most display modes is straight-forward through overrides to the SPField method GetFieldValueAsHtml(object) and the Render() methods of BaseFieldControl.  When it came to rendering the value on the list display (i.e. AllItems.aspx) I found that SharePoint limited me to doing this through the RenderPattern element in the field type definition XML, with the caveat that any values, properties or calculations be limited to the CAML View Schema elements. The MSDN documentation on Patterns of Custom Field Rendering hints that a DisplayTemplate specification will let you gain control of the display mode, including the list display. After messing around for a while I felt quite confident that it didn’t include the list display mode.

Some Googling later, I found someone who had bent the rules a little to achieve complete control over display list value rendering. This IconSet custom calculated field project uses a custom RenderPattern to generate a script reference to a custom aspx page that dynamically renders some document.write(…) code that then renders HTML. Values necessary to identifying which item and field are supplied by the CAML and passed by URL parameters.

Read the rest of this entry »

Best robots of 2008

January 20th, 2009

Singularity Hub rounded up the most interesting developments in robotics through 2008. Most of them are the humanoid variety. I know replicating biped movement and stability is a challenging task and still at the forefront of robotics pursuits, but I always like to see the evolutions in alternative robotic anatomies.

In my opinion, the coolest, and by far the most creepy, is a quadruped that is capable of traversing almost any terrain while carrying 150kg on its back. It’s called BigDog, but I think it’s more of a big mountain goat that buzzes like a wasp. The video is great - BigDog stumbing around foolishly on black ice, and swaggers to reclaim balance after a guy kicks it with all his weight. Are we permitted a little schadenfreude for robotic animals?