Monday, May 23, 2011

SPPostIt - It's Almost Here!

So I have been working quite a bit on SPPostIt and there is a lot to show you in this post. If you saw the last post, you might wonder what I was thinking with my design of the form that I was using. My better half pointed out that though it was nice, it just was too much, and I reluctantly had to agree!

So, I revamped the idea and came up with what I think is a better option. A floating bar that you can drag to wherever you need. But there is a lot built into it! So let me show you a few shots of what it looks like after it is loaded.


So I think that this is much better and is really clean. The bar can be dragged from the postit icon on the left. The arrow icon will dropdown the tabbed view and notice that the icon changes to an up icon. It's the little things right?! You will also notice what it has in the bar. "You have 1 unread message." The messages are checked based on a timer and could be changed. Currently it is 15 seconds.

So the next views will give you more looks at what is coming.


 Click on a message header and it slides down to reveal the message body. The check mark will allow you to mark it as "read".


The view below is how to send a message. The people picker was fun to reproduce, but it works great! The rich text editor works well but is only supported in IE. I am looking into options for other browsers.


Clicking the send button yields the below notice.


Notice now that there are 3 unread messages. I have already expanded it out.


So I hope this gives you  a great look into SPPostIt and I hope to hear feedback from you!

Stay tuned for more!

Friday, May 20, 2011

SPPostIt - The Beginning

A short time ago, I asked a few folks their opinion on an idea that I discussed in this post. I received very welcomed feedback and set to work on what I hope will be a great tool that people will want to use. As you may guess by the post's title, I have currently opted to call my tool SPPostIt. It will start as a fairly simple messaging tool that would be used in SharePoint to send messages to other users on a site. The cool part is that all the functionality is held in a jQuery plugin. So the two requirements so far is that you have jQuery and SharePoint.

I will tell you that this is being created for many reasons but the main drivers for this currently are:
  • No HTML email option. All emails look icky .(technical term!)
  • Users have multiple email accounts for security reasons.
There are other reasons as well, but these 2 are probably the biggest. Once I got the idea however, I saw a lot of potential for future growth and use. I will discuss more of that later. I wanted to go over what I have created so far and let you get a peek at some screen shots of what it looks like as of now.

Currently in my environment, I have to use Visual Studio 2008 to create solutions and I also have to use VSEWSS. I unlike most, however, actually like this set as it was just easy for me to pick it up and use. So what I did was create an empty SharePoint solution and went from there. As I am putting the functions in a jQuery plugin, you would not be required to deploy a solution per se, but there is a css file, and some images. My solution also deploys two lists. One to hold the messages and one to hold message categories. Fairly simple solution and I hope to have it up on Codeplex soon.

Enough Talk Let's Have Some Screenshots!

When I activate the feature the jquery plugin is loaded via an ascx control into the masterpage. This is not the only way to do it, just how I did it. It adds an option to ECB menus and  to all the "Actions" menus for list/library views and looks like this:


After clicking this, it will open a popup dialog that looks like this:


Of course, you could change this, but why would you want to ;-)

This is just the beginning and I hope it is well received. Please feel free to provide feedback of any kind!

Friday, May 6, 2011

Sharepoint Messaging System Concept Phase

A SharePoint Messaging System

