Monday, December 3, 2012

How to display live server side processing/messages in a browser

How to display live server side processing/messages in browser.

Use this in case the webpart/application invokes a long running processes. This way the user will continuously be updated on the progression of code execution. In my particular case I'm using a textbox to pass messages onto but feel free to replace it with any other type of control, such as a progression bar. You can make it as fancy as you want, sky's the limit. The trick is to create an iframe, in which you embed the application page that actually calls the long running process. So you'll need a few things here

- A parent .aspx page with a textbox ('txtLogs')
- An 'child' .aspx page ('LongRunningOp.aspx') which will execute the code. The page itself will not contain any controls, just code behind
- An iframe to embed that child .aspx within the parent page
- An update function to update the textbox, which will be called through the child .aspx page

Creating the iframe will be accomplished by using the following piece of code

function BeginLongRunningOp() {

       var iframe = document.createElement("iframe");

       // set the source to the .aspx, in this example it can be found in the layouts folder of the 14
       // hyve as i created this for a SP2010 webpart
       iframe.src = "/_layouts/CodingNCrap/LongRunningOp.aspx";
       iframe.style.display = "none";
       document.body.appendChild(iframe);
}

Calling this function from the parent page will open the child page and start the execution. You will also need an additional function to update the textbox as code is execution:

function UpdateProgress(Message) {
       var logWindow = document.getElementById('<%= txtLogs.ClientID %>');
       logWindow.value = logWindow.value + Message;
}

Last thing needed to make this work is to go to the code of the child .aspx page and add the following line to your method that will make the update:

protected void UpdateProgress(string Message)
{
       try
       {
              Response.Write(String.Format("< script>parent.UpdateProgress({0});< / script>", Message));
       }

       catch (Exception exc)
       {
              // your exception handling logics here
       }
}







Monday, October 22, 2012

Finding site column used anywhere in your web application

Little powershell script to find out where your fields are currently being used in the web app

$solutionWebApplication = "http://yourSPurl/"
$fieldTitle = "ActiveConfig"    #DisplayName
$fieldGroup = "Custom Columns"
$spAssigment = Start-SPAssignment    #Use to dispose SPWeb safely
$spWeb = Get-SPWeb $solutionWebApplication -AssignmentCollection $spAssignment     #Get SPWeb Instance
foreach ($field in $web.AvailableFields)
{
 if($field.Group -eq $fieldGroup -and $field.Title -eq $fieldTitle)
 {
  $field.ListsFieldUsedIn() | ForEach-Object {
   $web = $site.AllWebs[$_.WebID]
   $list = $web.Lists[$_.ListID]
   Write-Host "Web:" $web.Title " | List:" $list.Title " | ListUrl:" $list.RootFolder.Url " | StaticName:" $field.StaticName " | FieldID:" $field.ID
  }
 }
}

Tuesday, October 16, 2012

Finding site column used in content type by querying content db

Exception calling "Delete" with "0" argument(s): "Site columns which are included in content types or on lists cannot be deleted. Please remove all instances of this site column pri
or to deleting it."


ughh..

there are myriad approaches to hunt down that damn column, for example by using powershell. here is an alternative, directly querying the content db


SELECT
     ct.ContentTypeId            [ContentType ID]
,    Definition                  [ContentType Definition]    
,    ct.DeleteTransactionId      [ContentType DeleteTransaction]
,    ctu.ListId                  [List GUID using CT]
,    l.tp_Created                [List Creation Date]
,    tp_DeleteTransactionId      [List DeleteTransaction]
,    tp_FeatureId                [Feature GUID]
FROM
     ContentTypeUsage             ctu
     INNER JOIN dbo.Webs          w  ON ctu.WebId         = w.Id
     INNER JOIN dbo.AllLists      l  ON ctu.ListId        = l.tp_ID
     INNER JOIN dbo.ContentTypes  ct ON ct.ContentTypeId  = ctu.ContentTypeId
WHERE
     ct.Definition LIKE '%your column%'     

Friday, August 17, 2012

Splitting key-value string using LINQ

Time has come to get down-and-dirty with LINQ! For an app i'm building, i need to store an array of key/value pairs in a single string. It consists of an ID and a folder path, which are separated by use of a semicolon delimiter. The actual pairs are then separated using a pipe. Here is an example:



string folders = @"32;c:\|48;c:\code|52;c:\program files|";

