2012 - Development Simply Put

A blog simplifies main concepts in IT development and provides tips, hints, advices and some re-usable code. "If you can't explain it simply, you don't understand it well enough" -Albert Einstein

  • Development Simply Put

    If you can't explain it simply, you don't understand it well enough.

    Read More
  • Integrant

    Based in the U.S. with offshore development centers in Jordan and Egypt, Integrant builds quality custom software since 1992. Our staff becomes an extension of your team to eliminate the risks of software development outsourcing and our processes...

    Read More
  • ITWorx

    ITWorx is a global software professional services organization. Headquartered in Egypt, the company offers Portals, Business Intelligence, Enterprise Application Integration and Application Development Outsourcing services to Global 2000 companies.

    Read More
  • Information Technology Institute

    ITI is a leading national institute established in 1993 by the Information and Decision Support Centre.

    Read More

2012-12-31

How To Change Sharepoint PageLayout Icon To Custom Image

In Sharepoint, when you create a new article page, you have to choose a page layout for this article to control the way the article page will appear. So, to differentiate between different page layouts from UI, each page layout has its own title and an image. Thus, once you click on any page layout, its corresponding image appears.

This image should somehow reflect the basic structure of the layout so that you can differentiate between all of the layouts based on what you see in the image. So, if the page layout consists of three columns, the image should reflect this. Also, if the page layout consists of two columns, the image should reflect this and so on.

There are two places in system UI where you can see the page layout image.

When creating a new article page and selecting its page layout

When changing the page layout of an existing article page

So, when you work on your custom page layouts, the OOTB page layout images may not be suitable for your layouts. So, you may need to assign your custom descriptive images to these page layouts to maintain the same concept.

When I first decided to do this while working on my project, I thought that I will just need to set the "PublishingPreviewImage" property of my page layouts in the "Elements.xml" file. But, when I tried to do it, I found that the images of my custom layouts are still the default ones.

After some searching, I found out that some of the page layouts properties are not updated once they are set and deployed by upgrading the solution. The only way to update these images is to retract then deploy your solution or to do it using some code executed at the feature activation.

Sure I decided to take the second approach. So, now I need to write some code to be executed when the feature -provisioning my custom page layouts- is activated to set the image of my page layouts explicitly by code.

The "Elements.xml" file looks as follows;
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

So, now in the feature activation, I can set the "PublishingPreviewImage" property of each of these two page layouts to the path of my custom images. This is easy, but, I thought I can achieve the same result without depending on some hard-coded values in my code, instead, I can depend on the values already set in the "Elements.xml" file.

So, to do this, I used the approach I already explained before in a previous post. It is the post called "How Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener". If you didn't read this post, please try to read it before you move to the next step.

So, now the code will be as follows;
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using DevelopmentSimplyPut.CommonUtilities.Helpers;

namespace DevelopmentSimplyPut.News.Features.pagelayouts
{
    [Guid("e882ad84-444a-421a-961c-bb945556c3f8")]
    public class pagelayoutsEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            PageLayoutsHelper.UpdatePageLayoutsPublishingPreviewImage(properties);
        }
    }
}

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Globalization;
using System.Xml;
using Microsoft.SharePoint.Publishing;
using System.Collections.ObjectModel;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class PageLayoutsHelper
    {
  public static void UpdatePageLayoutsPublishingPreviewImage(SPFeatureReceiverProperties properties)
  {
   SPSite site = null;

   if (properties.Feature.Parent is SPSite)
   {
    site = properties.Feature.Parent as SPSite;
   }
   else
   {
    site = (properties.Feature.Parent as SPWeb).Site;
   }

   if(null != site)
   {
    PublishingSite publishingSite = new PublishingSite(site);
    PageLayoutCollection layouts = publishingSite.PageLayouts;
    List<ModuleInfo> moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
    foreach (PageLayout layout in layouts)
    {
     string layoutIdentifier = layout.ServerRelativeUrl;
     ModuleInfo moduleInfo = moduleInfoCollection.DefaultIfEmpty(null).FirstOrDefault(module => module.Url == "_catalogs/masterpage");
     if (null != moduleInfo)
     {
      ModuleFile fileInfo = moduleInfo.Files.DefaultIfEmpty(null).FirstOrDefault(file => layoutIdentifier.Contains(file.Url));
      if (null != fileInfo)
      {
       string imagePath = string.Empty;

       if (fileInfo.Properties["PublishingPreviewImage"].Contains(","))
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Split(',')[0].Trim().ToUpperInvariant();  
       }
       else
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Trim().ToUpperInvariant();
       }

       if (imagePath.Contains("~SITECOLLECTION"))
       {
        string siteCollectionPath = site.ServerRelativeUrl.ToUpperInvariant();
        if (!siteCollectionPath.EndsWith("/"))
        {
         siteCollectionPath += "/";
        }
        imagePath = imagePath.Replace("~SITECOLLECTION/", siteCollectionPath).Replace("~SITECOLLECTION", siteCollectionPath);
       }

       if (!string.IsNullOrEmpty(imagePath))
       {
        SPFile layoutFile = layout.ListItem.File;
        SPFileHelper.PrepareFileForChanges(layoutFile);
        layout.PreviewImageUrl = imagePath;
        layout.Update();
        SPFileHelper.FinalizeFile(layoutFile);
       }
      }
     }
    }
   }
  }
 }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class SPFileHelper
    {
        public static void PrepareFileForChanges(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.ForceCheckout
                        : file.Item.ParentList.ForceCheckout;

                if (checkOutEnabled)
                {
                    if (file.CheckOutType != SPFile.SPCheckOutType.None)
                    {
                        file.UndoCheckOut();
                    }
                    file.CheckOut();
                }
            }
        }
        public static void FinalizeFile(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                        file.Item == null
                            ? file.ParentFolder.Item.ParentList.ForceCheckout
                            : file.Item.ParentList.ForceCheckout;

                bool needsApproval =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.EnableModeration
                        : file.Item.ParentList.EnableModeration;

                if (checkOutEnabled)
                {
                    file.CheckIn(string.Empty, SPCheckinType.MajorCheckIn);
                }

                if (needsApproval)
                {
                    file.Approve(string.Empty);
                }

                file.Update();
            }
        }
    }
}

So, now every time the feature is activated, the page layouts images will be updated with the ones set in the "Elements.xml".


That's it. Hope this will help you someday.
Good Luck.


 

2012-12-28

How To Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener

Sometimes you need to run some code at the moment of certain feature activation. This is for sure doable by creating an event receiver on the desired feature and then overriding the code of the "FeatureActivated" method. This topic is not the main topic here so I will assume that you already know how to do this.

Ok, but what I will be talking about in this post is how to use the module "Elements.xml" file to get live or real-time data to be used inside your "FeatureActivated" code. Let me explain first what I really mean here.

Sometimes in your "FeatureActivated" code, you need to use some info you already provided in the module "Elements.xml" file. This info could be some files names, some properties or some URLs. For sure the code you write in the "FeatureActivated" code is somehow case specific and you may have no problem to duplicate this type of info between the "Elements.xml" and the "FeatureActivated" code, I know that. But, this introduces an new problem when you apply some changes on the "Elements.xml" and you forget to apply the same changes on the "FeatureActivated" code. This discrepancy in info will for sure cause you problems.

So, the best approach is to centralize your info in one place for you to be more easy and stable to apply further changes and I believe that the best place to use is the "Elements.xml" file cause the main concept behind this file is to make configurations more easy and manageable, so let's keep it that way.

Actually, it is more than that. By centralizing your info in the "Elements.xml" file, you provide system admins with the ability to apply some changes on these deployed "Elements.xml" files to reflect some changes on the live system and this will not need applying any changes on code cause your code uses the "Elements.xml" file as its source of info.

I think now we know what we need to achieve and this is the point where we go deep into some code.

Now, we need some code to access the "Elements.xml" file of certain feature inside the "FeatureActivated" code of this feature to get some info about the modules and their files to be able to use this info in the rest of the "FeatureActivated" code.

Before jumping into the code for this part, let's refresh our memory and have a look onto a sample "Elements.xml" file.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

This is an "Elements.xml" file for a module having two files. These files are page layouts and each of them has some info like "title", "content type" and "publishing preview image". The files themselves have some info like "path" and "url". Also, the module has info like "name" and "url".

