Skip Ribbon Commands
Skip to main content

Robin | zevenseas | SharePoint Blog

:

The zevenseas Community > Blogs > Robin | zevenseas | SharePoint Blog > Posts > QuickTask WebPart
November 09
QuickTask WebPart

Because a picture tells more than thousand words, here is what the webpart looks like..

So what do we see? A custom webpart that displays all the non completed tasks from a given tasklist and hereby giving the user the ability of speeding up the administration of those tasks by :

  1. Complete one or several tasks at once
  2. Assign a task to an user using the context menu

Let me explain how it all works.. first of all we have to setup the custom toolbar using the Microsoft.SharePoint.WebControls.Toolbar and adding a ToolBarButton to it.

//Defining a button 
checkAllTasksToolBarButton = (ToolBarButton)Page.LoadControl("/_controltemplates/ToolBarButton.ascx");
checkAllTasksToolBarButton.Click += new EventHandler(checkAllTasksToolBarButton_Click);

checkAllTasksToolBarButton.Text = "Complete selected tasks";
checkAllTasksToolBarButton.ImageUrl = "/_layouts/images/CheckNames.gif";

//Creating the toolbar and adding the button           
webPartToolBar = (ToolBar)Page.LoadControl("/_controltemplates/ToolBar.ascx");
webPartToolBar.Buttons.Controls.Add(checkAllTasksToolBarButton);

this.Controls.Add(webPartToolBar);

Secondly we are going to put a hidden inputsection in the page so we can store all the checked items and retrieve them when the button is clicked

//Adding the .ascx with all the javascript and the hidden input form
scriptcontrolpage = (UserControl)Page.LoadControl("/_layouts/zevenseas/controltemplates/hiddeninputsection.ascx");
scriptcontrolpage.ID = "scriptcontrolpage";
this.Controls.Add(scriptcontrolpage);

Next is to define the SPDataSource

datasource = new SPDataSource();
datasource.DataSourceMode = SPDataSourceMode.List;
datasource.UseInternalName = true;
datasource.SelectCommand = "<Query><Where><Neq><FieldRef Name=\"Status\" /><Value Type=\"Text\">Completed</Value></Neq></Where><OrderBy><FieldRef Name=\"ID\" /></OrderBy></Query>";

And after that the SPGridView with it’s columns

//Defining and building the SPGrid to display the datasource
grid = new SPGridView();
grid.AutoGenerateColumns = false;

//CheckBox
BoundField checkbox = new BoundField();
checkbox.DataField = "Id";
checkbox.DataFormatString = "<input type='checkbox' group='siteCheckboxes' name='{0}' onclick='checkBoxClick();'>";
checkbox.HtmlEncode = false;
checkbox.HeaderText = "<input type='checkbox' id='selectAllCheckBox' onclick='selectAllCheckBoxClick(this);' />";
grid.Columns.Add(checkbox);

//Title menu with ECB
SPMenuField titleMenu = new SPMenuField();
titleMenu.HeaderText = "Title";
titleMenu.TextFields = "Title";
titleMenu.MenuTemplateId = "TaskListMenu";

titleMenu.TokenNameAndValueFields = "EDIT=ID";
titleMenu.SortExpression = "Title";

MenuTemplate itemListMenu = new MenuTemplate();
itemListMenu.ID = "TaskListMenu";

MenuItemTemplate editItemMenu = new MenuItemTemplate("Edit Item", "/_layouts/images/edititem.gif");
editItemMenu.ClientOnClickNavigateUrl = SPContext.Current.Web.Url + "/Lists/" + 
    taskList + "/editform.aspx?id=%EDIT%&Source=" + SPContext.Current.Web.Url;
itemListMenu.Controls.Add(editItemMenu);

SubMenuTemplate listSettings = new SubMenuTemplate();
listSettings.Text = "Assign Task to";
listSettings.Description = "Quickly assing this task to an user";
listSettings.ImageUrl = "/_layouts/images/ALLUSR.gif";
                
foreach (SPUser user in SPContext.Current.Web.Users)
{
    MenuItemTemplate listItem = new MenuItemTemplate();
    listItem.Text = user.Name.ToString();
    listItem.Description = string.Format("Assign to task to {0}", user.Name);

    string url = string.Format("{0}/_layouts/zevenseas/taskassign.aspx?List={1}&Item=%EDIT%&UserId={2}&source={3}", 
        SPContext.Current.Web.Url, taskList, user.ID.ToString(), SPContext.Current.Web.Url);
    listItem.ClientOnClickNavigateUrl = url;
    listSettings.Controls.Add(listItem);
}

itemListMenu.Controls.Add(listSettings);
this.Controls.Add(itemListMenu);

grid.Columns.Add(titleMenu);