I created the following method that returns a Dictionary object:



        public static Dictionary<int, string> GetFolders(string folders)
        {
            string[] x = folders.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); // first retrieve a string array of all the pairs

            return x.ToDictionary(y => Convert.ToInt32(y.Split(';')[0]), y => y.Split(';')[1]); // second stage of splitting
        }

Here's a full working console example:



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

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string folders = @"30;c:\|48;c:\code|99;c:\Program Files";
            Dictionary<int, string> KeyValuePairs = GetFolders(folders);
            foreach (KeyValuePair<int, string> pair in KeyValuePairs)
            {
                Console.WriteLine("Key: '" + pair.Key + "', Value: '" + pair.Value + "'");
            }
            Console.ReadKey();

            /* Results:
             * 
             * Key: '30', Value: 'c:\'
             * Key: '48', Value: 'c:\code'
             * Key: '99', Value: 'c:\Program Files'
            */
        }

        public static Dictionary<int, string> GetFolders(string folders)
        {
            string[] x = folders.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); // first retrieve a string array of all the pairs

            return x.ToDictionary(y => Convert.ToInt32(y.Split(';')[0]), y => y.Split(';')[1]); // second stage of splitting
        }
    }
}

Wednesday, August 8, 2012

Convert enum to string and vice versa



            Vehicle myPreference = Vehicle.Bicycle;
            string vehicle = myPreference.ToString(); // returns string with value: "Bicycle"

            Vehicle secondChoice = (Vehicle)Enum.Parse(typeof(Vehicle), "Spaceship", true); // returns enum: Vehicle.Spaceship

Wednesday, June 20, 2012

Securing SharePoint lists by code

Rather than stuffing configuration keys into *.CONFIG files, it's more convenient to use SharePoint lists instead. This makes it easier to alter configuration settings, as for each change we would need to deploy a new .CONFIG file and involve SysAdmins, get approval from the management, et cetera.

But obviously, you do not want all users to have access to these, if you will, configuration lists. Manually setting all permissions per list and user group would be a nag. Therefore, I'm doing this by code, using the feature event receiver. I basically fetch the SPLists required, break the inheritance and re-set the permissions. The 'userGroup' variable holds the name of the user group associated with the feature. Same goes for the 'prefixList' variable.



public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            const string userGroup = "TeamGary";
            const string prefixList = "config.";

            using (SPSite site = (SPSite)properties.Feature.Parent)
            {
                using (SPWeb web = site.OpenWeb())
                {
                    List<string> fdtListTitles = new List<string>();

                    // get all feature-related SPList titles
                    foreach (SPList list in web.Lists)
                    {
                        if (list.Title.Substring(0, 7) == prefixList)
                        {
                            web.AllowUnsafeUpdates = true;

                            // break inheritance from parent
                            list.BreakRoleInheritance(false);

                            // remove all permissions from list, except for userGroup
                            SPGroupCollection groupCollection = list.ParentWeb.SiteGroups;
                            SPGroup group = groupCollection[userGroup];
                            SPRoleDefinitionCollection roleDefCollection = list.ParentWeb.RoleDefinitions;
                            SPRoleDefinition roleDefinition = roleDefCollection["Read"]; // set access level here
                            SPRoleAssignment roleAssignment = new SPRoleAssignment((SPPrincipal)group);

                            roleAssignment.RoleDefinitionBindings.Add(roleDefinition);

                            list.RoleAssignments.Add(roleAssignment);

                            list.Update();
                        }
                    }
                }
            }
        }

Friday, May 11, 2012

How to block the execution thread using RadConfirm

Finally found a way to use the RadConfirm popup, which is invoked from client side, in combination with server side events. Following example will display the confirmation window and only continues to call the button click event IF the OK button is clicked by the user.


ASPX:


<telerik:RadWindowManager ID="RadWindowManager1" runat="server" EnableShadow="true">
</telerik:RadWindowManager>

<telerik:RadCodeBlock ID="RadCodeBlock1" runat="server">
    <script type="text/javascript">

        var lastClickedItem = null;
        var clickCalledAfterRadprompt = false;
        var clickCalledAfterRadconfirm = false;

        function onClientButtonClicking(sender, args) {
            if (args.get_item().get_text() == "Order another beer") {
                if (!clickCalledAfterRadconfirm) {
                    args.set_cancel(true);
                    lastClickedItem = args.get_item();
                    radconfirm("Are you sure? You order it, you finish it!", confirmCallbackFunction);
                }
            }
        }

        function confirmCallbackFunction(args) {
            if (args) {
                clickCalledAfterRadconfirm = true;
                lastClickedItem.click();
            }
            else
            clickCalledAfterRadconfirm = false;
            lastClickedItem = null;
        }  

    </script>