THE CONCEPT

     So what exactly would a SharePoint Messaging System be about? What do I mean and why do I thnk it is a good idea? I wanted to use this post to go over the concept and then go into more detail in subsequent posts. But first, I would like to present some background that led me down this path.

     Currently I work in an environment with WSS 3 only. We are possibly going to upgrade to SP 2007 or may skip that one and go straight to SP 2010. There is no time line for that either. I am asked to create Sharepoint solutions that require a lot of work and I normally do most development in Visual Studio 2008. I do not have SPD 2007 on my production machine though I do have it on my development machine. So I have some tools to develop with. I am allowed to write .Net code or any code needed to get the job done so there really is no limit in that regard. So that is that side.

     The next piece I wanted to to share is that our email is plain text only email and any html is stripped out. So unfortunately alerts just look rather ugly even if they are customized to look somewhat better. So there is the meat of our environment. Not the best, but at least we have SharePoint!

     So with that said, I have been working on several sites and I really try to make things easy whenever I can. I am working on 2 major projects right now and both clients want to have something better than just email alerts that look ugly or emails that do not give them much detail. I was showing each of them some stuff with a custom Edit Control Block and the things that I could do with it. They were excited about the possibilities and I was asked if I could have an option to send a message to the "Assigned To" user or the "Author" of an item from the ECB. "Of Course I Can!" was my reply.

     It was at that point as I had already written several event receivers and a timer job that send emails that I just felt I needed to have something extra. A light bulb turned on and I have not been able to turn it off yet! I thought that yes, I could email users for sure, but what if I could also use SharePoint and a site to build a real messaging center. Something that could tell a user who was on any page in SharePoint that they had something new to look at and they could see it with full HTML without it being ugly. That was when the thought stuck and that is why I am writing this.

     There is an endless amount of ways I could do this thought I have a few in mind that I am going to lay out in more detail soon. But basicaly here is a pseudo list of how this might work.

  • User decides he needs to message a coworker for whatever reason.
  • User clicks a link on a page or item in an ECB to message someone.
  • A popup (jQuery UI dialog maybe) opens to present the messaging form.
  • User fills it out and hits OK/SEND or whatever.
  • If the recipient is on a Sharepoint site they will get the notification.
  • Recipient opens the message.
     I know this is just a small part of what you could really do, but I think that this has a lot of possibilities. I think that I want to do this using jQuery and SPServices for most of the work. I could wrap it in a feature or something like that and it could be added to any site that wants to support it.

     Well, there it is in a proverbial nutshell. Is it a good idea, mediocre at best or what? Any thoughts are appreciated!

Thursday, May 5, 2011

Creating A Custom Edit Control Block Part 2

     So in the last post, I went over the basic means for getting a custom ECB up and running. In this post, I will give more detailed examples and how to use .Net code to really give your ECB some nice features! So moving on to a more detailed example. The following image shows a custom ECB created using javascript and .Net object model code:


As you can see, there is a lot of functionality in there. It is security trimmed by the item and determines what the current user can do. The Repeat options allow the author of the item to basically copy the commitment to another department/division/ branch in this case. So how do we do this you ask? Let's take a look at some code that makes this kind of thing happen. First the javascript code:

function Custom_AddListMenuItems(m, ctx) {
    if (test.indexOf("Lists/Commitments") > 0) {
 var request;
 var url = ctx.HttpRoot + "/CustomPages/ECB.aspx?ListID=" + ctx.listName + "&ItemID=" + currentItemID + "&DateTime=" + Date();
 request = new ActiveXObject("Microsoft.XMLHTTP");
      if (request) {
  request.open("GET", url, false);
  request.send();
      }
      if (request) {
  var commands = request.responseXML.getElementsByTagName("Command");
  var dvs, brs;
  var menuitema, menuitemb, menuitemc, menuitemd, menuiteme;
  var menuitemsd = new Array();
  var menuitemse = new Array();
  var menuitemsf = new Array();
  var menuitems = new Array(100, 100);
  var chka = 0;
  var chkb = 0;
  var chkc = 0;
  var chkd = 0;
  var chke = 0;
  var tp0, tp1, tp2, tp3, tp4, tp5;
  var ddbr;
   CAMOpt(m, "Print Item...", "_nnsy_printListItem()", "/_layouts/images/fax.gif");
   // Add separator
   CAMSep(m);
   for (var i = 0; i < commands.length; i++) {
       var cmdName = commands[i].getElementsByTagName("Name")[0].firstChild.nodeValue;
       var imageUrl = commands[i].getElementsByTagName("ImageUrl")[0].firstChild.nodeValue;
       var type = commands[i].getElementsByTagName("Type")[0].firstChild.nodeValue;
       switch (type) {
           case "Seperator":
               CAMSep(m);
               break;

           case "MenuItem":
               var js = commands[i].getElementsByTagName("Script")[0].firstChild.nodeValue;
               CAMOpt(m, cmdName, js, imageUrl);
               break;

           case "Concurrence":
               var js = commands[i].getElementsByTagName("Script")[0].firstChild.nodeValue;
               if (chkb == 0) {
                   menuitemb = CASubM(m, "Concurrences", "", "/_layouts/images/forward.gif");
                   menuitemsd[chkb] = CASubM(menuitemb, cmdName, imageUrl)
                   CAMOpt(menuitemsd[chkb], "View", "viewconcurrence('" + js + "', 'Concurrence', 'View')", imageUrl);
                   CAMOpt(menuitemsd[chkb], "Edit", "editconcurrence('" + js + "', 'Concurrence', 'Edit')", imageUrl);
                   CAMOpt(menuitemsd[chkb], "Concur", "editconcurrence('" + js + "', 'Concurrence', 'Concur')", imageUrl);
                   chkb += 1;
               }
               else {
                   menuitemsd[chkb] = CASubM(menuitemb, cmdName, imageUrl)
                   CAMOpt(menuitemsd[chkb], "View", "viewconcurrence('" + js + "', 'Concurrence', 'View')", imageUrl);
                   CAMOpt(menuitemsd[chkb], "Edit", "editconcurrence('" + js + "', 'Concurrence', 'Edit')", imageUrl);
                   CAMOpt(menuitemsd[chkb], "Concur", "editconcurrence('" + js + "', 'Concurrence', 'Concur')", imageUrl);
                   chkb += 1;
               }
               break;

           case "Extension":
               var js = commands[i].getElementsByTagName("Script")[0].firstChild.nodeValue;
               if (chkc == 0) {
                   menuitemc = CASubM(m, "Extensions", "", "/_layouts/images/forward.gif");
                   menuitemse[chkc] = CASubM(menuitemc, cmdName, imageUrl)
                   CAMOpt(menuitemse[chkc], "View", "viewconcurrence('" + js + "', 'Extension', 'View')", imageUrl);
                   CAMOpt(menuitemse[chkc], "Edit", "editconcurrence('" + js + "', 'Extension', 'Edit')", imageUrl);
                   CAMOpt(menuitemse[chkc], "Concur", "editconcurrence('" + js + "', 'Extension', 'Concur')", imageUrl);
                   chkc += 1;
               }
               else {
                   menuitemse[chkc] = CASubM(menuitemc, cmdName, imageUrl)
                   CAMOpt(menuitemse[chkc], "View", "viewconcurrence('" + js + "', 'Extension', 'View')", imageUrl);
                   CAMOpt(menuitemse[chkc], "Edit", "editconcurrence('" + js + "', 'Extension', 'Edit')", imageUrl);
                   CAMOpt(menuitemse[chkc], "Concur", "editconcurrence('" + js + "', 'Extension', 'Concur')", imageUrl);
                   chkc += 1;
               }
               break;

           case "Closeout":
               var js = commands[i].getElementsByTagName("Script")[0].firstChild.nodeValue;
               if (chkd == 0) {
                   menuitemd = CASubM(m, "Closeout", "", "/_layouts/images/forward.gif");
                   CAMOpt(menuitemd, "View", "viewconcurrence('" + js + "', 'Closeout', 'View')", imageUrl);
                   CAMOpt(menuitemd, "Edit", "editconcurrence('" + js + "', 'Closeout', 'Edit')", imageUrl);
                   CAMOpt(menuitemd, "Concur", "editconcurrence('" + js + "', 'Closeout', 'Concur')", imageUrl);
                   chkd += 1;
               }
               else {
                   CAMOpt(menuitemd, "View", "viewconcurrence('" + js + "', 'Closeout', 'View')", imageUrl);
                   CAMOpt(menuitemd, "Edit", "editconcurrence('" + js + "', 'Closeout', 'Edit')", imageUrl);
                   CAMOpt(menuitemd, "Concur", "editconcurrence('" + js + "', 'Closeout', 'Concur')", imageUrl);
                   chkd += 1;
               }
               break;


           case "CopyToDept":
               var xmldata1 = new ActiveXObject("MSXML.DomDocument");
               xmldata1.loadXML(request.responseText);
               var depts = xmldata1.selectNodes("ECB/Departments/Department");
               menuitems[99, 99] = CASubM(m, "Repeat To Department");
               for (var j = 0; j < depts.length; j++) {
                   tp0 = depts[j].childNodes[0].firstChild.nodeValue;
                   CAMOpt(menuitems[99, 99], tp0, "copyitem('" + cmdName + "', '" + tp0 + "', '" + null + "', '" + null + "')", "/_layouts/images/forward.gif");
               }
               break;

           case "CopyToDiv":
               var xmldata1 = new ActiveXObject("MSXML.DomDocument");
               xmldata1.loadXML(request.responseText);
               var depts = xmldata1.selectNodes("ECB/Departments/Department");
               menuitems[99, 99] = CASubM(m, "Repeat To Division");
               for (var j = 0; j < depts.length; j++) {
                   tp0 = depts[j].childNodes[0].firstChild.nodeValue;
                   menuitems[j, 0] = CASubM(menuitems[99, 99], tp0);
                   dvs = depts[j].childNodes[1].childNodes;
                   for (var k = 0; k < dvs.length; k++) {
                       tp1 = dvs[k].childNodes[0].firstChild.nodeValue;
                       CAMOpt(menuitems[j, 0], tp1, "copyitem('" + cmdName + "', '" + tp0 + "', '" + tp1 + "', '" + null + "')", "/_layouts/images/forward.gif");
                   }
               }
               break;


           case "CopyToBranch":
               var xmldata1 = new ActiveXObject("MSXML.DomDocument");
               xmldata1.loadXML(request.responseText);
               var depts = xmldata1.selectNodes("ECB/Departments/Department");
               menuitems[99, 99] = CASubM(m, "Repeat To Branch");
               for (var j = 0; j < depts.length; j++) {
                   tp0 = depts[j].childNodes[0].firstChild.nodeValue;
                   menuitems[j, 0] = CASubM(menuitems[99, 99], tp0);
                   dvs = depts[j].childNodes[1].childNodes;
                   for (var k = 0; k < dvs.length; k++) {
                       tp1 = dvs[k].childNodes[0].firstChild.nodeValue;
                       menuitems[j, k + 1] = CASubM(menuitems[j, 0], tp1);
                       brs = dvs[k].childNodes[1].childNodes;
                       for (var n = 0; n < brs.length; n++) {
                           tp2 = brs[n].childNodes[0].firstChild.nodeValue;
                           CAMOpt(menuitems[j, k + 1], tp2, "copyitem('" + cmdName + "', '" + tp0 + "', '" + tp1 + "', '" + tp2 + "')", "/_layouts/images/forward.gif");
                       }
                   }
               }
               break;
       }

   }
   if (commands.length > 0) CAMSep(m);
   return true;
   //return false; // for testing
  }
 }
 else {
  return false;
 }  
}