So, now let's see the code for opening this xml file and extracting these info to be used.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace DevelopmentSimplyPut.Helpers
{
    public static class FeaturesHelper
    {
        public static List GetModuleFilesInfo(SPFeatureReceiverProperties instance)
        {
            List modules =
                    (from SPElementDefinition element in instance.Feature.Definition.GetElementDefinitions(CultureInfo.CurrentCulture)
                     where element.ElementType == "Module"
                     let xmlns = XNamespace.Get(element.XmlDefinition.NamespaceURI)
                     let module = XElement.Parse(element.XmlDefinition.OuterXml)
                     select new ModuleInfo
                     {
                         Url = module.Attribute("Url").Value,
                         Path = Path.Combine(element.FeatureDefinition.RootDirectory, module.Attribute("Url").Value),
                         Files = (from file in module.Elements(xmlns.GetName("File"))
                                  select new ModuleFile
                                  {
                                      Url = file.Attribute("Url").Value,
                                      Properties = (from property in file.Elements(xmlns.GetName("Property"))
                                                    select property).ToDictionary(
                                                          n => n.Attribute("Name").Value,
                                                          v => v.Attribute("Value").Value)
                                  }).ToList()
                     }).ToList();

            return modules;
        }
    }
    public class ModuleFile
    {
        public string Url { set; get; }
        public Dictionary Properties { set; get; }
    }
    public class ModuleInfo
    {
        public string Url { set; get; }
        public string Path { set; get; }
        public List Files { set; get; }
    }
}

So, now in the "FaeatureActivated" code;
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
 List moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
 
 /*
 foreach(ModuleInfo moduleInfo in moduleInfoCollection)
 {
  //moduleInfo.Url
  //moduleInfo.Path
  foreach(ModuleFile fileInfo in moduleInfo.Files)
  {
   //fileInfo.Url
   foreach(KeyValuePair property in fileInfo.Properties)
   {
    //property.Key
    //property.Value
   }
  }
 }
 */
}

You can use the info you get about the modules and files to do what you really want. For sure you may need to use some hard-coded files properties keys but at least these keys should be fixed in the first place and they are not subject to frequent changes (or any changes maybe). But, you have the advantage of dynamic files properties values and this is the most important thing. Now, when you apply changes on these values in the "Elements.xml" file you don't have to track these values in the code to apply matching changes.

Some may think that this is too much code to run for the sake of this purpose and that this approach will affect the overall performance. May be they are right but let's face it, this code will run only once every time the feature is activated and this doesn't happen frequently. Actually, if the system admin needs to activate and deactivate your feature frequently, you may need to reconsider the design.


That's it, hope you find this post helpful.
Good Luck.


 

2012-12-26

How To Make Sure jQuery Is Loaded And Only Once

While working with Sharepoint you will notice that adding jQuery to the main masterpage is not a good idea cause sometimes and with some jQuery versions problems happen just after adding jQuery even without using it. May be some of you encountered this problem or not but at least it happened to me and some other colleagues.

So, the first lesson here is, don't add jQuery on every page except when you really need it.

Also, when working with webparts, you may be using jQuery on more than one webpart, so you need to make sure that jQuery is added to whatever page including one of these webparts. Any page may include one or multiple of these webparts, so you can't depend on making only one of them responsible for adding jQuery to the page cause in case of absence of this webpart any jQuery code will fail.

Another thing, suppose that a page includes two webparts which require jQuery to be added to the page. One is loaded at the header of the page, lets call it "A", while the other is loaded at the footer of the same page, lets call it "B". If "A" extends jQuery object with some custom code, then "B" added jQuery again to the page, this will override the custom code introduced by "A" and jQuery will be reverted to its original source code. I am sure this is not what you really need.

So, the second lesson here, add jQuery only once to the page.

Another thing, you can add jQuery to the page using some JavaScript code which will add a "script" tag to the DOM, but you can't start using jQuery just after running this JavaScript code because jQuery library itself is not yet fully loaded.

So, the third lesson here, before firing jQuery code, you need to make sure that jQuery is fully loaded and ready to perform.


So, now before going through the practical approach to overcome all of these issues, lets summarize what we really need to achieve here.

We need to:
  1. Add jQuery to pages only when needed
  2. In case of adding jQuery to a page, make sure to add it only once
  3. Before firing any jQuery code, make sure jQuery is fully loaded


After searching for a while, I found some tips and came up with the code below.

This is a method called "RunWithJQuerySupport" which you can add to a common utilities js file. This method takes care of all the three points above.
function RunWithJQuerySupport(success) {
    if (typeof jQuery == 'undefined') {
        function getScript(success1) {
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            script.src = "_layouts/1033/JS/ITWorx.UTCNow/jquery-1.8.1.min.js";
            done = false;
            script.onload = script.onreadystatechange = function () {
                if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
                    done = true;
                    if (typeof (success1) != 'undefined' && success1 != null) { success1(); }
                    script.onload = script.onreadystatechange = null;
                };
            };
            head.appendChild(script);
        };

        getScript(function () {
            if (typeof jQuery == 'undefined') {
                // Super failsafe - still somehow failed...
            } else {
                //jQuery.noConflict();
                if (typeof (success) != 'undefined' && success != null) { success(); }
            }
        });
    } else {
        if (typeof (success) != 'undefined' && success != null) { success(); }
    };
}

So, suppose that somewhere on a page or a webpart you need to call a method called "StartAjaxCall"
and you want to make sure that your call will follow the three points above. To achieve this, you can use the code below.
function StartAjaxCall(){
 alert("Ajax Call Started");
}

RunWithJQuerySupport(StartAjaxCall);

This will make sure that the three points above are followed and finally fire your method “StartAjaxCall”.

If you need to only add jQuery to the page without calling any other method, you can call "RunWithJQuerySupport" without passing any parameters as follows.
RunWithJQuerySupport();

Please note that in the "RunWithJQuerySupport" method, the url for the jQuery.js file is hard-coded on the 6th line. You can update it or even add it as a parameter to the “RunWithJQuerySupport” method to be passed with each call.


That's all, wish you find this helpful.


2012-12-23

Using "EditModePanel" To Show/Hide Webparts In Edit/Display Mode in Sharepoint 2007/2010

While working on Sharepoint pagelayouts I needed to control whether an element will appear on edit/display mode. I specifically needed a webpart zone to appear in the edit mode but not appear in the display mode.

So, after searching, I found an OOTB control called "EditModePanel" which has an attribute to decide the mode in which the control children will be rendered. So, if this attribute -which is called "PageDisplayMode" by the way- is set to "Display", then the control contents will be rendered in the display mode of the page. While, if it is set to "Edit", then the control contents will be rendered in the edit mode of the page.

Then, I thought that I found what I need. But, after using this control, I figured out a problem with it. In the days of Sharepoint 2007, this control would have done exactly what I needed, but the case is different with Sharepoint 2010.

In Sharepoint 2010, Microsoft decided to introduce a security change to this control which I really can't understand till now. Microsoft added a part to this control which checks for the privilege of the logged in user. If this user has edit privilege on the page, then everything is ok and the control works as supposed to. But, if the user doesn't have edit privilege on the page, the control will never render its children whatever its display mode is set to.

So, to imagine the whole thing;

In Sharepoint 2007
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display">
 <!-- content here will be rendered in page display mode -->
</PublishingWebControls:EditModePanel>

<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit">
 <!-- content here will be rendered in page edit mode -->
</PublishingWebControls:EditModePanel>

In Sharepoint 2010
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display">
 <!-- if user has edit privilege, content here will be rendered in page display mode -->
</PublishingWebControls:EditModePanel>

<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit">
 <!-- if user has edit privilege, content here will be rendered in page edit mode -->
</PublishingWebControls:EditModePanel>

So, this didn't help me achieve what I needed for my case. So, after searching, I found some other ways to do it using a combination between the "EditModePanel" and "AuthoringContainer". The "AuthoringContainer" is a panel which also has the two modes but I didn't like this solution cause it has its limitations beside it is somehow complex.

This lead me to another idea, let's reflect the "EditModePanel" to find the newly introduced security change then inherit from the control and override this method. So, I reflected the control and found the line which causes the problem. The method I need to override is called "CalculateShouldRender" and the line causing me troubles is like the following
if (!ConsoleUtilities.CheckPermissions(SPBasePermissions.EditListItems))
{
 this.shouldRender = false;
}

This is good, I can now override this method. But, I noticed that the control is sealed which means I can't inherit from it, so, I had to make one extra step. I copied the same code of the control as it is except for the "CalculateShouldRender" method, I removed the security check.

This was the hard part. I started using my new control and used it to wrap the webpart zone which I wanted to display only into the edit mode. Then I started testing my work, I opened the page into edit mode, found the zone, added a webpart to the zone, crossed my fingers and switched to display mode. What?!!!!

I found that the zone itself is not displayed into the display mode, but, the webpart -which was located into the zone- is moved to another zone on the page and it is displayed!!!!!

So, after some searching, I figured out that Sharepoint is somehow sensitive against webparts that are found on the page but will not be displayed. In such occasion, Sharepoint decides to move this webpart to the nearest visible zone. Why meeeeeeeeee.......

