Stefan Pienaar
I would love to change the world, but they won't give me the source code

Creating a CRM Javascript function which is accessable to all events on a form

June 26, 2009 11:56 by StefanPienaar

There might be situations where you’ll need to perform the same actions/calculations (via javascript) on different form events. Code duplication is always a bad idea so you could either include an external javascript file with your function or create the function in your form’s load event as follows:

CalculateAge = function()
{
  if(crmForm.all.new_dateofbirth.DataValue != null)
  {
     // Your javascript logic
  }
}

You can then call this function from any event on the same form. For example I could call this function right after creating it on the load event to ensure the age is always updated when the record is opened, and I can also call it from my date of birth field’s change event. To call the event, I would use this line:
 
CalculateAge();

Categories: Microsoft CRM
Actions: E-mail | Permalink | Comments (2)

Changing the value of a Lookup field in CRM

June 26, 2009 11:46 by stefanpienaar

You can change the value of a Lookup Field on a CRM form (for example the owner or the selected currency) by using the following piece of javascript:

var lookupObject = new Object;
var lookupObjectArray = new Array();
lookupObject.id = '<Guid of the target record>';
lookupObject.name = '<Name of the target record>';
lookupObject.typename = "<target record type>";
lookupObjectArray[0] = lookupObject;
crmForm.all.<target field>.DataValue = lookupObjectArray;
crmForm.all.<target field>.ForceSubmit = true;


Just make sure to change the values between < and > to suit your own situation. For example if I wanted to change the default currency I would change the code to the following:

var lookupObject = new Object;
var lookupObjectArray = new Array();
lookupObject.id = '{EF63EA80-02BE-DD11-8241-0017A410DDDA}';    // Rand's Guid
lookupObject.name = 'Rand';                                    // Name of currency
lookupObject.typename = "transactioncurrency";                 // Lookup's type
lookupObjectArray[0] = lookupObject;
crmForm.all.transactioncurrencyid.DataValue = lookupObjectArray;
crmForm.all.transactioncurrencyid.ForceSubmit = true;


Note that the Guid used above will be unique per installation so yours won’t necessarily match mine.The last line just ensures that the record is updated correctly if my Lookup field is disabled on the form.


Categories: Microsoft CRM
Actions: E-mail | Permalink | Comments (0)

Changing regional settings in an ASP.net Website

June 25, 2009 13:52 by stefanpienaar

One of the ways in which the regional settings applicable to a website can be changed is by using the Session.LCID property. This is especially useful if you do not have direct access to the server where your website resides. Changing this property will change the way variables are formatted (for example dates and currency).

To change the regional settings to South Africa, add this line to your page load event:

Session.LCID = 7177;


Currency values will now be prefixed with a “R” instead of the default “$”.

Unfortunately it doesn’t seem like this setting is preserved throughout postbacks (contrary to what you would expect of the Session object) so I would recommend adding it to your Master Page’s page load event.

Here’s a list of available codes and their respective languages:

1078 Afrikaans 1052 Albanian
5121 Arabic(Algeria) 15361 Arabic(Bahrain)
3073 Arabic(Egypt) 2049 Arabic(Iraq)
11265 Arabic(Jordan) 13313 Arabic(Kuwait)
12289 Arabic(Lebanon) 4097 Arabic(Libya)
6145 Arabic(Morocco) 8193 Arabic(Oman)
16385 Arabic(Qatar) >1025 Arabic(Saudi Arabia)
10241 Arabic(Syria) 7169 Arabic(Tunisia)
14337 Arabic(U.A.E.) 9217 Arabic(Yemen)
1069 Basque 1059 Belarusian
1026 Bulgarian 1027 Catalan
3076 Chinese(Hong Kong) 2052 Chinese(PRC)
4100 Chinese(Singapore) 1028 Chinese(Taiwan)
1050 Croatian 1029 Czech
1030 Danish 2067 Dutch(Belgian)
1043 Dutch(Standard) 9 English
3081 English(Australian) 10249 English(Belize)
2057 English(British) 4105 English(Canadian)
9225 English(Caribbean) 6153 English(Ireland)
8201 English(Jamaica) 5129 English(New Zealand)
7177 English(South Africa) 11273 English(Trinidad)
1033 English(United States) 1061 Estonian
1080 Faeroese 1065 Farsi
1035 Finnish 2060 French(Belgian)
3084 French(Canadian) 5132 French(Luxembourg)
1036 French(Standard) 4108 French(Swiss)
2108 Gaelic(Irish) 1084 Gaelic(Scots)
3079 German(Austrian) 5127 German(Liechtenstein)
4103 German(Luxembourg) 1031 German(Standard)
2055 German(Swiss) 1032 Greek
1037 Hebrew 1081 Hindi
1038 Hungarian 1039 Icelandic
1057 Indonesian 1040 Italian(Standard)
2064 Italian(Swiss) 1041 Japanese
1042 Korean 2066 Korean(Johab)
1062 Latvian 1063 Lithuanian
1071 Macedonian 1086 Malaysian
1082 Maltese 1044 Norwegian(Bokmal)
2068 Norwegian(Nynorsk) 1045 Polish
1046 Portuguese(Brazilian) 2070 Portuguese(Standard)
1047 Rhaeto-Romanic 1048 Romanian
2072 Romanian(Moldavia) 1049 Russian
2073 Russian(Moldavia) 1083 Sami(Lappish)
3098 Serbian(Cyrillic) 2074 Serbian(Latin)
1051 Slovak 1060 Slovenian
1070 Sorbian 11274 Spanish(Argentina)
16394 Spanish(Bolivia) 13322 Spanish(Chile)
9226 Spanish(Colombia) 5130 Spanish(Costa Rica)
7178 Spanish(Dominican Republic) 12298 Spanish(Ecuador)
17418 Spanish(El Salvador) 4106 Spanish(Guatemala)
18442 Spanish(Honduras) 2058 Spanish(Mexican)
19466 Spanish(Nicaragua) 6154 Spanish(Panama)
15370 Spanish(Paraguay) 10250 Spanish(Peru)
20490 Spanish(Puerto Rico) 3082 Spanish,Spain-Modern Sort
1034 Spanish(Spain - Traditional Sort) 14346 Spanish(Uruguay)
8202 Spanish(Venezuela) 1072 Sutu
1053 Swedish 2077 Swedish(Finland)
1054 Thai 1073 Tsonga
1074 Tswana 1055 Turkish
1058 Ukrainian 1056 Urdu
1075 Venda 1066 Vietnamese
1076 Xhosa 1085 Yiddish
1077 Zulu 2048 default

source: http://www.123hostnow.com/articles/LCID.asp


Categories: .net
Actions: E-mail | Permalink | Comments (0)

Search through all SQL tables for string value

April 23, 2009 13:00 by stefanpienaar

Found this cool stored procedure on this site. What it does, is search through each text column in your database for a specified string value.

CREATE PROC SearchAllTables
(
    @SearchStr nvarchar(100)
)
AS
BEGIN
 
    -- Copyright © 2002 Narayana Vyas Kondreddi. All rights reserved.
    -- Purpose: To search all columns of all tables for a given search string
    -- Written by: Narayana Vyas Kondreddi
    -- Site: http://vyaskn.tripod.com
    -- Tested on: SQL Server 7.0 and SQL Server 2000
    -- Date modified: 28th July 2002 22:50 GMT
 
 
    CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))
 
    SET NOCOUNT ON
 
    DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
    SET  @TableName = ''
    SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')
 
    WHILE @TableName IS NOT NULL
    BEGIN
        SET @ColumnName = ''
        SET @TableName = 
        (
            SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
            FROM     INFORMATION_SCHEMA.TABLES
            WHERE         TABLE_TYPE = 'BASE TABLE'
                AND    QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
                AND    OBJECTPROPERTY(
                        OBJECT_ID(
                            QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
                             ), 'IsMSShipped'
                               ) = 0
        )
 
        WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)
        BEGIN
            SET @ColumnName =
            (
                SELECT MIN(QUOTENAME(COLUMN_NAME))
                FROM     INFORMATION_SCHEMA.COLUMNS
                WHERE         TABLE_SCHEMA    = PARSENAME(@TableName, 2)
                    AND    TABLE_NAME    = PARSENAME(@TableName, 1)
                    AND    DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
                    AND    QUOTENAME(COLUMN_NAME) > @ColumnName
            )
    
            IF @ColumnName IS NOT NULL
            BEGIN
                INSERT INTO #Results
                EXEC
                (
                    'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
                    FROM ' + @TableName + ' (NOLOCK) ' +
                    ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
                )
            END
        END    
    END
 
    SELECT ColumnName, ColumnValue FROM #Results
