05 December 2007

How to setting focus to a Tab?

There are many discusses in CRM Forum, about how to setting focus to a Tab?
For example, I want the default tab1Tab to be the default tab when I open a record.

So if you put this code in onLoad() event:

document.getElementById('tab1Tab').focus();

It doesn't work as you expected.

But, if you use:

crmForm.all.tab1Tab.click();

It will work like a dream. :)

28 November 2007

How to change the width of Queue Title column?

QueueItem and its Views are not customizable in CRM v3.0, one requirement was make the Title column wider then users can see more information. (The Title column contains subject of activities/cases)
By default, Title column width = 300px, so how to change it to 500px? Here's my soluiton:

/*
../workplace/home_workplace.aspx
Make the queue title column width = 500px
*/

//1. Edit CRMWeb/workplace/home_workplace.aspx, add a new JavaScript function call: titleWidth()
function titleWidth()
{
var barCols = document.getElementById("crmGrid_gridBarCols");
barCols.getElementsByTagName("COL")[2].width = 500;
crmGrid.Refresh();
}

//2. Call this function from the existing nodeSelect() and window.onload().
function nodeSelect( sQueueId, sViewId, sMenuId )
{
......
titleWidth();
}

function window.onload()
{
......
titleWidth();
}

20 November 2007

Hide/remove/move/change entity level tags at runtime


/* Removing Sub-Account navigation bar at runtime */
if(document.all.navSubAct != null)
{
navSubAct.style.display = 'none';
}

/* Hiding Service Tag at runtime */
if(document.all._NA_CS != null)
{
document.all._NA_CS.style.display = 'none';
}

/* Moving Case under details group at runtime */
if((document.all.navService != null) && (document.all._NA_Info != null))
{
document.all._NA_Info.appendChild(navService);
}

/* Changing group name from 'Sales' to 'Management' */
if (document.all._NA_SFA != null)
{
document.getElementById("_NA_SFA").innerHTML = document.getElementById("_NA_SFA").innerHTML.replace("Sales","Management");
}

14 November 2007

Contact Quick Find: a bug?

In my environment, I have 'fullname', 'lastname', 'firstname' as the Contact 'Quick Find Columns'.
I noticed that the 'quick find' only search contacts from the current result view, not all records(which it should do). To work around this problem, you can edit '\_common\scripts\stage.js'


crmGrid.Reset(); // add this line here

if (crmGrid.GetParameter("viewid") != SavedQuerySelector.quickFindQuery)
{
crmGrid.SetParameter("viewid", SavedQuerySelector.quickFindQuery);
crmGrid.Reset();
}
else
{
crmGrid.PageNumber = 1;
}


To test it, you must firstly delete all Temporary Internet Files on Internet Explorer Options.

11 November 2007

CRM how to hide field / label / line / section / tab


/*hide field only*/
crmForm.all.field.style.display = 'none';

/*hide field and this field's label*/
crmForm.all.field.style.display = 'none';
crmForm.all.field_c.style.display = 'none';

/*hide field and the whole line which contains this field*/
crmForm.all.field.parentElement.parentElement.style.display = 'none';

/*hide field and the section which contains this field*/
crmForm.all.field.parentElement.parentElement.parentElement.style.display = 'none';

/*hide a tab(tab number comes from 0)*/
crmForm.all.tab2Tab.style.display = 'none';

08 November 2007

How to disable / readOnly CRM fields / iframes

Sometimes we need to disable all fields in CRM, so a function could help!
Also, you don't want to diable INPUT/TEXTAREA nodes, because it will become unreadble (gray #808080) if you disable it, so I prefer to set those nodes readOnly. (only INPUT and TEXTAREA support readOnly property!)


/*
setDisabled function
1. set readOnly property for INPUT/TEXTAREA nodes
2. disable other nodes
id: element's Id
ignoreNodes: nodes can be ignored
nodesDisabled: bool, true = set disable/readonly
*/

function setDisabled(id, ignoreNodes, nodesDisabled)
{
var node, nodes;
nodes = id.getElementsByTagName('*');
if(!nodes)
return;

var i = nodes.length;
while (i--)
{
node = nodes[i];
if(node.nodeName && !(node.nodeName.toLowerCase() in ignoreNodes))
{
if((node.nodeName == "INPUT") || (node.nodeName == "TEXTAREA"))
{
node.readOnly = nodesDisabled;
}
else
{
node.disabled = nodesDisabled;
}
}
}
}

/*disable/readonly fields*/
setDisabled(document.getElementById("areaForm"), {table:'', iframe:'', div:'', form:'', col:'', colgroup:'', lable:'', span:'', tbody:'', body:'', tr:'', td:''}, true);