So, I decided to take another approach, instead of wraping the zone inside my control, I will leave the zone as it is and I will write some css to hide my webpart and locate this css inside the control while setting the control to display mode. So, now, in the display mode, the css will be rendered and the webpart will disappear. For sure I know that the webpart actually exists on the page and its code will be running but in my case this is enough.


Wish you find this helpful.
Bye.


 

2012-12-21

Sharepoint "ListItemPropertyField" WebControl To Render Any Subproperty Of ListItem Field

I was working on a Sharepoint 2010 project and I had a task to modify a custom page layout to include the e-mail of the article owner. I found that this article owner field is a custom field in the pages library and its type is "Person or Group".

So, I started to trace this field to find how I can get the email. I found that I can use an OOTB Sharepoint webcontrol called "BaseFieldControl" which has a property called "FieldName". When this property is set to the name of the field in a Sharepoint listitem, it fires its ToString() to get a string value for this field to be rendered.

But, this couldn't help me cause the email property I need is a subproperty of the listitem field. So, knowing that the field name is "ContentOwner", then to get the user email I will need to get "ContentOwner.User.Email". Actually, I will need to get "ContentOwner[0].User.Email" cause ContentOwner is a collection.

So, I searched for something OOTB to provide such capability but I couldn't and that's why I decided to make my own WebControl. This control should provide some powerful capabilities to be an asset to keep and reuse whenever needed.

So, I decided to prepare this control to have the following features:
  1. Get subproperty of a listitem field
  2. This field may be a collection, so need the ability to provide an index, if not provided assume zero (ie.: ContentOwner[1])
  3. Get multilevel subproperty of the field which could be represented by a properties pipeline (ie.: User.Email of ContentOwner[0])
  4. Be able to provide indexes for subproperties if they are collections or assume zero if indexes not provided (ie.: SubPropertyA[1].SubPropertyB[0].SubPropertyC[3] of Property)
  5. Be able to provide a type converter for the final subproperty in case of required custom conversion or manipulation code (ie.: like converting bool from true/false to yes/no or good/bad ......)
  6. Be able to provide a pattern to format the final output

So, I gave it a thought and then went deep into code -which I really enjoyed by the way :)- and I came out with the code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Reflection;
using System.Collections;
using System.Web.UI;
using System.ComponentModel;
using Microsoft.SharePoint.WebControls;

[assembly: TagPrefix("DevelopmentSimplyPut.Sharepoint.WebControls", "DevelopmentSimplyPutControls")]
namespace DevelopmentSimplyPut.Sharepoint.WebControls
{
    [ParseChildren(ChildrenAsProperties = true)]
    [PersistChildren(false)]
    [ToolboxData("<{0}:ListItemPropertyField runat=\"server\"></{0}:ListItemPropertyField>")]
    public class ListItemPropertyField : BaseFieldControl, INamingContainer
    {
        public string Property
        {
            get;
            set;
        }

        public string RenderTemplate
        {
            get;
            set;
        }

        public string ItemIndex
        {
            get;
            set;
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public TypeConverter PropertyTypeConverter
        {
            get;
            set;
        }

        public override void UpdateFieldValueInItem()
        {

        }

        protected override void Render(System.Web.UI.HtmlTextWriter output)
        {
            try
            {
                object fieldValue = this.ListItem[this.FieldName];
                fieldValue = GetItemFromCollectionByIndex(fieldValue, ItemIndex);

                string subPropertyValue = string.Empty;
                object subPropertyObject = null;
                if (!string.IsNullOrEmpty(Property))
                {
                    subPropertyObject = GetSubPropertyValue(fieldValue, Property);
                }
                else
                {
                    subPropertyObject = fieldValue;
                }

                if (null != PropertyTypeConverter)
                {
                    subPropertyObject = ApplyTypeConverter(subPropertyObject, PropertyTypeConverter);
                }

                if (!string.IsNullOrEmpty(RenderTemplate))
                {
                    subPropertyValue = string.Format(CultureInfo.InvariantCulture, RenderTemplate, subPropertyObject);
                }
                else
                {
                    subPropertyValue = subPropertyObject.ToString();
                }
                output.Write(subPropertyValue);
            }
            catch (NullReferenceException ex)
            {
                //Do something
            }
            catch (ArgumentOutOfRangeException ex)
            {
                //Do something
            }
            catch (ArgumentNullException ex)
            {
                //Do something
            }
            catch (ArgumentException ex)
            {
                //Do something
            }
        }

        private static object GetSubProperty(object item, string property, string index)
        {
            Type type = item.GetType();
            PropertyInfo propertyInfo = type.GetProperty(property);
            object value = propertyInfo.GetValue(item, null);

            return GetItemFromCollectionByIndex(value, index);
        }

        private static object GetSubPropertyValue(object item, string property)
        {
            object parentItem = item;
            if (!string.IsNullOrEmpty(property))
            {
                string[] parts = property.Split('.');
                for (int i = 0; i < parts.Length; i++)
                {
                    string[] subParts = parts[i].Split('[');
                    string propertyName = parts[i];
                    string index = "0";

                    if (subParts.Length > 1)
                    {
                        propertyName = subParts[0];
                        index = subParts[1].Replace("]", "");
                    }
                    else
                    {
                        propertyName = parts[i];
                    }

                    parentItem = GetSubProperty(parentItem, propertyName, index);
                }
            }

            return parentItem;
        }

        private static object GetItemFromCollectionByIndex(object searchCollection, string index)
        {
            ICollection collection = searchCollection as ICollection;
            object result = searchCollection;
            if (collection != null)
            {
                int requestedIndex = 0;
                int itemsCount = collection.Count;

                if (!string.IsNullOrEmpty(index))
                {
                    if (!int.TryParse(index, out requestedIndex))
                    {
                        requestedIndex = 0;
                    }
                }

                IEnumerator enumObject = collection.GetEnumerator();
                requestedIndex = Math.Max(0, requestedIndex);
                requestedIndex = Math.Min(itemsCount, requestedIndex);

                for (int x = 0; x < itemsCount; x++)
                {
                    enumObject.MoveNext();

                    if (x == requestedIndex)
                    {
                        result = enumObject.Current;
                        break;
                    }
                }
            }

            return result;
        }

        private static object ApplyTypeConverter(object property, TypeConverter converter)
        {
            object result = property;

            if (null != property && null != converter)
            {
                if (!string.IsNullOrEmpty(converter.AssemblyFullyQualifiedName) && !string.IsNullOrEmpty(converter.ClassFullyQualifiedName) && !string.IsNullOrEmpty(converter.MethodName))
                {
                    AssemblyName assemblyName = new AssemblyName(converter.AssemblyFullyQualifiedName);
                    Assembly assembly = Assembly.Load(assemblyName);
                    Type classType = assembly.GetType(converter.ClassFullyQualifiedName);
                    object[] parametersArray = new object[] { property };
                    result = classType.InvokeMember(converter.MethodName, System.Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, "", parametersArray);
                }
            }

            return result;
        }
    }

    public class TypeConverter
    {
        public string AssemblyFullyQualifiedName
        {
            get;
            set;
        }
        public string ClassFullyQualifiedName
        {
            get;
            set;
        }
        public string MethodName
        {
            get;
            set;
        }
    }
}

Notes
  1. The control is inherited from "BaseFieldControl" for extension
  2. The method "UpdateFieldValueInItem" is overridden by an empty method to prevent the control from updating the value of the field in the listitem. It took me some time to figure this out :) 
  3. I used reflection to get subproperties of the field property
  4. Some string manipulation is used to get indexes of subproperties
  5. The type converter is provided by setting three properties
    1. AssemblyFullyQualifiedName: this is the assembly fully qualified name (see the example below)
    2. ClassFullyQualifiedName: this is the fully qualified name of the class which includes the conversion method (see the example below)
    3. MethodName: this is the name of the conversion method (see the example below)
  6. To calculate the last value to be rendered, I first get the final subproperty value by reflection, then pass it to the type converter (if provided) and finally apply the formatting pattern (if provided) 
  7. I used the "Render" method to render the final value


So, now to use this control on a page layout, first we need to register the control on the page as follows:
<%@ Register Tagprefix="DevelopmentSimplyPutControls" Namespace="DevelopmentSimplyPut.Sharepoint.WebControls" Assembly="DevelopmentSimplyPut.Sharepoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a0271768eefdc39" %>

Then we can place the control on the page as follows:
<DevelopmentSimplyPutControls:ListItemPropertyField runat="server" id="ContentOwnerEmail" FieldName="ContentOwner" Property="User.Email" RenderTemplate="Email:{0}" ItemIndex="0">
 <PropertyTypeConverter MethodName="EmailCustomConverter" ClassFullyQualifiedName="DevelopmentSimplyPut.Utilities.TypeConverters" AssemblyFullyQualifiedName="DevelopmentSimplyPut.Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a0271768eefdc39"/>