END


Categories: SQL
Actions: E-mail | Permalink | Comments (0)

Getting meaningful error messages from CRM’s Web Service

April 16, 2009 22:43 by stefanpienaar

When I first started working with CRM’s web service, I was really frustrated by the lack of a detailed error message when something went wrong, turns out there is an easy way to always get a descriptive error.

I used to only catch a generic Exception and most of the time the only error message you’ll get from that is “Server was unable to process request”. In the example below I’m creating a phone call activity and assigning it to a system user.

image_thumb1

However, when you catch a SoapException (found in the System.Web.Services.Protocols namespace) instead, you can view the SoapException’s Detail Property which has an InnerXml property.

image_thumb4

Ahhhhh, I was using a Guid for a system user which doesn’t exists… *facepalm*


Categories: Microsoft CRM
Actions: E-mail | Permalink | Comments (0)

SharePoint, Checking in all documents in a document library

March 20, 2009 11:20 by stefanpienaar

Manually checking in a bunch of files in a SharePoint Document Library is a mission. Create a new console application and paste the code below in your Main method:

* You will need to reference Microsoft.SharePoint.dll which can be found on the server where SharePoint is installed.

** Just remember to change the hardcoded site name (“http://myserver/”) and document library name (“MyDocumentLibrary”) to reflect your own settings.

using (SPSite site = new SPSite("http://myserver/"))
{
    SPWeb web = site.OpenWeb();
 
    foreach (SPList list in web.Lists)
    {
        if (String.Compare(list.Title, "MyDocumentLibrary", true) == 0)
        {
            SPDocumentLibrary docLib = (SPDocumentLibrary)list;
            // Take ownership of all checked out files in the document library
            foreach (SPCheckedOutFile checkedFile in docLib.CheckedOutFiles)
            {
                Console.WriteLine("Taking ownership of " + checkedFile.Url);
                checkedFile.TakeOverCheckOut();
            }
 
            // Check in all checked out files
            foreach (SPListItem item in list.Items)
            {
                SPFile file = null;
                try
                {
                    file = item.File;
 
                    if (file != null)
                    {
                        if (file.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                        {
                            file.CheckIn("Checked in after uploading");
                            file.Update();
                            Console.WriteLine("File checked-in = " + file.Name);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not check-in file = " +
                                        file.Name + " : " + ex.Message);
                }
            }
        }
    }
}
 
Console.WriteLine("All documents checked in, press enter to close this application.");
Console.ReadLine();

Categories: SharePoint | .net
Actions: E-mail | Permalink | Comments (0)

ASP.net MVC 1.0

March 19, 2009 10:22 by stefanpienaar

MVC 1.0 has just been released, download it here.

Since it’s now considered stable and ready for production, I no longer have an excuse to not get my hands dirty :)


Categories: .net
Actions: E-mail | Permalink | Comments (0)

Performing a Cascade Delete

March 17, 2009 16:46 by stefanpienaar

Found a neat stored procedure on the SqlTeam.com site which automatically deletes child records when a parent record is deleted.

This is useful when you haven’t or don’t want to create your tables with the “ON DELETE CASCADE” option.

CREATE Procedure spDeleteRows
    (
    @cTableName varchar(50), /* name of the table where rows are to be deleted */
    @cCriteria nvarchar(1000), /* criteria used to delete the rows required */
    @iRowsAffected int OUTPUT /* number of records affected by the delete */
    )
As
set nocount on
declare     @cTab varchar(255), /* name of the child table */
    @cCol varchar(255), /* name of the linking field on the child table */
    @cRefTab varchar(255), /* name of the parent table */
    @cRefCol varchar(255), /* name of the linking field in the parent table */
    @cFKName varchar(255), /* name of the foreign key */
    @cSQL nvarchar(1000), /* query string passed to the sp_ExecuteSQL procedure */
    @cChildCriteria nvarchar(1000), /* criteria to be used to delete 
                                           records from the child table */
    @iChildRows int /* number of rows deleted from the child table */
 
/* declare the cursor containing the foreign key constraint information */
DECLARE cFKey CURSOR LOCAL FOR 
SELECT SO1.name AS Tab, 
       SC1.name AS Col, 
       SO2.name AS RefTab, 
       SC2.name AS RefCol, 
       FO.name AS FKName
FROM dbo.sysforeignkeys FK  
INNER JOIN dbo.syscolumns SC1 ON FK.fkeyid = SC1.id 
                              AND FK.fkey = SC1.colid 
INNER JOIN dbo.syscolumns SC2 ON FK.rkeyid = SC2.id 
                              AND FK.rkey = SC2.colid 
INNER JOIN dbo.sysobjects SO1 ON FK.fkeyid = SO1.id 
INNER JOIN dbo.sysobjects SO2 ON FK.rkeyid = SO2.id 
INNER JOIN dbo.sysobjects FO ON FK.constid = FO.id
WHERE SO2.Name = @cTableName
 
OPEN cFKey
FETCH NEXT FROM cFKey INTO @cTab, @cCol, @cRefTab, @cRefCol, @cFKName
WHILE @@FETCH_STATUS = 0
     BEGIN
    /* build the criteria to delete rows from the child table. As it uses the 
           criteria passed to this procedure, it gets progressively larger with 
           recursive calls */
    SET @cChildCriteria = @cCol + ' in (SELECT [' + @cRefCol + '] FROM [' + 
                              @cRefTab +'] WHERE ' + @cCriteria + ')'
    print 'Deleting records from table ' + @cTab
    /* call this procedure to delete the child rows */
    EXEC spDeleteRows @cTab, @cChildCriteria, @iChildRows OUTPUT
    FETCH NEXT FROM cFKey INTO @cTab, @cCol, @cRefTab, @cRefCol, @cFKName
     END
Close cFKey
DeAllocate cFKey
/* finally delete the rows from this table and display the rows affected  */
SET @cSQL = 'DELETE FROM [' + @cTableName + '] WHERE ' + @cCriteria
print @cSQL
EXEC sp_ExecuteSQL @cSQL
print 'Deleted ' + CONVERT(varchar, @@ROWCOUNT) + ' records from table ' + @cTableName

You can then call the procedure like this:

exec spDeleteRows 'X', 'field1 = ''234''', 0


The above means you want to delete from the ‘X’ table where ‘field1’ is equal to ‘234’.
DELETE FROM X WHERE field1 = ‘234’


Categories: SQL
Actions: E-mail | Permalink | Comments (0)

ASP.net and the Canonical Tag

March 10, 2009 18:50 by stefanpienaar

The major role players in the Search Engine world (Google, Yahoo and Microsoft) recently announced support for the canonical tag. What this means is you now have a way to *hint* the crawler to the best version of the current page.

Because “http://www.stefanpienaar.co.za”, “http://stefanpienaar.co.za” and “http://www.stefanpienaar.co.za/Default.aspx” are seen as different pages (with duplicate data) by search engine crawlers, we can now add the canonical tag to a page’s head tag to indicate which one should be used.

For example for my blog I could add the following tag between my opening and closing head tags:

<link href="http://www.stefanpienaar.co.za" rel="canonical" />

This way even if someone incorrectly links to “http://stefanpienaar.co.za”, the search crawler will now know that they are in fact the same page and which one I prefer.

To get your asp.net page to work with the canonical tag is really simple:

1. Change your <head> tag to include runat=”server” if it doesn’t already.

<head runat="server">
    <title>My Site</title>
</head>

 

2. From your page load event, create a new HtmlLink object and add it to the Page’s header:

HtmlLink lnkCanonical = new HtmlLink();
lnkCanonical.Href = "http://www.stefanpienaar.co.za";
lnkCanonical.Attributes["rel"] = "canonical";
Page.Header.Controls.Add(lnkCanonical);

Categories: .net
Actions: E-mail | Permalink | Comments (0)

Setting the focus to a specific tab in CRM

March 3, 2009 12:51 by stefanpienaar

Interesting tip I found on Ronald Lemmen’s blog. When you want to make a tab which isn’t the first tab on a form active programmatically, all you need to do is:

crmForm.all.tab1Tab.click();

 

In this case it will activate the 2nd tab on the form.


Categories: Microsoft CRM
Actions: E-mail | Permalink | Comments (1)