//Status column
SPBoundField status = new SPBoundField();
status.DataField = "Status";
status.HeaderText = "Status";
grid.Columns.Add(status);

//AssignedTo Column
SPBoundUserField assigned = new SPBoundUserField();
assigned.DataField = "AssignedTo";
assigned.HeaderText = "Assigned To";
grid.Columns.Add(assigned);

this.Controls.Add(grid);

Notice the SPBoundUserField, this is a custom SPBoundField that takes care of the value returned by a User/Group Column. Check out my guest post at Bamboo’s Community blog on how to extend the SPBoundField to act like you want it to ;) Next is to describe the event of when the ToolbarButton is clicked

void checkAllTasksToolBarButton_Click(object sender, EventArgs e)
{
    //Getting a reference to the hiddenInput field to get the values
    string controlId = "ctl00" + "$" + this.Parent.ID + "$" + this.ID + "$scriptcontrolpage" + "$HiddenSiteSelections";
    System.Web.UI.HtmlControls.HtmlInputHidden inputsection = (System.Web.UI.HtmlControls.HtmlInputHidden)this.Page.FindControl(controlId);

    if (inputsection != null)
    {
        string[] ids = inputsection.Value.Split(new Char[] { '#' });
        SPList list = SPContext.Current.Web.Lists[taskList];

        foreach (string id in ids)
        {
            if (!string.IsNullOrEmpty(id))
            {
                SPListItem item = list.GetItemById(Convert.ToInt32(id));
                item["PercentComplete"] = 1;
                item["Status"] = "Completed";
                item.SystemUpdate();
            }
        }
    }

    DataBind(taskList);
}

And of course the DataBind function

void DataBind(string listName)
{
    if (!string.IsNullOrEmpty(listName))
    {
        datasource.List = SPContext.Current.Web.Lists[listName];
        try
        {
            grid.DataSource = datasource;
            grid.DataBind();
        }
        catch (Exception error)
        {
            Literal literal = new Literal();
            literal.Text = error.Message.ToString();
            this.Controls.Add(literal);
        }
    }
}

Now if you are wondering where the variable ‘taskList’ is coming from.. don’t worry.. here it is ;)