</DevelopmentSimplyPutControls:ListItemPropertyField>

Notes
  1. The "Property" attribute is optional. If not provided, then the value returned will be the value of the field property, no subproperties
  2. The "ItemIndex" attribute is optional. If not provided, in case of collection field property, it will be assumed to be zero. I provided it here just for clarification but it could be ignored
  3. The "RenderTemplate" attribute is optional. If provided, it will be used to format the final value. Every "{0}" in the template will be replaced by the final value
  4. The "PropertyTypeConverter" inner property is optional. If provided, the control will load the conversion method from its assembly provided by reflection and apply this method on the final value
  5. For "PropertyTypeConverter" to work:
    1. The assembly provided should be in the GAC
    2. The class including the method should be static
    3. The conversion method should be static


Hope you find this control useful.
Good Luck


2012-11-07

How To Manage Your Resources


How To Manage Your Resources

As you are going on with your career you stumble on many resources. These resources are very important and they are your assets. So, you should think about how to save and keep track of these resources in order to be able to reuse them when needed.

What resources?
  1. People you know (colleagues, team members, family, friends, neighbors, bloggers, .......)
  2. Internet resources (emails, websites, blogs, communities, forums, ......)
  3. Personal work (trials, proof of concepts, projects, .......)

How to save/track/organize them?
It depends on the type of the resource. Some ways may be somehow common and others may not. Always keep your mind open for new ideas and from time to time try to invest some of your precious time to think of new ideas about organizing your resources. Believe me, do it and you will feel the difference.


What to do with "People I know"?
  1. Keep a good and kind relationship with them
  2. Try to keep in touch
  3. Know each one's strength points
  4. Be sensitive and aware of the context (what to say and when, .........)
  5. Show some -but not too much offending- interest in their life events
  6. Offer help when "needed" not "directly asked"
  7. Say thanks when you can
  8. Invest some time in following their news on Facebook, Twitter, LinkedIn, ......
  9. Integrate your outlook or email client with other services like Facebook, Twitter, LinkedIn, ......
  10. Know who knows/did what
  11. Use a smartphone, it really offers some good magic
  12. Always update your mobile contacts (think of synchronizing them with your Google account, if you don't have one, then for sure have one!!!)
  13. Synchronize your calendar with your contacts' birthdays and occasions
  14. Integrate your internet browser (please try to use one not a bunch) with an email notifier to keep you notified about any incoming mails
  15. It is not a good thing to give someone an email you don't intend to check. It is like allocating a junk box especially for him, then you should probably tell him to know how special he is to you. What a kind gesture of you!!!
  16. Try to find the best in everyone and try to do it better
  17. Don't repeat their mistakes. If someone treated you bad, don't do it with others
  18. Avoid prejudging when you can
  19. Try to find common aspects between you and each one of them
  20. Don't underestimate them
  21. Try to be a model for others to follow because they love to
  22. Be a human with all what the word means not just a being


What to do with my "Internet Resources"?
  1. Try to use only one internet browser to be easy to manage
  2. Bookmark your favorite pages
  3. Add meaningful tags to your bookmarks to be able to search among them later
  4. Synchronize your bookmarks online so that you will not be afraid of losing them and also you can access them anywhere
  5. Synchronize your accounts and subscriptions details (like passwords) online so that you can keep them safe and access them anywhere
  6. If you have any important technical content you wish to keep, try to send it to yourself into a  personal email and specify a folder on your email provider for these mails
  7. Use StumbleUpon service to provide you with random pages on your favorite categories. You can have more than one profile so that each profile has its own categories. It is a very good service and you will be shocked by what sites it gets you


What to do with my "Personal Work"?
  1. Find an online file hosting service to host your important files, documents, source code, ...... so that you can access them anywhere
  2. Find an online source control service to keep your important files, documents, source code, ...... so that you can access them anywhere


That's all what I have in mind right now but please try to put your own system to keep your resources safe and organized.


2012-11-06

How To Restore/Backup SQL Database From/To Remote Server

Did you ever need to restore a SQL database from a backup (.bak) file which is located on another remote server? Or let's be more general, did you ever need to access a file from SQL Server Management Studio File Explorer and this file was located on a remote server?

Some may think that this could be achieved by creating a mapped network drive, on the machine having SQL server installed, which is pointing to the shared path in the other remote server...... then this mapped network drive would be accessible from SQL Server Management Studio File Explorer.

Alas, even after creating the mapped network drive this drive will not be accessible from SQL Server Management Studio File Explorer, actually it will not appear among the available drives.

So, how to do it? If you are interested to know, you can read the rest of the post which will explain in details and screenshots how to do it.


Problem???!!

How To Restore/Backup SQL Database From/To Remote Server
  1. We have a "DB.bak" file which is a database backup file
  2. It is located on a server named "ServerName" (this server may be a web server, file server or any type of servers with file system, it doesn't need to be a SQL server at all so don't get confused)
  3. We have another SQL server on which we want to restore the "DB.bak" file
  4. When we proceed with the regular DB restore operation, we can't browse to the "DB.bak" file from SQL Server Management Studio File Explorer and we can't enforce it by anyway
  5. We need to make the "DB.bak" file accessible from SQL Server Management Studio File Explorer


Configuring Security & Sharing on the Remote Server (ServerName)

On the remote server called "ServerName" which has the "DB.bak" file on its file system, browse to the folder including the file, right-click, properties and then follow the screenshots below.






Creating Mapped Network Drive on the SQL Server

Log on the SQL server machine to create a mapped network drive which is mapped to the shared path you already created in the previous step. To do this, follow the screenshot below.



Registering the Mapped Network Path on SQL Server Management Studio

You, you need to register the mapped network path on the SQL Server Management Studio in order to be able to access the mapped drive from SQL Server Management Studio File Explorer. To do so, open SQL Server Management Studio and execute the query below.
EXEC sp_configure 'show advanced options', 1  
GO  
RECONFIGURE  
GO  
EXEC sp_configure 'xp_cmdshell', 1  
GO  
RECONFIGURE  
GO   

EXEC XP_CMDSHELL 'net use N: /delete'  
EXEC XP_CMDSHELL 'net use N: \\ServerName\DB-Backup-ShareName'  
EXEC XP_CMDSHELL 'Dir N:'


Result???

Now, you can find the mapped network drive (N:\ in our case) among the other drives in the SQL Server Management Studio File Explorer. Now you can restore/backup from/to the shared path as you wish.


Wish this may help you some day, good luck :)



2012-11-04

[ExtensionMethods] Generics (T)

These are extension methods for "Generics (T)". Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
 
namespace DevelopmentSimplyPut.ExtensionMethods.GenericsExtensionMethods
{
 public static class GenericsExtensionMethods
 {
  /// <summary>
  /// Returns a bool indicating whether the object is nullable or not
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="source"></param>
  /// <returns></returns>
  public static bool ext_IsNullable<T>(this T source)
  {
   bool result = false;
 
   if (source == null)
   {
    result = true;
   }
   else
   {
    Type type = typeof(T);
 
    if (!type.IsValueType)
    {
     result = true;
    }
    else if (Nullable.GetUnderlyingType(type) != null)
    {
     result = true;
    }
   }
 
   return result;
  }
  /// <summary>
  /// Returns a bool indicating whether the object is value type or not
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="source"></param>
  /// <returns></returns>
  public static bool ext_IsItValueType<T>(this T source)
  {
   Type type = typeof(T);
   return (type.IsValueType);
  }
  /// <summary>
  /// Converts from T1 to T2 if doable or just return a default value
  /// </summary>
  /// <typeparam name="T1">Type of Object to convert</typeparam>
  /// <typeparam name="T2">Type of Object to convert to</typeparam>
  /// <param name="source">Object to convert</param>
  /// <param name="CustomConvertOrDefault">Predicate to be used for conversion</param>
  /// <returns>Converted object of type T2 or a default value</returns>
  public static T2 ext_ConvertOrDefault<T1, T2>(this T1 source, Func<T1, T2> CustomConvertOrDefault)
  {
   return CustomConvertOrDefault(source);
  }
  /// <summary>
  /// Converts from T1 to T2 if doable or just return a default value
  /// </summary>
  /// <typeparam name="T1">Type of Object to convert</typeparam>
  /// <typeparam name="T2">Type of Object to convert to</typeparam>
  /// <param name="source">Object to convert</param>
  /// <param name="CustomConvertOrDefault">Predicate to be used for conversion</param>
  /// <param name="defaultValue">Default value to be returned if conversion fails</param>
  /// <returns>Converted object of type T2 or a default value</returns>
  public static T2 ext_ConvertOrDefault<T1, T2>(this T1 source, Func<T1, T2, T2> CustomConvertOrDefault, T2 defaultValue)
  {
   return CustomConvertOrDefault(source, defaultValue);
  }
  /// <summary>
  /// Serialises an object of type T in to an xml string
  /// </summary>
  /// <typeparam name="T">Any class type</typeparam>
  /// <param name="objectToSerialise">Object to serialise</param>
  /// <returns>A string that represents Xml, empty oterwise</returns>
  public static string ext_XmlSerialize<T>(this T objectToSerialise) where T : class
  {
   var serialiser = new XmlSerializer(typeof(T));
   string xml;
   using (var memStream = new MemoryStream())
   {
    using (var xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8))
    {
     serialiser.Serialize(xmlWriter, objectToSerialise);
     xml = Encoding.UTF8.GetString(memStream.GetBuffer());
    }
   }
 