So as you can see here there is a lot going on. The first step is to setup an ajax request to the .aspx page that has the object model code. This could be done using inline script or code behind which is the better choice. This page is actually in a document library that users would have read access to. So the request object sends requests to the page and sends the list and item id's in the querystring. This page has several responsibilities. It must return valid xml to be used by the javascript code. It will also determine the users ability based on what the requirements are. In the code below, there is a lot of stuff to go through but I will try to tie it all together in the end.

private void Page_Load(Object sender, EventArgs e)
{
    SPWeb web = SPContext.Current.Web;
    SPUser curuser;
    String author = String.Empty;
    String cuser = String.Empty;
    SPListItem item = null;
    String qry;
    int itemID = 1;
    curuser = SPContext.Current.Web.CurrentUser;
    SPList emps = web.Lists["User Information List"];
    SPList status = web.Lists["Status"];
    SPList ddbr = web.Lists["DDBR"];
    SPList prefs = web.Lists["Preferences"];
    SPListItem pref = prefs.Items.GetItemById(1);
    SPListItem user = emps.Items.GetItemById(Convert.ToInt32(curuser.ID));
    SPList list = web.Lists["Commitments"];
    SPList concs = web.Lists["Concurrences"];
    Boolean goon = true;
    try
    {
        itemID = int.Parse(this.Page.Request.QueryString["ItemID"]);
        item = list.Items.GetItemById(itemID);
        author = item["Author"].ToString();
        int loc = author.IndexOf("#");
        author = author.Remove(0, loc + 1);
        cuser = curuser.Name.ToString();
    }
    catch (Exception ex){ author = ex.Message.ToString() + ", " + ex.Source.ToString(); }
        
    String xhtml = "";

    String asgdiv = String.Empty;
    if (item["AssignedDivision"] != null) { asgdiv = item["AssignedDivision"].ToString(); }
    String asgdept;
    asgdept = item["AssignedDepartment"].ToString();
        
    this.Page.Response.ClearHeaders();
    this.Page.Response.ClearContent();
    this.Page.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    this.Page.Response.AddHeader("Content-type", "text/xml");

    string cmdPatterna = @"";
    
    xhtml += "";
    xhtml += "";    
    String udept = "";
    String udiv = "";
    String ubr = "";
    SPQuery dpqry;
    SPQuery dvqry;

    xhtml += "";
    foreach (SPListItem x in ddbr.Items)
    {
        if (x["Department"].ToString() != udept)
        {
            udept = x["Department"].ToString();
            xhtml += string.Format("{0}", udept);
            dpqry = new SPQuery();
            dpqry.Query = "" + udept + "";
            foreach (SPListItem y in ddbr.GetItems(dpqry))
            {
                if (y["Division"].ToString() != udiv)
                {
                    udiv = y["Division"].ToString();
                    xhtml += string.Format("{0}", udiv);
                    dvqry = new SPQuery();
                    dvqry.Query = "" + udiv + "";
                    foreach (SPListItem z in ddbr.GetItems(dvqry))
                    {
                        if (z["Branch"].ToString() != ubr)
                        {
                            ubr = z["Branch"].ToString();
                            xhtml += string.Format("{0}", ubr);
                        }
                    }
                    xhtml += "";
                    xhtml += "";
                }
            }
            xhtml += "";
            xhtml += "";
        }
    }
    xhtml += "";
    xhtml += "";

    if (list.DoesUserHavePermissions(SPBasePermissions.CreateAlerts)) { xhtml += string.Format(cmdPatterna, "MenuItem", "Alert Me", Page.ResolveUrl("~/_layouts/images/outl.gif"), "alertme('" + itemID + "', '{" + list.ID.ToString() + "}')"); }
    if (list.DoesUserHavePermissions(SPBasePermissions.ViewListItems)) { xhtml += string.Format(cmdPatterna, "MenuItem", "View Commitment", Page.ResolveUrl("~/_layouts/images/openfold.gif"), "viewitem('" + itemID + "', '" + item["Priority"].ToString() + "')"); }
    if (list.DoesUserHavePermissions(SPBasePermissions.ViewVersions)) { xhtml += string.Format(cmdPatterna, "MenuItem", "Version History", Page.ResolveUrl("~/_layouts/images/versions.gif"), "versions('" + itemID + "', '{" + list.ID.ToString() + "}')"); }
    xhtml += string.Format(cmdPatterna, "Seperator", null, null, null);
        
    try
    {
        if (web.IsCurrentUserMemberOfGroup(web.Groups["CTS Content Manager"].ID) == true || web.IsCurrentUserMemberOfGroup(web.Groups["WebAdmin"].ID) == true || web.IsCurrentUserMemberOfGroup(web.Groups["CTS Owners"].ID) == true || author == curuser.Name.ToString()) //  || author == curuser.Name.ToString()
        {
            xhtml += string.Format(cmdPatterna, "CopyToDept", itemID, null, null);
            xhtml += string.Format(cmdPatterna, "CopyToDiv", itemID, null, null);
            xhtml += string.Format(cmdPatterna, "CopyToBranch", itemID, null, null);
            xhtml += string.Format(cmdPatterna, "Seperator", null, null, null);
            xhtml += string.Format(cmdPatterna, "MenuItem", "Create Concurrence Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "concurrence('" + itemID + "')");
            xhtml += string.Format(cmdPatterna, "MenuItem", "Create Complete Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "createcloseout('" + itemID + "')");
            xhtml += string.Format(cmdPatterna, "MenuItem", "Revise ECD", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "revecd('" + itemID + "')");
            xhtml += string.Format(cmdPatterna, "Seperator", null, null, null);
            xhtml += string.Format(cmdPatterna, "MenuItem", "Edit Commitment", Page.ResolveUrl("~/_layouts/images/edit.gif"), "edititem('" + itemID + "', '" + item["Priority"].ToString() + "')");
            xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')"); 
            if (list.DoesUserHavePermissions(SPBasePermissions.DeleteListItems)) { xhtml += string.Format(cmdPatterna, "MenuItem", "Delete Commitment", Page.ResolveUrl("~/_layouts/images/delete.gif"), "deleteitem('" + itemID + "')"); }
            if (list.DoesUserHavePermissions(SPBasePermissions.ManagePermissions)) { xhtml += string.Format(cmdPatterna, "MenuItem", "Manage Permissions", Page.ResolveUrl("~/_layouts/images/managePerm.gif"), "permissions('" + itemID + "', '{" + list.ID.ToString() + "}')"); }
        }
        else
        {
            if (web.IsCurrentUserMemberOfGroup(web.Groups["CTS Power Users"].ID) == true || web.IsCurrentUserMemberOfGroup(web.Groups["CTS Users"].ID) == true)
            {
                if (item["Priority"].ToString().Contains("Interdepartmental"))  // Interdepartmental commitment
                {
                    if (item["AssignedDepartment"].ToString().Equals(user["CTSDepartment"].ToString()))
                    {
                        xhtml += string.Format(cmdPatterna, "MenuItem", "Create Concurrence Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "concurrence('" + itemID + "')");
                        xhtml += string.Format(cmdPatterna, "MenuItem", "Revise ECD", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "revecd('" + itemID + "')"); 
                        xhtml += string.Format(cmdPatterna, "MenuItem", "Edit Commitment", Page.ResolveUrl("~/_layouts/images/edit.gif"), "edititem('" + itemID + "', '" + item["Priority"].ToString() + "')");
                        if (list.DoesUserHavePermissions(SPBasePermissions.DeleteListItems))
                        {
                            xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')");
                            xhtml += string.Format(cmdPatterna, "MenuItem", "Delete Commitment", Page.ResolveUrl("~/_layouts/images/delete.gif"), "deleteitem('" + itemID + "')");
                        }
                        else
                        {
                            if (pref["AllowCloseout"].ToString().Equals("Yes"))
                            {
                                if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')"); }
                            }
                            else
                            {
                                if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Create Complete Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "createcloseout('" + itemID + "')"); }
                            }
                        }
                    }
                }
                else  // item is critical or commitment
                {
                    xhtml += string.Format(cmdPatterna, "MenuItem", "Create Concurrence Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "concurrence('" + itemID + "')");
                    xhtml += string.Format(cmdPatterna, "MenuItem", "Revise ECD", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "revecd('" + itemID + "')");
                    xhtml += string.Format(cmdPatterna, "MenuItem", "Edit Commitment", Page.ResolveUrl("~/_layouts/images/edit.gif"), "edititem('" + itemID + "', '" + item["Priority"].ToString() + "')");
                    if (list.DoesUserHavePermissions(SPBasePermissions.DeleteListItems))
                    {
                        xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')");
                        xhtml += string.Format(cmdPatterna, "MenuItem", "Delete Commitment", Page.ResolveUrl("~/_layouts/images/delete.gif"), "deleteitem('" + itemID + "')");
                    }
                    else
                    {
                        if (pref["AllowCloseout"].ToString().Equals("Yes"))
                        {
                            if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')"); }
                        }
                        else
                        {
                            if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Create Complete Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "createcloseout('" + itemID + "')"); }
                        }
                    }
                }
            }
            else
            {
                if (web.IsCurrentUserMemberOfGroup(web.Groups["CTS Dept Power Users"].ID) == true || web.IsCurrentUserMemberOfGroup(web.Groups["CTS Dept Users"].ID) == true)
                {
                    if (item["Priority"].ToString().Contains("Interdepartmental"))  // Interdepartmental commitment
                    {
                        if (item["AssignedDepartment"].ToString().Equals(user["CTSDepartment"].ToString()))
                        {
                            xhtml += string.Format(cmdPatterna, "MenuItem", "Create Concurrence Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "concurrence('" + itemID + "')");
                            xhtml += string.Format(cmdPatterna, "MenuItem", "Revise ECD", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "revecd('" + itemID + "')");
                            xhtml += string.Format(cmdPatterna, "MenuItem", "Edit Commitment", Page.ResolveUrl("~/_layouts/images/edit.gif"), "edititem('" + itemID + "', '" + item["Priority"].ToString() + "')");
                            if (pref["AllowCloseout"].ToString().Equals("Yes"))
                            {
                                if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Complete Commitment", Page.ResolveUrl("~/_layouts/images/star.gif"), "closeitem('" + itemID + "')"); }
                            }
                            else
                            {
                                if (item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Create Complete Request", Page.ResolveUrl("~/_layouts/images/icspgen.gif"), "createcloseout('" + itemID + "')"); }
                            }
                            if (list.DoesUserHavePermissions(SPBasePermissions.DeleteListItems) && item["Status"].ToString().Equals("In Progress")) { xhtml += string.Format(cmdPatterna, "MenuItem", "Delete Commitment", Page.ResolveUrl("~/_layouts/images/delete.gif"), "deleteitem('" + itemID + "')"); }
                        }
                        else
                        {
                            // not in assigned dept!
                        }
                    }
                    else  //critical or commitment
                    {
                        // Already have the print and view options at the top
                    }
                }
            }
        }            
    }
        
    catch { }
        
    xhtml += "";
    this.Page.Response.Write(xhtml);
    this.Page.Response.End();          
}

There is one thing I want to point out here in that due to the code formatting, the commandpattern line is not formatted correctly due to the </Script> tag. If you pasted this code into Visual Studio for example, it will show an error unless you escape the slash with a \

Now, as you can see, there is a lot of stuff here. I wanted to get it out there for you and I will go over it more in the next post. Sorry, but there is a lot here and I really want to get into it! Until then!

Tuesday, May 3, 2011

Creating A Custom Edit Control Block Part 1



What is an Edit Control Block?
(And why would I want a custom one?)

The image below is an example of an Edit Control Block or ECB as it is normally called:

This is the ECB of a tasks list. There are many things that can be said based on just this. The user has edit and delete permissions on this item, and can even manage the items permissions. You will find that out of the box, the ECB is different depending on the type of list or library you are viewing and the permissions of the user. The cool thing here though, is that you can completly customize this depending on your needs. There are a few methods that you can use to do this and I will outline them briefly.


  1. javascript/jQuery - This is a must! You must write javascript code (ack the "c" word!). The functions that allow you to do this are not too difficult and we will go through them next. You can build a great custom ECB with just this method.
  2. .Net (c#) code. (Please do not run away screaming in terror! It is not that bad!). Adding this to the mix means we can go from a great ECB to an outstanding one!
I would have added ajax to that list, but that is just the bridge that we will use to combine the two methods together. So without further ado, let me introduce you to the code that will get you going!

There are two functions contained in core.js that render the ECB for an item depending on what type it is.
  • AddListMenuItems
  • AddDocLibMenuItems
Both of these functions look for a “custom” version allowing you to write your own items into the ECB.
  • Custom_AddListMenuItems
  • Custom_AddDocLibMenuItems
The functions are javascript functions that support 3 core functions for creating menus:
  • CAMOpt – This creates a menu item
  • CASubM – This creates a submenu item
  • CAMSep – This creates a separator
Combining these functions will allow you to create many different ECB options.


As you can see, you can do quite a bit with the ECB. Some things to remember:

JAVASCRIPT IS YOUR FRIEND!

The functions are all working with the javascript provided by the core.js file and the list view. This means you have access to:

  • The id of the item you are on.(currentItemID)
  • The list of the item you are on.(ctx.listName)


Javascript can then be used to make changes to items or get more information from them by utilizing the SharePoint web services.

JAVASCRIPT IS YOUR ENEMY!

Because the ECB works with javascript there is an important fact that you must be aware of.
You must decide if you want to check for permissions!
There is no javascript solution for item-level security!
You can use the webservices to determine if a user is in a certain group but you can not check the item directly.
To directly check the item you must use custom .Net code in conjunction with the javascript code.

Here is an example:
function Custom_AddListMenuItems(m, ctx) {
CAMOpt(m, "Print Item...", "_printListItem('" + currentItemID + "')", "/_layouts/images/fax.gif");
     return false; 
}

This just adds a Print Item option to the dropdown and will run the _printListItem function when clicked! The return false line tells the calling function from the core.js file to render the rest of the ECB after your code. If you returned true, this would only draw your code!

As I stated earlier, you will only have so much control at the javascript level. You can use .net code to do more, but I will save that for a future post.

Stay Tuned!
 Some time ago I wrote a part 1 post for what I thought would be a several part series on creating a custom ECB. However, I got sidetracked and have decided that it was time to revisit it again. With that said, I will be copying some items from the old post into this one.

    In some ways, you might call me a lazy user when it comes to having to click through multiple forms or pages to perform certain actions. In other ways, you just might say that I did this because it is cool and makes things easier to the end user. I prefer you say the latter, but I will leave that up to you! I say we get to what we are here to discuss!