/*disable IFRAME*/
try
{
window.setTimeout(iframeDisabled, 3000);
}
catch(err)
{
alert("System busy, please try again later!" + err);
window.close();
}

function iframeDisabled()
{
setDisabled(document.frames("IFRAME_1").document.getElementById("mnuBar1"),{}, true);
}

05 November 2007

Microsoft CRM: Filter On: Last 30 days

I have been asked how to change the default Filter On from 'Last 30 days' to 'All' in History associated view.

Micrsoft CRM doesn't provide this customisation, and there are no good solutions on the Internet. After 3 hours research for the source, I found a simple solution:
The file you need to modify is: \CRMWeb\_controls\AppGridFilterContainer\AppGridFilterContainer.htc

Open the file by notepad, and search the string : oCallback(oCtrl);
Just add some code above it:


if(oCtrl.DataValue=="LastXDays;30")
{
oCtrl.DataValue = "All";
RefreshGridView();
}

oCallback(oCtrl);


Save and Close, that's it. Next time when you open History, you will see the change.

27 October 2007

Automatically resolve e-mail sender to selected contact emailaddress1. Part II

OK, then what's the next problem?

The next problem was: If you have a contact(senderA) in CRM, the senderA has 2 email address saved in emailaddress1 and emailaddress2 fields. If senderA send an email from ouside world into CRM by using emailaddress2, then CRM will think that is a associated email address, it's good so far. But when CRM user reply this email to senderA, CRM will always use emailaddress1 to send email! Which is not good because senderA sent this email by emailaddress2, he/she doesn't expect the reply email send into his/her another email box.

So this is the requirement: make emailaddress1 dynamically, so senderA's emailaddress1 will always keep the latest email address he/she used. Because CRM Email router doesn't fire Email Pre/Post Create callout, so I decide to use Workflow assembly to make it happen. Here's the code, you still need to update workflow.xml and set up a workflow Email.Create job.


/*
* Update contact.emailaddress1 by email sender
*
* */

public void GetEmailSender(Guid activityid, String sender)
{
CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
string connectionString = "Server=_db;Database=_MSCRM;Integrated Security=SSPI";

//only select a contact(partyobjecttypecode=2) which is also a sender(participationtypemask=1)
string queryString = "SELECT partyid FROM FilteredActivityParty "
+ "WHERE(participationtypemask = 1) AND "
+ "(partyobjecttypecode = 2) AND "
+ "(activityid = '" + activityid.ToString()
+ "')";

SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand(queryString, connection);
command.Connection.Open();

try
{
SqlDataReader reader = command.ExecuteReader();
Guid cid = new Guid();

while(reader.Read())
{
cid = reader.GetGuid(0); // get FilteredActivityParty.partyid
}
if(cid.ToString() != "00000000-0000-0000-0000-000000000000")
{
contact c = new contact();
c.contactid = new Key();
c.contactid.Value = cid;
c.emailaddress1 = sender;
service.Update(c);
}
}
catch (Exception ex)
{
TextWriter log = TextWriter.Synchronized(File.AppendText(@"C:\CRM_Debug\error.txt"));
log.WriteLine(DateTime.Now);
log.WriteLine(ex.ToString());
log.WriteLine("");
log.Close();
}
finally
{
connection.Close();
}
}

20 October 2007

Automatically resolve e-mail sender to selected contact emailaddress1. Part I

We faced two problems in CRM 3.0 Email, the first problem was:
When an email(senderA@example.com) sent from outside world into CRM system, if sender's email address isn't in the system, then CRM will ask you to associate it with an existing record(only associate with a Contact record in our case). But once you select a contact, it looks associated, but it doesn't update the Contact's email field. You have to open the Contact record and manually copy the sender's email address into Contact's email field.

Regarding Microsoft KB: 922116, it is by design! Our requirement is make it automatically copy the sender's email address into the assocaited Contact.emailaddress1 field.

To achieve that, I made an unsupported change(again? :))


/*
Automatically resolve e-mail sender to selected contact emailaddress1
Microsoft KB: 922116, http://support.microsoft.com/kb/922116
\_controls\PartyList\resolve.aspx
*/

function applychanges()
{
//update cotnact emailaddress1
if((document.getElementById("crmExistingLookup").DataValue != null)
&&(document.getElementById("crmExistingLookup").DataValue[0] != null)
&&(document.getElementById("crmExistingLookup").DataValue[0].type == 2))
{
var eml = document.getElementById("txtName").value;
var cid = document.getElementById("crmExistingLookup").DataValue[0].id;
var connection = new ActiveXObject("ADODB.Connection");
var connectionString = "Provider=SQLOLEDB; Server=_db; Database=_mscrm; Integrated Security=SSPI";

connection.Open(connectionString);
var sql = "UPDATE FilteredContact SET emailaddress1='" + eml +"'WHERE contactid = '" + cid + "'" ;
rs = new ActiveXObject("ADODB.Recordset");
rs.Open(sql, connection, 1, 2);

connection.Close();

}

....

}