   // ascii 60 = '<' and ascii 62 = '>'
   xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
   xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
   return xml;
  }
  /// <summary>
  /// Checks if a value falls in between two given values
  /// </summary>
  /// <typeparam name="T">Type</typeparam>
  /// <param name="value"></param>
  /// <param name="low">Lower bound value</param>
  /// <param name="high">Higher bound value</param>
  /// <returns></returns>
  public static bool ext_IsBetween<T>(this T value, T low, T high) where T : IComparable<T>
  {
   return value.CompareTo(low) >= 0 && value.CompareTo(high) <= 0;
  }
 }
}

[ExtensionMethods] System.IConvertible

These are extension methods for "System.IConvertible" class. Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace DevelopmentSimplyPut.ExtensionMethods.IConvertibleEM
{
 public static class IConvertibleExtensionMethods
 {
  public static T ext_ConvertTo<T>(this IConvertible value)
  {
   object holder = Convert.ChangeType(value, typeof(T));
   T result = default(T);
 
   if (null != holder)
   {
    result = (T)holder;
   }
 
   return result;
  }
 }
}


[ExtensionMethods] System.Linq.IOrderedQueryable

These are extension methods for "System.Linq.IOrderedQueryable" class. Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System.Text;
 
namespace DevelopmentSimplyPut.ExtensionMethods.IOrderedQueryableEM
{
 /// <summary>
 /// LinQ  Extentensions
 /// </summary>
 public static class LinqExtensionMethods
 {
  /// <summary>
  /// Converts the Linq data to a commaseperated string including header.
  /// </summary>
  /// <param name="data">The data.</param>
  /// <returns></returns>
  public static string ext_ToCSVString(this System.Linq.IOrderedQueryable data)
  {
   return ext_ToCSVString(data, "; ");
  }
  /// <summary>
  /// Converts the Linq data to a commaseperated string including header.
  /// </summary>
  /// <param name="data">The data.</param>
  /// <param name="delimiter">The delimiter.</param>
  /// <returns></returns>
  public static string ext_ToCSVString(this System.Linq.IOrderedQueryable data, string delimiter)
  {
   return ext_ToCSVString(data, "; ", null);
  }
  /// <summary>
  /// Converts the Linq data to a commaseperated string including header.
  /// </summary>
  /// <param name="data">The data.</param>
  /// <param name="delimiter">The delimiter.</param>
  /// <param name="nullvalue">The nullvalue.</param>
  /// <returns></returns>
  public static string ext_ToCSVString(this System.Linq.IOrderedQueryable data, string delimiter, string nullvalue)
  {
   StringBuilder csvdata = new StringBuilder();
   string replaceFrom = delimiter.Trim();
   string replaceDelimiter = "?";
   System.Reflection.PropertyInfo[] headers = data.ElementType.GetProperties();
   switch (replaceFrom)
   {
    case ";":
     replaceDelimiter = ":";
     break;
    case ",":
     replaceDelimiter = "¸";
     break;
    case "\t":
     replaceDelimiter = "    ";
     break;
    default:
     break;
   }
   if (headers.Length > 0)
   {
    foreach (var head in headers)
    {
     csvdata.Append(head.Name.Replace("_", " ") + delimiter);
    }
    csvdata.Append("\n");
   }
   foreach (var row in data)
   {
    var fields = row.GetType().GetProperties();
    for (int i = 0; i < fields.Length; i++)
    {
     object value = null;
     try
     {
      value = fields[i].GetValue(row, null);
     }
     catch { }
     if (value != null)
     {
      csvdata.Append(value.ToString().Replace("\r", "\f").Replace("\n", " \f").Replace("_", " ").Replace(replaceFrom, replaceDelimiter) + delimiter);
     }
     else
     {
      csvdata.Append(nullvalue);
      csvdata.Append(delimiter);
     }
    }
    csvdata.Append("\n");
   }
   return csvdata.ToString();
  }   
 }
}


[ExtensionMethods] System.DateTime

These are extension methods for "System.DateTime" class. Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System;
 
namespace DevelopmentSimplyPut.ExtensionMethods.DateTimeEM
{
 /// <summary>
 /// DateTime Extensions
 /// </summary>
 public static class DateTimeExtensionMethods
 {
  /// <summary>
  /// Elapseds the time.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <returns>TimeSpan</returns>
  public static TimeSpan ext_Elapsed(this DateTime datetime)
  {
   return DateTime.Now - datetime;
  }
  /// <summary>
  /// Weeks the of year.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="weekrule">The weekrule.</param>
  /// <param name="firstDayOfWeek">The first day of week.</param>
  /// <returns></returns>
  public static int ext_WeekOfYear(this DateTime datetime, System.Globalization.CalendarWeekRule weekrule, DayOfWeek firstDayOfWeek)
  {
   System.Globalization.CultureInfo ciCurr = System.Globalization.CultureInfo.CurrentCulture;
   return ciCurr.Calendar.GetWeekOfYear(datetime, weekrule, firstDayOfWeek);
  }
  /// <summary>
  /// Weeks the of year.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="firstDayOfWeek">The first day of week.</param>
  /// <returns></returns>
  public static int ext_WeekOfYear(this DateTime datetime, DayOfWeek firstDayOfWeek)
  {
   System.Globalization.DateTimeFormatInfo dateinf = new System.Globalization.DateTimeFormatInfo();
   System.Globalization.CalendarWeekRule weekrule = dateinf.CalendarWeekRule;
   return ext_WeekOfYear(datetime, weekrule, firstDayOfWeek);
  }
  /// <summary>
  /// Weeks the of year.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="weekrule">The weekrule.</param>
  /// <returns></returns>
  public static int ext_WeekOfYear(this DateTime datetime, System.Globalization.CalendarWeekRule weekrule)
  {
   System.Globalization.DateTimeFormatInfo dateinf = new System.Globalization.DateTimeFormatInfo();
   DayOfWeek firstDayOfWeek = dateinf.FirstDayOfWeek;
   return ext_WeekOfYear(datetime, weekrule, firstDayOfWeek);
  }
  /// <summary>
  /// Weeks the of year.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="weekrule">The weekrule.</param>
  /// <returns></returns>
  public static int ext_WeekOfYear(this DateTime datetime)
  {
   System.Globalization.DateTimeFormatInfo dateinf = new System.Globalization.DateTimeFormatInfo();
   System.Globalization.CalendarWeekRule weekrule = dateinf.CalendarWeekRule;
   DayOfWeek firstDayOfWeek = dateinf.FirstDayOfWeek;
   return ext_WeekOfYear(datetime, weekrule, firstDayOfWeek);
  }
  /// <summary>
  /// Gets the date time for day of week.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="day">The day.</param>
  /// <param name="firstDayOfWeek">The first day of week.</param>
  /// <returns></returns>
  public static DateTime ext_GetDateTimeForDayOfWeek(this DateTime datetime, DayOfWeek day, DayOfWeek firstDayOfWeek)
  {
   int current = DaysFromFirstDayOfWeek(datetime.DayOfWeek, firstDayOfWeek);
   int resultday = DaysFromFirstDayOfWeek(day, firstDayOfWeek);
   return datetime.AddDays(resultday - current);
  }
  /// <summary>
  /// Gets the date time for day of week.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="day">The day</param>
  /// <returns></returns>
  public static DateTime ext_GetDateTimeForDayOfWeek(this DateTime datetime, DayOfWeek day)
  {
   System.Globalization.DateTimeFormatInfo dateinf = new System.Globalization.DateTimeFormatInfo();
   DayOfWeek firstDayOfWeek = dateinf.FirstDayOfWeek;
   return ext_GetDateTimeForDayOfWeek(datetime, day, firstDayOfWeek);
  }
  /// <summary>
  /// Firsts the date time of week.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <returns></returns>
  public static DateTime ext_FirstDateTimeOfWeek(this DateTime datetime)
  {
   System.Globalization.DateTimeFormatInfo dateinf = new System.Globalization.DateTimeFormatInfo();
   DayOfWeek firstDayOfWeek = dateinf.FirstDayOfWeek;
   return ext_FirstDateTimeOfWeek(datetime, firstDayOfWeek);
  }
  /// <summary>
  /// Firsts the date time of week.
  /// </summary>
  /// <param name="datetime">The datetime.</param>
  /// <param name="firstDayOfWeek">The first day of week.</param>
  /// <returns></returns>
  public static DateTime ext_FirstDateTimeOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek)
  {
   return datetime.AddDays(-DaysFromFirstDayOfWeek(datetime.DayOfWeek, firstDayOfWeek));
  }
  /// <summary>
  /// Days from first day of week.
  /// </summary>
  /// <param name="current">The current.</param>
  /// <param name="firstDayOfWeek">The first day of week.</param>
  /// <returns></returns>
  private static int DaysFromFirstDayOfWeek(DayOfWeek current, DayOfWeek firstDayOfWeek)
  {
   //Sunday = 0,Monday = 1,...,Saturday = 6
   int daysbetween = current - firstDayOfWeek;
   if (daysbetween < 0) daysbetween = 7 + daysbetween;
   return daysbetween;
  }
  /// <summary>
  /// Gets the string representation of a date if available
  /// </summary>
  /// <param name="datetime">The date</param>
  /// <param name="defaultvalue">The default value</param>
  /// <returns></returns>
  public static string ext_GetValueOrDefaultToString(this DateTime? datetime, string defaultvalue)
  {
   if (datetime == null) return defaultvalue;
   return datetime.Value.ToString();
  }
  /// <summary>
  /// Gets the string representation of a date if available
  /// </summary>
  /// <param name="datetime">The date</param>
  /// <param name="format">The format of the string representation of the date</param>
  /// <param name="defaultvalue">The default value</param>
  /// <returns></returns>
  public static string ext_GetValueOrDefaultToString(this DateTime? datetime, string format, string defaultvalue)
  {
   if (datetime == null) return defaultvalue;
   return datetime.Value.ToString(format);
  }
 }
}


