Wednesday, April 15, 2009

Делаю быстрый переключатель языка пользователя

Надеюсь, что мой читатель согласится, что переключение языка системы в MS CRM 4.0 не очень удобный механизм. Для этого надо перейти в рабочую область, нажать настройка рабочей области, в открывшемся окне перейти на закладку языки, наконец выбрать нужный язык и нажать ОК. Был разработан механизм быстрого переключения языка и о нём я расскажу в этом сообщении.


Для начала выполню экспорт ISV.config файла. Для этого нужно перейти Settings - Customization - Export Customizations - выбрать в перечне ISV Config, нажать Export Selected Customizations, сохранить полученный файл, разархивировать, открыть его на редактирование. Найти элемент Root и вставить в него описание нового элемента управления - драпдауна языков при помощи такого кода:

<Root>
<MenuBar>
<CustomMenus>
<Menu ValidForCreate="0">
<Titles>
<Title LCID="1049" Text="Язык" />
<Title LCID="1033" Text="Language" />
</Titles>
<MenuItem JavaScript="alert('English')">
<Titles>
<Title LCID="1049" Text="Английский" />
<Title LCID="1033" Text="English" />
</Titles>
</MenuItem>
<MenuItem JavaScript="alert('Russian')">
<Titles>
<Title LCID="1049" Text="Русский" />
<Title LCID="1033" Text="Russian" />
</Titles>
</MenuItem>
</Menu>
</CustomMenus>
</MenuBar>
</Root>

Если Вы все манипуляции проводите на чистой инсталяции без своих уже сделанных ISV расширений, у получится примерно такой XML файл:

<ImportExportXml version="4.0.0.0" languagecode="1033" generatedBy="OnPremise">
<Entities>
</Entities>
<Roles>
</Roles>
<Workflows>
</Workflows>
<IsvConfig>
<configuration version="3.0.0000.0">
<Root>
<MenuBar>
<CustomMenus>
<Menu ValidForCreate="0">
<Titles>
<Title LCID="1049" Text="Язык" />
<Title LCID="1033" Text="Language" />
</Titles>
<MenuItem JavaScript="alert('English')">
<Titles>
<Title LCID="1049" Text="Английский" />
<Title LCID="1033" Text="English" />
</Titles>
</MenuItem>
<MenuItem JavaScript="alert('Russian')">
<Titles>
<Title LCID="1049" Text="Русский" />
<Title LCID="1033" Text="Russian" />
</Titles>
</MenuItem>
</Menu>
</CustomMenus>
</MenuBar>
</Root>
<Entities>
<Entity name="account" />
<Entity name="contact" />
<Entity name="lead" />
<Entity name="opportunity" />
<Entity name="list" />
<Entity name="campaign" />
<Entity name="campaignactivity" />
<Entity name="campaignresponse" />
<Entity name="incident" />
<Entity name="quote" />
<Entity name="salesorder" />
<Entity name="invoice" />
</Entities>
<ServiceManagement>
<AppointmentBook>
<SmoothScrollLimit>2000</SmoothScrollLimit>
<TimeBlocks>
<!-- All CSS Class mapping for Service activities -->
<TimeBlock EntityType="4214" StatusCode="1" CssClass="ganttBlockServiceActivityStatus1" />
<TimeBlock EntityType="4214" StatusCode="2" CssClass="ganttBlockServiceActivityStatus2" />
<TimeBlock EntityType="4214" StatusCode="3" CssClass="ganttBlockServiceActivityStatus3" />
<TimeBlock EntityType="4214" StatusCode="4" CssClass="ganttBlockServiceActivityStatus4" />
<TimeBlock EntityType="4214" StatusCode="6" CssClass="ganttBlockServiceActivityStatus6" />
<TimeBlock EntityType="4214" StatusCode="7" CssClass="ganttBlockServiceActivityStatus7" />
<TimeBlock EntityType="4214" StatusCode="8" CssClass="ganttBlockServiceActivityStatus8" />
<TimeBlock EntityType="4214" StatusCode="9" CssClass="ganttBlockServiceActivityStatus9" />
<TimeBlock EntityType="4214" StatusCode="10" CssClass="ganttBlockServiceActivityStatus10" />
<!-- All CSS Class mapping for Appointments -->
<TimeBlock EntityType="4201" StatusCode="1" CssClass="ganttBlockAppointmentStatus1" />
<TimeBlock EntityType="4201" StatusCode="2" CssClass="ganttBlockAppointmentStatus2" />
<TimeBlock EntityType="4201" StatusCode="3" CssClass="ganttBlockAppointmentStatus3" />
<TimeBlock EntityType="4201" StatusCode="4" CssClass="ganttBlockAppointmentStatus4" />
<TimeBlock EntityType="4201" StatusCode="5" CssClass="ganttBlockAppointmentStatus5" />
<TimeBlock EntityType="4201" StatusCode="6" CssClass="ganttBlockAppointmentStatus6" />
</TimeBlocks>
</AppointmentBook>
</ServiceManagement>
</configuration>
</IsvConfig>
<EntityMaps />
<EntityRelationships />
<Languages>
<Language>1033</Language>
<Language>1049</Language>
</Languages>
</ImportExportXml>