</telerik:RadCodeBlock>

<CommandItemTemplate>
    <telerik:RadToolBar ID="RadToolBar1" runat="server" OnClientButtonClicking="onClientButtonClicking" OnButtonClick="RadToolBar1_ButtonClick" Width="100%">
        <CollapseAnimation Duration="200" Type="OutQuint" /> 
            <Items> 
                <telerik:RadToolBarButton CommandName="OrderBeer" runat="server" Text="Order another beer"></telerik:RadToolBarButton> 
            </Items> 
    </telerik:RadToolBar>
</CommandItemTemplate>

The CommandItemTemplate part you obviously need to add to your MasterTableView element..



CS:



protected void RadToolBar1_ButtonClick(object sender, RadToolBarEventArgs e)
    {
        RadToolBarButton button = e.Item as RadToolBarButton;
        if (button.CommandName == "OrderBeer")
        {
            \\Do stuff, drink the beer
            \\DrinkBeer();
        }
    }

Have a nice weekend folks!!!


Tuesday, April 10, 2012

Serverside RowClick event handling for RadGrid

Here are a few steps to handle an event on the server side for Telerik's RadGrid

1. Add attribute OnItemCommand to <Telerik:RadGrid>, for example "OnItemCommand="radGrid1_ItemCommand" if your grid name is radGrad1
2. Add attribute EnablePostBackOnRowClick="true" to <ClientSettings>
3. To your code behind, add the following, which will assign a specific cell value to a string:

protected void radGrid1_ItemCommand
{
    // this block will take the value of the column "ContactName" from the grid and assign it to a string
    if (e.CommandName == "RowClick")
    {
        int index = e.Item.ItemIndex;
        GridDataItem item = (GridDataItem)radGrid1.Items[index];
        string contactName = item["ContactName"].Text;
    }
}

Friday, March 9, 2012

Registering Telerik.Web.UI when used in a webpart

Quick post about another problem I stumbled across. My team decided to proceed using Telerik controls for our webparts. I ran the .msi and installing went just fine.

Also in VS, I immediately noticed the extended Toolbox with all the awesome new controls so I wanted to horse around with the RadGrid to see how it works.

So I created a new webpart in my project and drag-n-dropped the RadGrid control onto the web user controls. It added the following two lines of code:



And added the related assembly to the bin folder of my project and a reference.



But then BOOM! Big fat ass exception thrown; complaining that it cannot find the Telerik.Web.UI assembly



This is how to solve it.

First, go to your Package\Packge.package file, open it and go to Advanced (at the bottom). Next, click 'Add'->'Add existing assembly...'. Find the dll and then add a new item to the Safe Controls section, where you fill out the Namespace and the Assembly Name. Make sure that the Deployment Target is set to the GlobalAssemblyCache



Then, open the VS Command Prompt, and execute the following command line:

sn -T fullpathtoyourassembly



This wil give you the PublicKeyToken which you will use in a minute. Close the prompt and go to the folder where your dll is residing. Right-click it, choose properties -> Details and take note of the File version:


Finally, go back to the .ascx file and in the Register element, extend the Assembly attribute with the following:

<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI, Version=2011.3.1115.35, Culture=neutral, PublickKeyToken=121fae78165ba3d4" %>


That should make it work!

Enjoy ur weekend!

Namespaces and class declarations in a webpart

How it relates and stuff



Also, check out my new H+SON Formation Face wheelset I bought yesterday. BLB track hubs and fyxation tires. Still need to change my gear ratio, currently running at a lame 42 - 16

Monday, February 27, 2012

Using LINQ to order an ASP.NET DropDownList

How LINQ can you save you from writing many lines of code. Easy stuff:

protected void SortDropDownList(DropDownList ddlToSort)
{
    ddlToSort.DataSource = ddlToSort.Items.Cast<ListItem>()
                            .OrderBy(o => o.Text)
                            .ToList();
    ddlToSort.DataBind();
}

Wednesday, February 22, 2012

Passing on ViewState Items onto ObjectDataSource