2012-11-03

[ExtensionMethods] System.Web.UI.WebControls.WebControl

These are extension methods for "System.Web.UI.WebControls.WebControl" class. Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System;
using System.Linq;
using System.Web.UI.WebControls;
using System.Collections.Generic;
 
namespace DevelopmentSimplyPut.ExtensionMethods.WebControlEM
{
 public static class WebControlExtensionMethods
 {
  /// <summary>
  /// Adds a CSS class to a WebControl
  /// </summary>
  /// <param name="control">The WebControl</param>
  /// <param name="cssClass">CSS class name</param>
  public static void ext_AddCssClass(this WebControl control, string cssClass)
  {
   if (null != control && !string.IsNullOrEmpty(cssClass) && !string.IsNullOrWhiteSpace(cssClass))
   {
    if (string.IsNullOrEmpty(control.CssClass) || string.IsNullOrWhiteSpace(control.CssClass))
    {
     control.CssClass = cssClass;
    }
    else
    {
     bool found = false;
     {
      found = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .Any<string>(classEntry => classEntry.Trim().ToUpperInvariant().Equals(cssClass.Trim().ToUpperInvariant(), StringComparison.OrdinalIgnoreCase));
     }
 
     if (!found)
     {
      control.CssClass += " " + cssClass;
      control.CssClass.Trim();
     }
    }
   }
  }
  /// <summary>
  /// Removes a CSS class from a WebControl
  /// </summary>
  /// <param name="control">The WebControl</param>
  /// <param name="cssClass">CSS class name</param>
  public static void ext_RemoveCssClass(this WebControl control, string cssClass)
  {
   if (null != control && !string.IsNullOrEmpty(cssClass) && !string.IsNullOrWhiteSpace(cssClass) && !string.IsNullOrEmpty(control.CssClass) && !string.IsNullOrWhiteSpace(control.CssClass))
   {
    if (control.CssClass.Trim().ToUpperInvariant().Equals(cssClass.Trim().ToUpperInvariant()))
    {
     control.CssClass = string.Empty;
    }
    else
    {
     var classes = control.CssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
          .SkipWhile<string>(classEntry => classEntry.Trim().ToUpperInvariant().Equals(cssClass.Trim().ToUpperInvariant(), StringComparison.OrdinalIgnoreCase))
          .ToArray<string>();
 
     if (null != classes && classes.Length > 0)
     {
      control.CssClass = String.Join(" ", classes);
      control.CssClass.Trim();
     }
    }
   }
  }
  /// <summary>
  /// Gets an IEnumerable of all child WebControls
  /// </summary>
  /// <param name="source">WebControl</param>
  /// <returns></returns>
  public static IEnumerable<WebControl> ext_GetChildControlsRecursively(this WebControl source)
  {
   return source.ext_GetChildControlsRecursively(null);
  }
  /// <summary>
  /// Gets an IEnumerable of all child WebControls which satisfy a certain condition
  /// </summary>
  /// <param name="source">WebControl</param>
  /// <param name="selector">Selector method which decides if a certain WebControl should be selected</param>
  /// <returns></returns>
  public static IEnumerable<WebControl> ext_GetChildControlsRecursively(this WebControl source, Func<WebControl,bool> selector)
  {
   if (null != source)
   {
    if ((null == selector) || (null != selector && selector(source)))
    {
     yield return source;
    }
 
    if (!source.HasControls())
    {
     yield break;
    }
 
    foreach (WebControl ctrl in source.Controls)
    {
     foreach (WebControl ctrl1 in ctrl.ext_GetChildControlsRecursively())
     {
      if ((null == selector) || (null != selector && selector(ctrl1)))
      {
       yield return ctrl1;
      }
     }
    }
   }
   else
   {
    yield break;
   }
  }
 }
}


[ExtensionMethods] System.String

These are extension methods for "System.String" class. Some are written by me and the rest are collected from other sources. Hope you find them useful :)


using System;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
 