09 September 2007

try{} cacth{} ,make it useful!

Microsoft CRM Platform will only throw SoapException whose message property is always "Server was unable to process request.". So we definitely need it in our catch{} try{} .
Plus, if we want to catch SqlException, we need catch it as well. Or to use a common Exception to catch everything!


catch (System.Web.Services.Protocols.SoapException ex)
{
TextWriter log = TextWriter.Synchronized(File.AppendText(@"C:\CRM_Debug\error.txt"));
log.WriteLine("SoapException: " + DateTime.Now.ToString());
log.WriteLine("Error Message: " + ex.Detail.InnerText);
XmlDocument error = new XmlDocument();
error.LoadXml(ex.Detail.InnerXml);
log.WriteLine("Error Code: " + error.SelectSingleNode("/error/code").InnerText);
log.WriteLine("Error Description: " + error.SelectSingleNode("/error/description").InnerText);
log.WriteLine("Error Type: " + error.SelectSingleNode("/error/type").InnerText);
log.WriteLine();
log.Close();
}
catch (SqlException ex)
{
//
}
catch (Exception ex)
{
//
}

21 August 2007

Troubleshooting Callout

1. Grant the account that is running the CRMAppPool application pool the Full Control permission for the following folder:
C:\Program Files\Microsoft CRM\Server\bin\assembly

2. Change the SetupMode registry subkey too.
HKEY_LOCAL_MACHINE\Software\Microsoft\MSCRM

3. Re-add CRMServices web service from Visual Studio.Net

4. Set output to file system, for example: PostUpdate

TextWriter log = TextWriter.Synchronized( File.AppendText @"C:\CRM_SDK_Drop\Update.txt"));
log.WriteLine("PostUpdate");
log.WriteLine("ObjectType: " + entityContext.EntityTypeCode.ToString());
log.WriteLine("ObjectId: " + entityContext.InstanceId.ToString());
log.WriteLine("CreatorId: " + userContext.UserId.ToString());
log.WriteLine();
log.Close();