Because the datasource instance does not run in your page instance, you will not be able to fetch your ViewState items. The only way to do this is to pass it on to the parameters. Suppose you've got a gridview, using an object data source instance called 'odsBand' and you would like to retrieve a DataSet from your db filtered by band name. In this case you need to define a electParameter which will hold the band name retrieved from your ViewState. See markup below

<asp:ObjectDataSource ID="odsBands" runat="server" 
    SelectMethod="SelectBands" 
    OnSelecting="odsBands_Selecting"
    OnSelected="odsBands_Selected"
    OnFiltering="odsBands_Filtering"
    &lt;SelectParameters>
        <asp:Parameter Name="BandName" Type="String" />
</SelectParameters>
</asp:ObjectDataSource>

Here I set up the BandName property which will chuck the request band name string into the ViewState:

private string BandName
{
    get
    {
        return ViewState["BandName"] == null ? "" : ((string)ViewState["BandName"]);
    }
    set
    {
        ViewState["BandName"] = value;
    }
}

In the Page_Load method, I'm putting the QueryString into the Viewstate, using the BandName property:

protected void Page_Load(object sender, EventArgs e)
{
    odsBands.TypeName = this.GetType().AssemblyQualifiedName;
    
    if (!Page.IsPostBack)
    {
        BandName = Request.QueryString["bandName"];
        // This is my SPGridView instance which will display the data
        grdBand.DataSourceID = "odsBands";
    }
}

Ok cool. From here I use the property to set the input parameter 'BandName' like this:

protected void odsBands_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
    e.InputParameters["BandName"] = BandName;
}

Finally, I define the SelectMethod 'SelectBands' with one string argument which is the input parameter. In this particular case I'm using a DataSet from which I then create a DataView to filter on the parameter value:

public DataView SelectBands(string bandName)
{
    // The method below fetches the data from a db, replace this with your own source
    DataSet dsBands = BandsResourceManagement.Instance.GetBands();
    
    // BandName is also the field name in the table and I'm sorting the records by the field BandId
    DataView dvBand = new DataView(dsBands.Tables[0],"BandName = '" + bandName + "', "BandId", DataViewRowState.CurrentRows);
}

Tuesday, January 10, 2012

Connecting VS2008 to TFS2010

What a hassle. My virtual machine crashed today and it got reset to the last snapshot, which had been taken like somewhere in 1957. So I've practically wasted all day on reconfiguring all the crap that I had installed after the snapshot was taken.

One of the things I spent a fuck load of time on was getting VS2008 connecting to TFS2010. Finally, got it to work. This is how:

1. I installed VS2008
2. Then downloaded and installed Microsoft Visual Studio 2008 Service Pack 1 (Installer), get it here
3. Then, downloaded and installed Visual Studio Team System 2008 Service Pack 1 Forward Compatibility Update for Team Foundation Server 2010 (Installer), get it here
4. Checked in VS2008 if it installed correctly

Finally, I drilled down into the registry, to "HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\TeamFoundation\Servers" (you should be able to find it, if not make sure you installed the earlier mentioned stuff in the correct order).

In here, I added a string value where the value name can be anything and the value data field should contain the full address of your server.

From here, you should be able to go to the Team Explorer window in VS and add the team project. That's it.

Friday, January 6, 2012

Note2Self: Configuring web service for web application usage

Entries to web.config in WSS/VirtualDirectories/80 to configure webservices
Steps:

1 Add 'endpoint' element to 'client':
<client>
   <endpoint address="http://localhost/YourService/ServicesYou.asmx"
             binding="basicHttpBinding"
             bindingConfiguration="###MyCustomBindingName1###"
             contract="###DefinedConfigurationName###"
             name="###stringEndPointConfigurationName###" />
</client>
###MyCustomBindingName1### -- is a reference to what will be added to the system.serviceModel/bindings/basicHttpBinding/ element. Preferably, prefix it with the binding type (i.e. in this case: "basicHttpBinding_MyCustomBindingName1")

###DefinedConfigurationName### -- should be the value that you defined in the Reference.CS file. To be more specific in the attribute before the interface definition:
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://WebServices/", ConfigurationName = "###DefinedConfigurationName###")]    
2 Create binding element
        <binding name="###MyCustomBindingName1###" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="2147483647" maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="None">
            <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>
I changed the maxReceivedMessageSize and maxBufferSize to the max. allowed size since I had trouble retrieving large DataTables.