namespace DevelopmentSimplyPut.ExtensionMethods.StringEM
{
 public static class StringExtensionMethods
 {
  /// <summary>
  /// Repeats a string for a given number of times
  /// </summary>
  /// <param name="source">String</param>
  /// <param name="numberOfTimes">Number of times</param>
  /// <returns></returns>
  public static string ext_RepeatNoOfTimes(this string source, int numberOfTimes)
  {
   string result = string.Empty;
   for (int i = 0; i < numberOfTimes; i++)
   {
    result += source;
   }
   return result;
  }
  /// <summary>
  /// Formats numbers inside a string into a given number of digits with extra preceeding zeros
  /// </summary>
  /// <param name="source">String</param>
  /// <param name="numOfDigits">Number of digits</param>
  /// <returns></returns>
  public static string ext_PutNumbersIntoNoOfDigits(this string source, int numOfDigits)
  {
   string result = source;
   result = Regex.Replace(result, @"\d+", new MatchEvaluator(delegate(Match match)
   {
    if (match.Length < numOfDigits)
    {
     string zero = "0";
     result = zero.ext_RepeatNoOfTimes(numOfDigits - match.Length) + match.Value;
     return result;
    }
    else
    {
     result = match.Value;
     return result;
    }
   }));
 
   return result;
  }
  /// <summary>
  /// Gets the {n} first characters of a string
  /// </summary>
  /// <param name="source">String</param>
  /// <param name="NumOfChars">Number of characters</param>
  /// <returns></returns>
  public static string ext_First(this string source, int NumOfChars)
  {
   if (string.IsNullOrEmpty(source) || source.Length <= NumOfChars)
   {
    return source;
   }
 
   return source.Substring(0, NumOfChars);
  }
  /// <summary>
  /// Gets the {n} last characters of a string
  /// </summary>
  /// <param name="source">String</param>
  /// <param name="NumOfChars">Number of characters</param>
  /// <returns></returns>
  public static string ext_Last(this string source, int NumOfChars)
  {
   if (string.IsNullOrEmpty(source) || source.Length <= NumOfChars)
   {
    return source;
   }
 
   return source.Substring(source.Length - NumOfChars, NumOfChars);
  }
  /// <summary>
  /// Splits a string into two parts, the first one is the string part before the last existance of a certain character,
  /// the second one is the string part after the last existance of the same character
  /// </summary>
  /// <param name="source"></param>
  /// <param name="splitAtChar"></param>
  /// <returns></returns>
  public static string[] ext_SplitAtLastCharExistance(this string source, char splitAtChar)
  {
   string[] result = { string.Empty, string.Empty };
   if (string.IsNullOrEmpty(source))
   {
    //string is empty
    return result;
   }
 
   if (!(source.Contains(splitAtChar)))
   {
    //character doesn't exist in the string
    result[0] = source;
    return result;
   }
 
   if (source.Length == 1)
   {
    //the string is only the character itself
    return result;
   }
 
   int temp = source.LastIndexOf(splitAtChar);
 
   if (temp == 0)
   {
    //the character is at the start
    result[0] = string.Empty;
    result[1] = source.Substring(temp + 1, source.Length - temp - 1);
   }
   else if (temp == source.Length - 1)
   {
    //the character is at the end
    result[0] = source.Substring(0, temp);
    result[1] = string.Empty;
   }
   else
   {
    //the character is in the middle
    result[0] = source.Substring(0, temp);
    result[1] = source.Substring(temp + 1, source.Length - temp - 1);
   }
 
   return result;
  }
  /// <summary>
  /// Gets the preceding part of a string except the last given number of characters
  /// </summary>
  /// <param name="source">String</param>
  /// <param name="NumOfChars">Number of characters</param>
  /// <returns></returns>
  public static string ext_ExceptLastNoOfChars(this string source, int NumOfChars)
  {
   if (string.IsNullOrEmpty(source) || source.Length <= NumOfChars)
   {
    return string.Empty;
   }
 
   return source.Substring(0, source.Length - NumOfChars);
  }
  /// <summary>
  /// Gets the succeeding part of a string except the first given number of characters
  /// </summary>
  /// <param name="source"></param>
  /// <param name="NumOfChars"></param>
  /// <returns></returns>
  public static string ext_ExceptFirstNoOfChars(this string source, int NumOfChars)
  {
   if (string.IsNullOrEmpty(source) || source.Length <= NumOfChars)
   {
    return string.Empty;
   }
 
   return source.Substring(NumOfChars, source.Length - NumOfChars);
  }
  /// <summary>
  /// Formats a string with one literal placeholder.
  /// </summary>
  /// <param name="text">The extension text</param>
  /// <param name="arg0">Argument 0</param>
  /// <returns>The formatted string</returns>
  public static string ext_FormatWith(this string text, object arg0)
  {
   return string.Format(text, arg0);
  }
  /// <summary>
  /// Formats a string with two literal placeholders.
  /// </summary>
  /// <param name="text">The extension text</param>
  /// <param name="arg0">Argument 0</param>
  /// <param name="arg1">Argument 1</param>
  /// <returns>The formatted string</returns>
  public static string ext_FormatWith(this string text, object arg0, object arg1)
  {
   return string.Format(text, arg0, arg1);
  }
  /// <summary>
  /// Formats a string with tree literal placeholders.
  /// </summary>
  /// <param name="text">The extension text</param>
  /// <param name="arg0">Argument 0</param>
  /// <param name="arg1">Argument 1</param>
  /// <param name="arg2">Argument 2</param>
  /// <returns>The formatted string</returns>
  public static string ext_FormatWith(this string text, object arg0, object arg1, object arg2)
  {
   return string.Format(text, arg0, arg1, arg2);
  }
  /// <summary>
  /// Formats a string with a list of literal placeholders.
  /// </summary>
  /// <param name="text">The extension text</param>
  /// <param name="args">The argument list</param>
  /// <returns>The formatted string</returns>
  public static string ext_FormatWith(this string text, params object[] args)
  {
   return string.Format(text, args);
  }
  /// <summary>
  /// Formats a string with a list of literal placeholders.
  /// </summary>
  /// <param name="text">The extension text</param>
  /// <param name="provider">The format provider</param>
  /// <param name="args">The argument list</param>
  /// <returns>The formatted string</returns>
  public static string ext_FormatWith(this string text, IFormatProvider provider, params object[] args)
  {
   return string.Format(provider, text, args);
  }
  /// <summary>
  /// Deserialises an xml string in to an object of Type T
  /// </summary>
  /// <typeparam name="T">Any class type</typeparam>
  /// <param name="xml">Xml as string to deserialise from</param>
  /// <returns>A new object of type T is successful, null if failed</returns>
  public static T ext_XmlDeserialize<T>(this string xml) where T : class
  {
   var serialiser = new XmlSerializer(typeof(T));
   T newObject;
 
   using (var stringReader = new StringReader(xml))
   {
    using (var xmlReader = new XmlTextReader(stringReader))
    {
     try
     {
      newObject = serialiser.Deserialize(xmlReader) as T;
     }
     catch (InvalidOperationException) // String passed is not Xml, return null
     {
      return null;
     }
 
    }
   }
 
   return newObject;
  }
  /// <summary>
  /// Parses a string into an Enum
  /// </summary>
  /// <typeparam name="T">The type of the Enum</typeparam>
  /// <param name="value">String value to parse</param>
  /// <returns>The Enum corresponding to the stringExtensions</returns>
  public static T ext_ToEnum<T>(this string value)
  {
   return ext_ToEnum<T>(value, false);
  }
  /// <summary>
  /// Parses a string into an Enum
  /// </summary>
  /// <typeparam name="T">The type of the Enum</typeparam>
  /// <param name="value">String value to parse</param>
  /// <param name="ignorecase">Ignore the case of the string being parsed</param>
  /// <returns>The Enum corresponding to the stringExtensions</returns>
  public static T ext_ToEnum<T>(this string value, bool ignorecase)
  {
   if (value == null)
    throw new ArgumentNullException("Value");
 
   value = value.Trim();
 
   if (value.Length == 0)
    throw new ArgumentNullException("Must specify valid information for parsing in the string.", "value");
 
   Type t = typeof(T);
   if (!t.IsEnum)
    throw new ArgumentException("Type provided must be an Enum.", "T");
 
   return (T)Enum.Parse(t, value, ignorecase);
  }
  /// <summary>
  /// Toes the integer.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <param name="defaultvalue">The defaultvalue.</param>
  /// <returns></returns>
  public static int ext_ToInteger(this string value, int defaultvalue)
  {
   return (int)ext_ToDouble(value, defaultvalue);
  }
  /// <summary>
  /// Toes the integer.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <returns></returns>
  public static int ext_ToInteger(this string value)
  {
   return ext_ToInteger(value, 0);
  }
  /// <summary>
  /// Toes the double.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <param name="defaultvalue">The defaultvalue.</param>
  /// <returns></returns>
  public static double ext_ToDouble(this string value, double defaultvalue)
  {
   double result;
   if (double.TryParse(value, out result))
   {
    return result;
   }
   else return defaultvalue;
  }
  /// <summary>
  /// Toes the double.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <returns></returns>
  public static double ext_ToDouble(this string value)
  {
   return ext_ToDouble(value, 0);
  }
  /// <summary>
  /// Toes the date time.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <param name="defaultvalue">The defaultvalue.</param>
  /// <returns></returns>
  public static DateTime? ext_ToDateTime(this string value, DateTime? defaultvalue)
  {
   DateTime result;
   if (DateTime.TryParse(value, out result))
   {
    return result;
   }
   else return defaultvalue;
  }
  /// <summary>
  /// Toes the date time.
  /// </summary>
  /// <param name="value">The value.</param>
  /// <returns></returns>
  public static DateTime? ext_ToDateTime(this string value)
  {
   return ext_ToDateTime(value, null);
  }
  /// <summary>
  /// Converts a string value to bool value, supports "T" and "F" conversions.
  /// </summary>
  /// <param name="value">The string value.</param>
  /// <returns>A bool based on the string value</returns>
  public static bool? ext_ToBoolean(this string value)
  {
   if (string.Compare("T", value, true) == 0)
   {
    return true;
   }
   if (string.Compare("F", value, true) == 0)
   {
    return false;
   }
   bool result;
   if (bool.TryParse(value, out result))
   {
    return result;
   }
   else return null;
  }
  /// <summary>
  /// Gets the string value or a default value
  /// </summary>
  /// <param name="value">String</param>
  /// <returns></returns>
  public static string ext_GetValueOrEmpty(this string value)
  {
   return ext_GetValueOrDefault(value, string.Empty);
  }
  /// <summary>
  /// Gets the string value or a default value
  /// </summary>
  /// <param name="value">String</param>
  /// <param name="defaultvalue">Default value</param>
  /// <returns></returns>
  public static string ext_GetValueOrDefault(this string value, string defaultvalue)
  {
   if (value != null) return value;
   return defaultvalue;
  }
  /// <summary>
  /// Converts string to a Name-Format where each first letter is Uppercase.
  /// </summary>
  /// <param name="value">The string value.</param>
  /// <returns></returns>
  public static string ext_ToUpperLowerNameVariant(this string value)
  {
   if (string.IsNullOrEmpty(value)) return "";
   char[] valuearray = value.ToLower().ToCharArray();
   bool nextupper = true;
   for (int i = 0; i < (valuearray.Count() - 1); i++)
   {
    if (nextupper)
    {
     valuearray[i] = char.Parse(valuearray[i].ToString().ToUpper());
     nextupper = false;
    }
    else
    {
     switch (valuearray[i])
     {
      case ' ':
      case '-':
      case '.':
      case ':':
      case '\n':
       nextupper = true;
       break;
      default:
       nextupper = false;
       break;
     }
    }
   }
   return new string(valuearray);
  }
  /// <summary>
  /// Encryptes a string using the supplied key. Encoding is done using RSA encryption.
  /// </summary>
  /// <param name="stringToEncrypt">String that must be encrypted.</param>
  /// <param name="key">Encryptionkey.</param>
  /// <returns>A string representing a byte array separated by a minus sign.</returns>
  /// <exception cref="ArgumentException">Occurs when stringToEncrypt or key is null or empty.</exception>
  public static string ext_Encrypt(this string stringToEncrypt, string key)
  {
   if (string.IsNullOrEmpty(stringToEncrypt))
   {
    throw new ArgumentException("An empty string value cannot be encrypted.");
   }
 
   if (string.IsNullOrEmpty(key))
   {
    throw new ArgumentException("Cannot encrypt using an empty key. Please supply an encryption key.");
   }
 
   System.Security.Cryptography.CspParameters cspp = new System.Security.Cryptography.CspParameters();
   cspp.KeyContainerName = key;
 
   System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(cspp);
   rsa.PersistKeyInCsp = true;
 
   byte[] bytes = rsa.Encrypt(System.Text.UTF8Encoding.UTF8.GetBytes(stringToEncrypt), true);
 
   return BitConverter.ToString(bytes);
  }
  /// <summary>
  /// Decryptes a string using the supplied key. Decoding is done using RSA encryption.
  /// </summary>
  /// <param name="key">Decryptionkey.</param>
  /// <returns>The decrypted string or null if decryption failed.</returns>
  /// <exception cref="ArgumentException">Occurs when stringToDecrypt or key is null or empty.</exception>
  public static string ext_Decrypt(this string stringToDecrypt, string key)
  {
   string result = null;
 
   if (string.IsNullOrEmpty(stringToDecrypt))
   {
    throw new ArgumentException("An empty string value cannot be encrypted.");
   }
 
   if (string.IsNullOrEmpty(key))
   {
    throw new ArgumentException("Cannot decrypt using an empty key. Please supply a decryption key.");
   }
 
   try
   {
    System.Security.Cryptography.CspParameters cspp = new System.Security.Cryptography.CspParameters();
    cspp.KeyContainerName = key;
 
    System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(cspp);
    rsa.PersistKeyInCsp = true;
 
    string[] decryptArray = stringToDecrypt.Split(new string[] { "-" }, StringSplitOptions.None);
    byte[] decryptByteArray = Array.ConvertAll<string, byte>(decryptArray, (s => Convert.ToByte(byte.Parse(s, System.Globalization.NumberStyles.HexNumber))));
 
 
    byte[] bytes = rsa.Decrypt(decryptByteArray, true);
 
    result = System.Text.UTF8Encoding.UTF8.GetString(bytes);
 
   }
   finally
   {
    // no need for further processing
   }
 
   return result;
  }
  /// <summary>
  /// Determines whether it is a valid URL.
  /// </summary>
  /// <returns>
  ///     <c>true</c> if [is valid URL] [the specified text]; otherwise, <c>false</c>.
  /// </returns>
  public static bool ext_IsValidUrl(this string text)
  {
   System.Text.RegularExpressions.Regex rx = new System.Text.RegularExpressions.Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");
   return rx.IsMatch(text);
  }
  /// <summary>
  /// Determines whether it is a valid email address
  /// </summary>
  /// <returns>
  ///     <c>true</c> if [is valid email address] [the specified s]; otherwise, <c>false</c>.
  /// </returns>
  public static bool ext_IsValidEmailAddress(this string email)
  {
   System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
   return regex.IsMatch(email);
  }
  /// <summary>
  /// Send an email using the supplied string.
  /// </summary>
  /// <param name="body">String that will be used i the body of the email.</param>
  /// <param name="subject">Subject of the email.</param>
  /// <param name="sender">The email address from which the message was sent.</param>
  /// <param name="recipient">The receiver of the email.</param>
  /// <param name="server">The server from which the email will be sent.</param>  
  /// <returns>A boolean value indicating the success of the email send.</returns>
  public static bool ext_Email(this string body, string subject, string sender, string recipient, string server)
  {
   try
   {
    // To
    MailMessage mailMsg = new MailMessage();
    mailMsg.To.Add(recipient);
 
    // From
    MailAddress mailAddress = new MailAddress(sender);
    mailMsg.From = mailAddress;
 
    // Subject and Body
    mailMsg.Subject = subject;
    mailMsg.Body = body;
 
    // Init SmtpClient and send
    SmtpClient smtpClient = new SmtpClient(server);
    System.Net.NetworkCredential credentials = new System.Net.NetworkCredential();
    smtpClient.Credentials = credentials;
 
    smtpClient.Send(mailMsg);
   }
   catch (Exception ex)
   {
    throw new Exception("Could not send mail from: " + sender + " to: " + recipient + " thru smtp server: " + server + "\n\n" + ex.Message, ex);
   }
 
   return true;
  }
  /// <summary>
  /// Truncates the string to a specified length and replace the truncated to a ...
  /// </summary>
  /// <param name="maxLength">total length of characters to maintain before the truncate happens</param>
  /// <returns>truncated string</returns>
  public static string ext_Truncate(this string text, int maxLength)
  {
   // replaces the truncated string to a ...
   const string suffix = "...";
   string truncatedString = text;
 
   if (maxLength <= 0) return truncatedString;
   int strLength = maxLength - suffix.Length;
 
   if (strLength <= 0) return truncatedString;
 
   if (text == null || text.Length <= maxLength) return truncatedString;
 
   truncatedString = text.Substring(0, strLength);
   truncatedString = truncatedString.TrimEnd();
   truncatedString += suffix;
   return truncatedString;
  }
  /// <summary>
  /// Converts to a HTML-encoded string
  /// </summary>
  /// <param name="data">The data.</param>
  /// <returns></returns>
  public static string ext_HtmlEncode(this string data)
  {
   return System.Web.HttpUtility.HtmlEncode(data);
  }
  /// <summary>
  /// Converts the HTML-encoded string into a decoded string
  /// </summary>
  public static string ext_HtmlDecode(this string data)
  {
   return System.Web.HttpUtility.HtmlDecode(data);
  }
  /// <summary>
  /// Parses a query string into a System.Collections.Specialized.NameValueCollection
  /// using System.Text.Encoding.UTF8 encoding.
  /// </summary>
  public static System.Collections.Specialized.NameValueCollection ext_ParseQueryString(this string query)
  {
   return System.Web.HttpUtility.ParseQueryString(query);
  }
  /// <summary>
  /// Encode an Url string
  /// </summary>
  public static string ext_UrlEncode(this string url)
  {
   return System.Web.HttpUtility.UrlEncode(url);
  }
  /// <summary>
  /// Converts a string that has been encoded for transmission in a URL into a
  /// decoded string.
  /// </summary>
  public static string ext_UrlDecode(this string url)
  {
   return System.Web.HttpUtility.UrlDecode(url);
  }
  /// <summary>
  /// Encodes the path portion of a URL string for reliable HTTP transmission from
  /// the Web server to a client.
  /// </summary>
  public static string ext_UrlPathEncode(this string url)
  {
   return System.Web.HttpUtility.UrlPathEncode(url);
  }
  /// <summary>
  /// Replaces the format item in a specified System.String with the text equivalent
  /// of the value of a specified System.Object instance.
  /// </summary>
  /// <param name="arg">The arg.</param>
  /// <param name="additionalArgs">The additional args.</param>
  public static string ext_Format(this string format, object arg, params object[] additionalArgs)
  {
   if (additionalArgs == null || additionalArgs.Length == 0)
   {
    return string.Format(format, arg);
   }
   else
   {
    return string.Format(format, new object[] { arg }.Concat(additionalArgs).ToArray());
   }
  }
  /// <summary>
  /// Determines whether [is not null or empty] [the specified input].
  /// </summary>
  /// <returns>
  ///     <c>true</c> if [is not null or empty] [the specified input]; otherwise, <c>false</c>.
  /// </returns>
  public static bool ext_IsNotNullOrEmpty(this string input)
  {
   return !String.IsNullOrEmpty(input);
  }
 }
}