5. Debug:
Callout assembly: w3wp.exe, (don't forget PDB file)
Workflow assembly: CrmWorkflowService.exe , (don't forget PDB file)
Also, see these useful links:
http://blog.sonomapartners.com/2007/03/is_your_microso.html
http://ronaldlemmen.blogspot.com/2007/01/callout-not-working.html
http://support.microsoft.com/kb/933842

28 July 2007

How to allow '&' in email address

CRM 3.0 doesn't allow '&' in email address ,however, it's a requirement to make it possible!
The file you need to change is located in: \_forms\controls\INPUT.text.eml.htc,
The idea is change the regular expression for email field:

/*
Allow '&' in email address
Need to edit htc file(HTML Components)
\_forms\controls\INPUT.text.eml.htc
*/
//var _validEmailRegexp = /^\w([-._'\w]*\w)?@\w([-._\w]*\w)?\.\w+$/;
var _validEmailRegexp = /^\w([&-._'\w]*\w)?@\w([-._\w]*\w)?\.\w+$/;

14 July 2007

Always show address bar in Internet Explorer

Sometimes we need to customise .aspx page in CRM (unsupported way). So it's very useful if you could show IE address bar at All time. Actually it's very easy to do:

1. Open Internet Explorer, go to [Tools] -> [Internet Options] -> [Security], select a site which contain your CRM website.

2. Click Custom level, under section "Miscellaneous", disable this option: "Allow websites to open windows without address or status bars", and restart IE.

20 May 2007

Integrate CRM with WSS 3.0 Picture Library

I worked for a project which needs integrate CRM with WSS 3.0 picture library.
The requirement was: Each account has it's own picture library in WSS, so the pictures could be use in CRM report etc.
Here's the code:


public partial class _Default : System.Web.UI.Page
{
string accountGUID = "";
protected void Page_Load(object sender, EventArgs e)
{
string entityId = Request.QueryString["oId"];
Guid accountId = (entityId == null) ? (new Guid("00000000-0000-0000-0000-000000000000")) : new Guid(entityId);
accountGUID = accountId.ToString();
string url = "http://WSS:6666/PL/" + accountGUID; // PL is a picture library in WSS 3.0

if (CheckUrl(url))
{
Response.Redirect(url);
}
else
{
Label1.Text = "This account doesn't has a Pictures Library, please create a one for it.";
}
}

public static bool CheckUrl(string url)
{
//check if the url(pictures library) is existing
HttpWebResponse httpResponse = null;

try
{
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
httpResponse = (HttpWebResponse)httpRequest.GetResponse();
return (httpResponse.StatusCode == System.Net.HttpStatusCode.OK);
}
catch (Exception ex)
{
return false;
}
}

protected void Button1_Click(object sender, EventArgs e)
{
//Response.Redirect("http://WSS:6666/PL/Forms/Upload.aspx?Type=1");
SPSite Site = new SPSite("http://WSS:6666"); //site url
SPWeb Web = Site.OpenWeb();
Site.AllowUnsafeUpdates = true;
Web.AllowUnsafeUpdates = true;
SPFolder rootFolder = Web.GetFolder("PL"); // PL is a picture library in WSS 3.0
rootFolder.SubFolders.Add(accountGUID);
Page_Load(null, null);
}
}

05 May 2007

Accessing CRM database from JScript

Here's the code how to accesss CRM database via JScript!


//initial connection
var connection = new ActiveXObject("ADODB.Connection");
var connectionString = "Provider=SQLOLEDB;Server=_db;Database=_mscrm;Integrated Security=sspi";
//open connection
connection.Open(connectionString);
//query string
var query = "SELECT name FROM FilteredAccount";
//create a ADO object
var rs = new ActiveXObject("ADODB.Recordset");
//open connection
rs.Open(query, connection, 1, 2);
rs.moveFirst();
var values = "";
//read data
while (!rs.eof)
{
values += rs.Fields(0).Value.toString() + " ";
rs.moveNext();
}
//close connection
connection.Close();
//alert value
alert(values);

28 April 2007

Age calculate by Birthday


if(crmForm.all.birthdate.DataValue != null)
{
var now = new Date();
var birthday = crmForm.all.birthdate.DataValue;
var monthdif = now.getMonth() - birthday.getMonth();
if(monthdif > -1)
crmForm.all.new_age.DataValue = now.getYear() - birthday.getYear();
else
crmForm.all.new_age.DataValue = now.getYear() - birthday.getYear() - 1;
}

21 April 2007

Uninstall WSS 3.0 MICROSOFT##SSEE Database

Sometimes we need to uninstall WSS 3.0 database manually, here is the way:
1. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
2. Look for each {GUID} which Display Name = "Microsoft SQL 2005 Embedded Edition"
3. Run: MsiExec.exe /X {GUID} CALLERID=OCSETUP.EXE

09 April 2007

WORD TIPS 1 – Find and Replace

History: XXXX

History 02): XXXX

History 03): XXXX


Use Find and Replace (Use wildcards):

Find what: ^13History ([0-9][0-9])

Replace with: , \1

History: XXXX , 02): XXXX , 03): XXXX

05 April 2007

EXCEL Tips 2

'Macro to split cells at the comma

Sub SplitName()
Dim cell As Range
Dim separator As Integer
'loop through each cell in the selection
For Each cell In Selection
'search for a comma
separator = InStr(cell.Value, ",")
If separator > 0 Then
'put the lastname in the first column to the right
cell.Offset(0, 1).Value = Left(cell.Value, separator - 1)
'put the firstnames in the second column to the right
cell.Offset(0, 2).Value = Mid(cell.Value, separator + 1)
End If
Next cell
End Sub


'Macro to join 2 cells

Sub Join()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationAutomatic
On Error Resume Next
Dim irows As Long, mrows As Long, ir As Long, ic As Long
irows = Selection.Rows.Count
Set lastcell = Cells.SpecialCells(xlLastCell)
mrow = lastcell.Row
If mrow < irows =" mrow" icols =" Selection.Columns.Count" ir =" 1" newcell =" Trim(Selection.Item(ir," ic =" 2" trimmed =" Trim(Selection.Item(ir,"> 0 Then newcell = newcell & " " & trimmed
Selection.Item(ir, ic) = ""
Next ic
Selection.Item(ir, 1).Value = newcell
Next ir
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub

EXCEL Tips 1


User

Hobby

Read

Internet

Music

Computer

User1

Reading,Internet,Music

Yes

Yes

Yes

No

User2

Internet,Computer,Reading

Yes

Yes

No

Yes

User3


No

No

No

No

User4

Music

No

No

Yes

No


=IF(ISERR(FIND(C$1,$B2)),"No","Yes")


30 March 2007

IE7 Window Close Alert solution:

Solution 1: http://support.microsoft.com/kb/911328/
Solution 2: edit default.aspx, modify:

if (window.name != "MSCRM_MAIN")
{
//var oMe = window.self;
//oMe.opener = window.self;
//oMe.close();
window.open('','_parent','');
window.close();
}