Сохраните полученный файл, выполните его импорт в MS CRM. Для этого - перейдите Settings - Customization - Import Customizations, выберите файл с ISV кастомизацией, нажмите Upload, Import Customization. Выполните перезагрузку браузера. Если всё было сделано правильно - появится новое меню, как на приведённом ниже скриншоте:



Для того, чтобы переключатель языка действительно заработал, а не показывал алерты, необходимо доработать Java Script пунктов меню. Для того чтобы выполнять изменение языка пользуюсь веб сервисом.

Для начала необходимо получить идентификатор текущего пользователя. Для этого воспользуюсь запросом WhoAmIRequest:

var soapBody = '<soap:Body><Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'><Request xsi:type=\'WhoAmIRequest\' /></Execute></soap:Body>';

var soapXml = '<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'>';
soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += '</soap:Envelope>';


var xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
xmlhttp.open('POST', '/mscrmservices/2007/crmservice.asmx', false);
xmlhttp.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xmlhttp.setRequestHeader('SOAPAction', 'http://schemas.microsoft.com/crm/2007/WebServices/Execute');

xmlhttp.send(soapXml);

xmlDoc=new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async=false;
xmlDoc.loadXML(xmlhttp.responseXML.xml);

var userid = xmlDoc.getElementsByTagName('UserId')[0].childNodes[0].nodeValue;

Далее основываясь на идентификаторе пользователя и языке на который следует переключиться (1033 - английский, 1049 - русский, 1058 - украинский) и воспользовавшись запросом UpdateUserSettingsSystemUserRequest можно выполнить изменения языка текущего пользователя при помощи приведённого далее кода:


var xml = '<?xml version=\'1.0\' encoding=\'utf-8\'?>'+
'<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\''+
' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\''+
' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'>'+
GenerateAuthenticationHeader()+
'<soap:Body>'+
'<Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'>'+
'<Request xsi:type=\'UpdateUserSettingsSystemUserRequest\'>'+
'<UserId>'+userid+'</UserId>'+
'<Settings xsi:type=\'usersettings\' >'+
'<helplanguageid>1033</helplanguageid>'+
'<uilanguageid>1033</uilanguageid>'+
'<systemuserid>'+userid+'</systemuserid>'+
'</Settings>'+
'</Request>'+
'</Execute>'+
'</soap:Body>'+
'</soap:Envelope>';


var xHReq = new ActiveXObject('Msxml2.XMLHTTP');
xHReq.Open('POST', '/mscrmservices/2007/CrmService.asmx', false);
xHReq.setRequestHeader('SOAPAction','http://schemas.microsoft.com/crm/2007/WebServices/Execute');
xHReq.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xHReq.setRequestHeader('Content-Length', xml.length);
xHReq.send(xml);

Также необходимо выполнить перезагрузку браузера, чтобы применился новый язык. Для этого пользовался таким кодом:

parent.location.reload(true);

В результате получился следующий ISV Config файл, импортировав который получаем быстрый переключатель языка интерфейса пользователя:

<ImportExportXml version="4.0.0.0" languagecode="1033" generatedBy="OnPremise">
<Entities>
</Entities>
<Roles>
</Roles>
<Workflows>
</Workflows>
<IsvConfig>
<configuration version="3.0.0000.0">
<Root>
<MenuBar>
<CustomMenus>
<Menu ValidForCreate="0">
<Titles>
<Title LCID="1049" Text="Язык" />
<Title LCID="1033" Text="Language" />
</Titles>
<MenuItem JavaScript="
var soapBody = '&lt;soap:Body&gt;&lt;Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'&gt;&lt;Request xsi:type=\'WhoAmIRequest\' /&gt;&lt;/Execute&gt;&lt;/soap:Body&gt;';

var soapXml = '&lt;soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'&gt;';
soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += '&lt;/soap:Envelope&gt;';


var xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
xmlhttp.open('POST', '/mscrmservices/2007/crmservice.asmx', false);
xmlhttp.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xmlhttp.setRequestHeader('SOAPAction', 'http://schemas.microsoft.com/crm/2007/WebServices/Execute');

xmlhttp.send(soapXml);

xmlDoc=new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async=false;
xmlDoc.loadXML(xmlhttp.responseXML.xml);

var userid = xmlDoc.getElementsByTagName('UserId')[0].childNodes[0].nodeValue;



var xml = '&lt;?xml version=\'1.0\' encoding=\'utf-8\'?&gt;'+
'&lt;soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\''+
' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\''+
' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'&gt;'+
GenerateAuthenticationHeader()+
'&lt;soap:Body&gt;'+
'&lt;Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'&gt;'+
'&lt;Request xsi:type=\'UpdateUserSettingsSystemUserRequest\'&gt;'+
'&lt;UserId&gt;'+userid+'&lt;/UserId&gt;'+
'&lt;Settings xsi:type=\'usersettings\' &gt;'+
'&lt;helplanguageid&gt;1033&lt;/helplanguageid&gt;'+
'&lt;uilanguageid&gt;1033&lt;/uilanguageid&gt;'+
'&lt;systemuserid&gt;'+userid+'&lt;/systemuserid&gt;'+
'&lt;/Settings&gt;'+
'&lt;/Request&gt;'+
'&lt;/Execute&gt;'+
'&lt;/soap:Body&gt;'+
'&lt;/soap:Envelope&gt;';


var xHReq = new ActiveXObject('Msxml2.XMLHTTP');
xHReq.Open('POST', '/mscrmservices/2007/CrmService.asmx', false);
xHReq.setRequestHeader('SOAPAction','http://schemas.microsoft.com/crm/2007/WebServices/Execute');
xHReq.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xHReq.setRequestHeader('Content-Length', xml.length);
xHReq.send(xml);
parent.location.reload(true);
">
<Titles>
<Title LCID="1049" Text="Английский" />
<Title LCID="1033" Text="English" />
</Titles>
</MenuItem>
<MenuItem JavaScript="
var soapBody = '&lt;soap:Body&gt;&lt;Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'&gt;&lt;Request xsi:type=\'WhoAmIRequest\' /&gt;&lt;/Execute&gt;&lt;/soap:Body&gt;';

var soapXml = '&lt;soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'&gt;';
soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += '&lt;/soap:Envelope&gt;';


var xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
xmlhttp.open('POST', '/mscrmservices/2007/crmservice.asmx', false);
xmlhttp.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xmlhttp.setRequestHeader('SOAPAction', 'http://schemas.microsoft.com/crm/2007/WebServices/Execute');

xmlhttp.send(soapXml);

xmlDoc=new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async=false;
xmlDoc.loadXML(xmlhttp.responseXML.xml);

var userid = xmlDoc.getElementsByTagName('UserId')[0].childNodes[0].nodeValue;