[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Select TaskList"), WebDescription("Select TaskList")]
public string taskList { get; set; }

If you look closely on the creating of the ECB menu, you see that I link to a page called ‘taskassign.aspx’. I couldn’t figure out how to use the ClientOnClickUsingPostBackEvent method of the MenuItemTemplate class in a webpart so I used the ClientOnClickNavigateUrl and pointed to a custom aspx page which does nothing than accepting all the variables and modifying the task and redirecting back to the page. Below is the code and the markup of the associated .aspx page

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" Inherits="zevenseas.Community.Web.UI.WebControls.WebParts.UITaskAssign" %>
<asp:Content ID="Content5" ContentPlaceHolderID="PlaceHolderMain" runat="server">
</asp:Content>  
public class UITaskAssign: Microsoft.SharePoint.WebControls.LayoutsPageBase
{
    protected override void CreateChildControls()
    {
        int listItem = Convert.ToInt32(Page.Request.QueryString["Item"]);
        string list = Page.Request.QueryString["List"];
        int userId = Convert.ToInt32(Page.Request.QueryString["UserID"]);
        string requestUrl = Page.Request.QueryString["Source"];

        using (SPSite site = new SPSite(SPContext.Current.Web.Url))
        {
            using (SPWeb web = site.OpenWeb())
            {
                web.AllowUnsafeUpdates = true;
                try
                {
                    SPListItem taskListItem = web.Lists[list].GetItemById(listItem);
                    SPFieldUserValue userValue = new SPFieldUserValue(web, userId.ToString());
                    taskListItem["AssignedTo"] = userValue;
                    taskListItem.Update();
                }
                catch (Exception error)
                {
                    Literal output = new Literal();
                    output.Text = error.Message.ToString();
                    this.Controls.Add(output);
                }
                finally
                {
                    web.AllowUnsafeUpdates = false;
                }
            }
        }

        SPUtility.Redirect(requestUrl, SPRedirectFlags.Default, Context);
        base.CreateChildControls();
    }       
}
 

And last but not least.. here is the code of the hiddeninputsection.ascx. Please note that I didn’t took the time to find out what custom javascript needs to be in there and what not. I just copied this stuff from MS’s very own Administration Toolkit ;)

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<script type="text/javascript">
function    Visascii(ch)
{
    return (!(ch.charCodeAt(0) & 0x80));
}
function Visspace(ch)
{
    return (ch.charCodeAt(0) == 32) || ((9 <= ch.charCodeAt(0)) && (ch.charCodeAt(0) <= 13));
}
function stripWS(str)
{
    var b = 0;
    var e = str.length;
    while (str.charAt(b) && (Visascii(str.charAt(b)) && Visspace(str.charAt(b))))
        b++;
    while ((b < e) && (Visascii(str.charAt(e-1)) && Visspace(str.charAt(e-1))))
        e--;
    return ((b>=e)?"":str.substring(b, e ));
}
var L_NoFieldEmpty_TEXT = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,common_nofieldempty_TEXT%>' EncodeMethod='EcmaScriptStringLiteralEncode'/>";
function CheckForEmptyField(text_orig,field_name)
{
    var text = stripWS(text_orig);
    if (text.length == 0)
    {
        alert(StBuildParam(L_NoFieldEmpty_TEXT, field_name));
        return false;
    }
    return (true);
}
function CheckForEmptyFieldNoAlert(text_orig)
{
    var text = stripWS(text_orig);
    if (text.length == 0)
    {
        return false;
    }
    return (true);
}
var L_WrongEmailName_TEXT = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,common_wrongemailname_TEXT%>' EncodeMethod='EcmaScriptStringLiteralEncode'/>";
function CheckForAtSighInEmailName(text_orig,field_name)
{
    var text = stripWS(text_orig);
    if (!CheckForEmptyField(text_orig,field_name)) return false;
    var indexAt = 0;
    var countAt = 0;
    var countSpace = 0;
    var len = text.length;
    while(len--)
    {
        if (text.charAt(len) == '@')
        {
            indexAt = len;
            countAt++;
        }
        if (text.charAt(len) == ' ')
            countSpace ++;
    }
    if ((countAt == 0) ||
        (indexAt == 0) ||
        (indexAt == (text.length-1))
        )
    {
        alert(StBuildParam(L_WrongEmailName_TEXT, field_name));
        return false;
    }
    if (countSpace !=0 )
    {
        alert(L_TextWithoutSpaces1_TEXT + field_name);
        return false;
    }
    return (true);
}
    var checkBoxGroupName = "siteCheckboxes";
    function checkBoxClick ()
    {
        var checkBoxHidden = (document.getElementById("<%= HiddenSiteSelections.ClientID %>"));
        checkBoxHidden.value = "";
        var selectAllCheckBox = document.getElementById ("selectAllCheckBox");
        if (selectAllCheckBox != null)
            selectAllCheckBox.checked = false;
        var i;
        for (i = 0; i < theForm.length; i ++)
        {
            curGroup = theForm[i].group;
            if (checkBoxGroupName==curGroup && theForm[i].checked==true)
            checkBoxHidden.value += theForm[i].name + "#";
        }
    }
    function selectAllCheckBoxClick (selectAllCheckBox)
    {
        var newCheckBoxState = selectAllCheckBox.checked;
        for (i = 0; i < theForm.length; i ++)
        {
            curGroup = theForm[i].group;
            if (checkBoxGroupName==curGroup)
                theForm[i].checked = newCheckBoxState;
        }
        checkBoxClick ();
        selectAllCheckBox.checked = newCheckBoxState;
    }
    function _spBodyOnLoad()
    {
        var checkBoxHidden = (document.getElementById("<%= HiddenSiteSelections.ClientID %>"));
        checkBoxHidden.value = "";
    }
</script>
<input type="hidden" id="HiddenSiteSelections" runat="server" />

There you have it.. all of this is also available on our CodePlex site.. let me know what you think of it! ;)

Technorati Tags: ,

Comments

Oliver Wirkus

Hi Robin,

excellent work! Thank's for posting the code.

Regards,
Oliver
Robin MeureNo presence information on 16/11/2008 07:41

Matt Moore

Good job! it would have been great to do a bulk task edit and assignment.

-Matt
Robin MeureNo presence information on 16/11/2008 07:41

Onur BIYIK

Is there any way to apply custom menu and checkboxes on a dynamic list where users may edit their own views?

btw, great work, thanks.
System Account on 02/01/2009 04:24

Knox

Good morning. In an industrial society which confuses work and productivity, the necessity of producing has always been an enemy of the desire to create.
I am from Salvador and too poorly know English, tell me right I wrote the following sentence: "Home airline tickets cheaper than the cheapest."

Thanks :p. Knox.
System Account on 12/03/2009 12:08

Amira

Is there a way to customize the ECB of Document Library?
Cause I need to add submenu to ECB, but without accessing core.js directly.
System Account on 30/03/2009 04:49

julius

hi robin
do you think this code is beatable?
is there any chance to bypass it?or inject it?
System Account on 09/05/2009 11:07

Robin

Hi Julius,

what do you mean precisely? It's only a 'shortcut' to completing a regular task. It's still auditable who did what..
System Account on 10/05/2009 12:36
 

 Statistics

 
Views: 7654
Comments: 7
Tags:
Published:1646 Days Ago