var xml = '&lt;?xml version=\'1.0\' encoding=\'utf-8\'?&gt;'+
'&lt;soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\''+
' xmlns:xsi=\'http://www.w3.org/2001/XMLSchema-instance\''+
' xmlns:xsd=\'http://www.w3.org/2001/XMLSchema\'&gt;'+
GenerateAuthenticationHeader()+
'&lt;soap:Body&gt;'+
'&lt;Execute xmlns=\'http://schemas.microsoft.com/crm/2007/WebServices\'&gt;'+
'&lt;Request xsi:type=\'UpdateUserSettingsSystemUserRequest\'&gt;'+
'&lt;UserId&gt;'+userid+'&lt;/UserId&gt;'+
'&lt;Settings xsi:type=\'usersettings\' &gt;'+
'&lt;helplanguageid&gt;1049&lt;/helplanguageid&gt;'+
'&lt;uilanguageid&gt;1049&lt;/uilanguageid&gt;'+
'&lt;systemuserid&gt;'+userid+'&lt;/systemuserid&gt;'+
'&lt;/Settings&gt;'+
'&lt;/Request&gt;'+
'&lt;/Execute&gt;'+
'&lt;/soap:Body&gt;'+
'&lt;/soap:Envelope&gt;';


var xHReq = new ActiveXObject('Msxml2.XMLHTTP');
xHReq.Open('POST', '/mscrmservices/2007/CrmService.asmx', false);
xHReq.setRequestHeader('SOAPAction','http://schemas.microsoft.com/crm/2007/WebServices/Execute');
xHReq.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xHReq.setRequestHeader('Content-Length', xml.length);
xHReq.send(xml);
parent.location.reload(true);">
<Titles>
<Title LCID="1049" Text="Русский" />
<Title LCID="1033" Text="Russian" />
</Titles>
</MenuItem>
</Menu>
</CustomMenus>
</MenuBar>
</Root>
<Entities>
<Entity name="account" />
<Entity name="contact" />
<Entity name="lead" />
<Entity name="opportunity" />
<Entity name="list" />
<Entity name="campaign" />
<Entity name="campaignactivity" />
<Entity name="campaignresponse" />
<Entity name="incident" />
<Entity name="quote" />
<Entity name="salesorder" />
<Entity name="invoice" />
</Entities>
<ServiceManagement>
<AppointmentBook>
<SmoothScrollLimit>2000</SmoothScrollLimit>
<TimeBlocks>
<!-- All CSS Class mapping for Service activities -->
<TimeBlock EntityType="4214" StatusCode="1" CssClass="ganttBlockServiceActivityStatus1" />
<TimeBlock EntityType="4214" StatusCode="2" CssClass="ganttBlockServiceActivityStatus2" />
<TimeBlock EntityType="4214" StatusCode="3" CssClass="ganttBlockServiceActivityStatus3" />
<TimeBlock EntityType="4214" StatusCode="4" CssClass="ganttBlockServiceActivityStatus4" />
<TimeBlock EntityType="4214" StatusCode="6" CssClass="ganttBlockServiceActivityStatus6" />
<TimeBlock EntityType="4214" StatusCode="7" CssClass="ganttBlockServiceActivityStatus7" />
<TimeBlock EntityType="4214" StatusCode="8" CssClass="ganttBlockServiceActivityStatus8" />
<TimeBlock EntityType="4214" StatusCode="9" CssClass="ganttBlockServiceActivityStatus9" />
<TimeBlock EntityType="4214" StatusCode="10" CssClass="ganttBlockServiceActivityStatus10" />
<!-- All CSS Class mapping for Appointments -->
<TimeBlock EntityType="4201" StatusCode="1" CssClass="ganttBlockAppointmentStatus1" />
<TimeBlock EntityType="4201" StatusCode="2" CssClass="ganttBlockAppointmentStatus2" />
<TimeBlock EntityType="4201" StatusCode="3" CssClass="ganttBlockAppointmentStatus3" />
<TimeBlock EntityType="4201" StatusCode="4" CssClass="ganttBlockAppointmentStatus4" />
<TimeBlock EntityType="4201" StatusCode="5" CssClass="ganttBlockAppointmentStatus5" />
<TimeBlock EntityType="4201" StatusCode="6" CssClass="ganttBlockAppointmentStatus6" />
</TimeBlocks>
</AppointmentBook>
</ServiceManagement>
</configuration>
</IsvConfig>
<EntityMaps />
<EntityRelationships />
<Languages>
<Language>1033</Language>
<Language>1049</Language>
</Languages>
</ImportExportXml>