Кастомизация
26
Апр
23

Меняем дефолтные представления (views)

Сегодня я расскажу о некоторых способах кастомизации некастамизируемых дефолтных пердставлений. Странно звучит, да? 🙂 Тогда приступм:

Удаляем стандартные представления

Если Вы попробуете удалить стандартное системное представление, то получите сообщение об ошибке. Если же попробуете отредактировать, то увидите желтинкий прямоугольник с предупреждением об ограничениях в настройках. Ну, что же… не хотят по хорошему – будет по Фрейду 🙂


Настройки представлений CRM хранятся в БД в таблице savedquerybaseset. В которой столбец IsCustomizable отвечает за возможность реактирования представления (значение 1), а столбец IsPrivate за скрытие представления (значение 1).

Чтобы посмотреть настройки этих полей для определенного представления выполните SQL скрипт в Management Studio (точное названия (посмотреть точное название Вы можете открыв его в разделе Форма и представления в настройках объектов):

select
	name,
	IsCustomizable,
	IsPrivate
from
	SavedQueryBase
where
	name='Контакты: без действий кампании за последние 3 месяца'

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

update
	SavedQueryBase
set
	IsPrivate=1, -- делаем представление невидимым
	IsCustomizable=1 -- и кастомизируемым (вплоть до удаления)
where
	name='Контакты: без действий кампании за последние 3 месяца'

Пробуем:

Скроем представление «Контакты: без действий кампании за последние 3 месяца». Откройте SQL Server Management Studio, создайте новы запрос, введите выше указанный скрипт Update и жмите Execute. Закройте и откройте заново окно CRM (или нажмите Ctrl + F5) и смотрим обновленный список представлени.


Учтите, что если Вы скрываете представление, то оно не будет видно и в настройка объектов. А если делаете представление настраевым, то его можно удалить через настроку объектов!

Правим файлик AppGridFilterContainer.htc

В некоторых объектах, например, в форме Контакты при открыти трех связанныех объектов – Действия, Журнал и Возможные сделки (на левой панели) – заданным по умолчанию фильтром будет «Предыдущие 30 дней».
Это не всегда удобно, но к сожалению не поддется настройке. Но есть один способ изменить это — для каждого, из связанных объектов, мы сделаем заданным по умолчанию фильтр «Все». Так как мы будем править исходный код, то этот способ является неподдерживаемой модификацией. Поэтому, во-первых, на Вашем CRM сервере сделайте копию файла AppGridFilterContainer.htc, который по умолчанию находится в папке: C:\Inetpub\wwwroot\_static\_controls\appgridfiltercontainer\

Теперь, когда у нас есть резервная копия, мы можем спокойно изменить оригинальный файл. Откройте AppGridFilterContainer.htc с в каком-нибудь текстовом редакторе. Ближе к концу файла, найдите строки:

if(!IsNull(oCtrl.DataValue)) {
	oCallback(oCtrl);

И замените их на следующий код:

if(!IsNull(oCtrl.DataValue)) {
	if(oCtrl.DataValue=="LastXDays;30" || oCtrl.DataValue=="NextXDays;30" || oCtrl.DataValue=="0") {
		oCtrl.DataValue = "All";
		RefreshGridView();
	}
	oCallback(oCtrl);

Сохраните и закройте файл.


Удалите временные файлы Internet Explorer’а после произведения изменения, иначе Вы не увидите изменений, т.е. IE по-прежнему будет использовать старые файлы, находящиеся у него в кэше.

Удалить временные файлы можно открыв IE, далее Сервис — Свойства обозревателя — в области История просмотра щелкните Удалить… ОК.


Откройте запись контакта в CRM 4.0 и пройдитесь по связанным записям Действий, Журнала и Возможных сделок. Заданное по умолчанию представление, для всех трех связанных объектов, теперь должно быть «Все». Если Вы получаете сообщение об ошибках, Вы можете легко вернуться назад к оригинальному файлу, который Вы скопировали.

Изменение стандартного представления Действия/История/Обращения

Вот еще один способ поменять заданный по умолчанию фильтр в связанных объектах. Также не поддерживаемый – т.к. будет производить не разрешенную манипуляцию с DOM. Весь способ заключается в использовании следующего js скрипта:

//Опции фильтра Действий
var ActivityOptions = {
	All : "All",
	Overdue :"Overdue",
	Today :"Today",
	Tomorrow :"Tomorrow",
	Next7Days :"NextXDays;7",
	Next30Days :"NextXDays;30",
	Next90Days :"NextXDays;90",
	Next6Months :"NextXMonths;6"
} 

//Опции фильтра Журнала
var HistoryOptions = {
	All : "All",
	Today : "Today",
	Yesterday : "Yesterday",
	Last7Days : "LastXDays;7",
	Last30Days : "LastXDays;30",
	Last90Days : "LastXDays;90",
	Last6Months : "LastXMonths;6",
	Last12Months: "LastXMonths;12"
}

//Опции фильтра Обращений
var ServiceOptions = {
	Active : "0",
	Resolved : "1",
	Canceled : "2",
	All : "All"
}

var _loadarea = loadArea;
loadArea = function(sArea, sParams, sUrl, bIsvMode) {
	//загружаем iframe
	_loadarea(sArea, sParams, sUrl, bIsvMode); 

	if( sArea != "areaActivityHistory" && sArea != "areaActivities" && sArea != "areaService" ) return;

	//создаем объект iframe
	var iframe = document.getElementById(sArea + "Frame");
	//ждем пока iframe полностью не загрузится (статус "complete")
	iframe.onreadystatechange = function() {
		if( iframe.readyState == "complete") {
			var picklist,option;
			//ссылаемся на документ iframe
			var iframeDoc = iframe.contentWindow.document;
			switch(sArea) {
				case "areaActivityHistory":
					picklist = iframeDoc.all.actualend[0];
                    /* меняем значение фильтра по умолчанию на то каторое Вам нужно */
                    option = HistoryOptions.Last90Days;
                break;
                case "areaActivities":
                    picklist = iframeDoc.all.scheduledend[0];
                    /* меняем значение фильтра по умолчанию на то каторое Вам нужно */
                    option = ActivityOptions.Next7Days;
                break;
                case "areaService":
                    picklist = iframeDoc.all.statecode[0];
                    /* меняем значение фильтра по умолчанию на то каторое Вам нужно */
                    option = ServiceOptions.All;
                break;
                default: return;
			}
			picklist.value = option;
			picklist.FireOnChange();
		}
	}
}

Тестируем на объекте Контакт. Добавьте вышеуказанный код в событие onload объекта контакт и опубликуйте его. Откройте какую-нибудь запись контактов и пройдитесь по трем связанным объектам – Действия, Журнал, Обращения. У всех у них по умолчанию будут установлены фильтры, которые мы указали в скрипте.


Кастомная HTML-страница

MS CRM предоставляет два стандартных способа добавить кастомные скрипты в приложение; первый через события формы, второй через пункты меню и кнопки в ISV.Config и Карте сайта. Ни один из них не позволяет Вам писать код для управления заданными по умолчанию представлениями объектов.

Однако, есть обходной путь. Подход заключается в том, чтобы создать HTML (или ASP.Net) страницу, которая не имеет никаких компонентов интерфейса, но отображает списки объектов CRM через IFrame. Она будет выглядеть точно так же, как стандартная страница CRM, с той лишь разницей, что Вы можете написать скрипт к своей подключенной через IFrame странице, чтобы управлять ею, и следовательно, изменяет отображаемое представление.
Учтите, что этот подход также неподдерживаемым, так как он включает программный контроль над picklist’ом за пределами формы CRM.

Создаем базовую страницу

HTML страница содержит IFrame без каких либо границ или дополнений, а также яваскрипт, который выполняется один раз, как только IFrame загрузится. Следующий HTML код демонстрирует как встроить IFrame в страницу:

<body style="margin:0" onload="Init();">
	<iframe onreadystatechange="ors();" id="ifr" src="about:blank" width="100%" height="100%" frameborder="0" leftmargin="0">
</body>

В этом примере представлена универсальная страница, которая может содержать большинство страниц CRM, для этого свойство src для IFrame не жестко фиксировано, а задается программно:

function Init() {
	var etc = getQS('etc');
	if (etc != null) {
		document.all.ifr.src = '/_root/homepage.aspx?etc=' + etc;
	}
}

Параметр src — стандартный способ отобразить большинство списков объектов, а etc – код типа объекта.
Событие onreadystatechange используется, чтобы определить загрузился ли IFrame:

function ors() {
	if (event.srcElement.readyState == 'complete') {
		var sView = getQS('view');
		if (sView != null)
			SetView(sView);
			document.all.ifr.style.visibility = 'visible';
		} else {
			document.all.ifr.style.visibility = 'hidden';
		}
	}

Это код проверяет свойство readyState нашего IFrame, который будет равняться ‘complete’, когда контент IFrame загрузился, и после того, как контент IFrame был изменен.

Следующий код показывает, как изменить, какое представление выбрано:

function SetView(sView) {
	var ifDoc = document.frames['ifr'].document.all; // доступ к контенту IFrame
	var oSel = ifDoc['SavedQuerySelector']; // picklist control to select view
	if (oSel != null) {
		var v = GetSelectValue(oSel, sView);
		if (v) {
			oSel.DefaultValue = v;
			oSel.DataValue = v;
			oSel.FireOnChange(); // need to fire this event to apply changes
		}
	}
}
// helper function to select item in picklist
function GetSelectValue(oSel, sText) {
	for (var i=0;i<osel.options.length;i++) {
		if (oSel.options[i].text == sText) return oSel.options[i].value;
	}
}

Код использует другую вспомогательную функцию, чтобы получить доступ к параметрам переданным в строке запроса:

function getQS(name) {
	var ret = '';
	if (window.location.search != null && window.location.search.length > 1) {
		var aQS = window.location.search.substring(1).split('&');
		if (aQS != null)
			for (var i=0;i<aQS.length;i++)
				if (aQS[i].indexOf(name + '=') == 0)
					ret = aQS[i].substring(name.length + 1).replace('%20', ' ');
	}
	return ret;
}

Поместив все это вместе получится страница, которая отобразит список объекта, и изменит заданное по умолчанию представление, основанное на двух параметрах переданных через строку запроса. Например, чтобы отобразить список Бизнес-партнеров, и установит представление в Активные организации (%20, кодирование пробела, которое не разрешено в url):

defaultViewChanger.htm?etc=1&view=Active%20Accounts

Установка различных значений представлений по умолчанию для различных пользователей

У нас теперь есть страница, которая может программно изменить заданное по умолчанию представление. Есть два способа, чтобы отобразить различные заданные по умолчанию представления различным пользователям: программируемый путь, который идентифицирует текущего пользователя (и вероятно группу или роль) и следовательно определяет заданное по умолчанию представление, или через разрешения в SiteMap. Про то как идентифицировать роли пользователя и в зависимости от этого выполнять какие-либо действия смотрите статью «Отображение и скрытие полей в зависимости от роли пользователя».

Другой подход заключается в использовании элемента Privilege в SiteMap, чтобы отображать различные навигационные кнопки, основываясь на их привилегиях. Предположим, что у нас есть две группы пользователей, которые хотят различные заданные по умолчанию представления объекта Бинес-партнеры: одна группа хочет видеть «Мои активные организации», а другие «Активные организации».

И так пробуем. Скачайте файл defaultViewChanger.htm (или соберите его из кода представленного выше) и поместите его в папку C:\Inetpub\wwwroot\ . Экспортируйте SiteMap и добавьте в него несколько строк (я добавил в Workplace, т.е. Рабочую область), незабыв поменять имя сервера на свое:

<SubArea Id="nav_accthostA" Title="AccountA" Url="http://crmtest/defaultViewChanger.htm?etc=1&amp;view=Неактивные%20организации" Icon="/_imgs/ico_18_1.gif">
	<Privilege Entity="account" Privilege="Write" />
</SubArea>
<SubArea Id="nav_accthostB" Title="AccountB" Url="http://crmtest/defaultViewChanger.htm?etc=1&amp;view=Активные%20организации" Icon="/_imgs/ico_18_1.gif">
	<Privilege Entity="account" Privilege="Assign" />
</SubArea>

Заметьте, что в XML необходимо использовать &amp; вместо &, чтобы указать разделитель в строке запроса между парами переменная=значение.

Импортируйте SiteMap обратно. Теперь назначьте какому-либо пользователю роль, у которой нет прав на назначение записи Бизнес-партнер. Откройте рабочую область, Вы увидите только одну из новых подобластей – AccountA – и щелкнув по ней отобразится список бизнес-партнеров, в котором по умолчанию, будет представление Неактивные организации. Ну, а если Вы зайдете в рабочую область под пользователем, которому назначена роль с правом назначения для объекта Бизнес-партнер (и нет прав на запись), то по умолчанию отобразится представление Активные контакты.



Plug-In

А тепреь скроем представления сапортным путем:

  • Скачайте проект плагина и откройте его в Visual Studio (нужно открыть файл SNC.Plugin.DefaultView.sln). В обозревателе проекта (вверху срава) дважды щелкните по SetView.cs;
  • Откроется основной код проекта, который нужно чуть-чуть подредактировать:
    • в строчке
      if (condition.Values[0].Equals(1))
      

      в условии Equals(1) укажите код объекта для которого собираемся фильтровать представления. Если таких объектов несколько продублируйте весь блок

      if (condition.Values[0].Equals(<код_объекта>))
      

      его необходимо копировать друг за другом.

    • В строчках
      if (usersRoles.Contains("Системный администратор"))
      

      укажите какой ролью должен обладать пользователь чтобы выполнились следующий действия (если не хотите чтобы производилаь фильтрация по роли – удалите условие if и закрывающую его фигурную скобку)…

    • с помощью этой строки
      ourDefaultView = "Неактивные организации";
      

      задается дефолтное представление для указанного объекта и для пользователя с указанной ролью

    • ну, а строчкой
      viewsToHide.Add("Активные организации");
      

      мы скрываем не нужные вьюхи (продублируйте ее если вам нужно скрыть больше представлений)

  • После того как отредактировали код перейдите Build — Build Solution. VS соберет проект и положит его в папку
    …\SNC.Plugin.DefaultView\SNC.Plugin.DefaultView\bin\Debug. Перейдите в нее и скопируйте из нее файл SNC.Plugin.DefaultView.dll в папку C:\Program Files\Microsoft Dynamics CRM\Server\bin\assembly;



  • Откройте Plugin Registration Tool — введите параеметры подключение к CRM-серверу — после установления соединения перейдите Register -Register New Assembly — в верхеней строчке введите путь к dll файл скопрированному в папку assembly — в качестве места расположения укажите Disk — Register Selected Plugins;
  • Полсе того как плагин зарегистрируется, в основном окне щелкните по нему правой кнопкой мыши — Register New Step — заполните следующие поля:
    • Message = RetreieveMultiple
    • Primary Entity = savedquery
    • В качестве стадии укажиет Post stag
  • все остально оставьте без изменений — Register New Step;
  • На этом все 🙂 можете любоваться результатом работы 🙂



Комментарии (23)
  • Евгений 26.04.2009

    Добрый день!
    А возможно ли изменение порядка системных представлений?
    Поясню: по умолчанию представления рассортированы в алфавитном порядке: Активные…, Мои…, Неактивные… и т.д. Можно ли расставить эти представления несколько в ином порядке?

  • slivka_83 26.04.2009

    Раньше не приходилось сталкиваться с такой задачей, но наверника можно 🙂 нужно копать в сторону SDK-разработки 🙂

  • Sergey 26.04.2009

    Хорошая статья. А не подскажите, как поменять представление действия определённой сущности. Хотелось бы выводить не только, что с ней в отношении, а и привязанные сущности. Например как в возможной сделке при создании встречи, она отражается в действиях и в возможной сделке и в контакте, с которым возможная сделка

  • slivka_83 26.04.2009

    хм… ничего другого, кроме динамического представления в голову не приходит: http://mmcrm.ru/?p=866

  • oikcom 26.04.2009

    Доброго времени суток.
    Подскажите, а как то можно изменять statecode либо statuscode в действии электронная почта, дело в том, что входящие письма электронной почты, завершенное действие, соответственно его нельзя редактировать. А ситуация такова, что одного поля в отношении недостаточно, нужно привязывать входящее письмо, еще по двум кастомным полям (N:1), что не возможно в связи с тем что действие завершено и не редактируется.
    Спасибо.

  • slivka_83 26.04.2009

    А с помощью бизнес-процесса или плагина не пробовали?

  • oikcom 26.04.2009

    Доброго времени суток
    Дело в том, что в бизнес-процессе, собственно как и в записи, эти поля запрещены для редактирования, можно впринципе вставить чо нибудь в скуль для автоматического изменения этих полей, но не глюкнит ли CRM, если изменять запрещенные для редактирования поля непосредственно в БД?

  • slivka_83 26.04.2009

    А так пробовали?
    http://mmcrm.ru/?p=547

  • Павел 26.04.2009

    а как вывести определенное представление Действий?

  • slivka_83 26.04.2009

    Здрасьте 🙂
    Куда вывести? 🙂

  • ech 26.04.2009

    Я так и не понял, как сделать по умолчанию для пользователя его представление, сделанное через расширенный поиск?? Спасибо

  • slivka_83 26.04.2009

    Добрый день 🙂 в CRM 4.0 этого не сделать. По крайней мере через стандартный функционал 🙂 А вот в CRM 2011 можно 🙂

  • Дмитрий 26.04.2009

    Спасибо большое за статью, очень пригодилась!
    А возможно ли ранее созданное личное представление сделать системны, чтобы заново не создавать его из страницы настроек объекта?

  • slivka_83 26.04.2009

    Ну в теории можно взять XML определение личного представления. Выгрузить системное представление (в составе оъекта) и заменить его XML определение. Как то так…

  • дмитрий 26.04.2009

    Тогда невольно возникает вопрос: а где лежат личные представления? ))

  • slivka_83 26.04.2009

    Если не ошибаюсь XML определения сохраненных представлений можно найти в таблице UserQueryBase.

  • Павел 26.04.2009

    Добрый день!
    Подскажите, как можно скрыть неактивные подразделения в представлении для поиска подразделений? Можно ли это сделать без написания плагина? (CRM 4.0)

  • slivka_83 26.04.2009

    Добрый день.

    Думаю да — нужно задать фильтр для Представления Быстрого поиска.

  • Павел 26.04.2009

    Прошу подсказать, как это можно сделать? Захожу в настройку сущности, в раздел «Представления» но там нет пункта меню «Добавить фильтр» и т.п. Пробовал разрешить изменение представления, как указано в данной статье, но так и не удалось добавить фильтр.

  • slivka_83 26.04.2009

    Нужно открыть Представление Быстрого поиска. Там Изменить условия фильтра. Сохраняем — Публикуем.

    З.Ы. Вам ведь нужен именно быстрый поиск? Или лукап?

  • Павел 26.04.2009

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

  • Павел 26.04.2009

    Вроде разобрался. Решение нашел в другой ветке:
    На онлоад формы повесил: crmForm.all..lookupclass = «alllookups»;
    В C:\Program Files\Microsoft Dynamics CRM\Server\ApplicationFiles\alllookups.xml для объекта подразделение (код — 10) добавил условие . Неактивные подразделения больше не отображаются в лукапе.
    Остался еще один вопрос. Как быть, если в лукапе выбирается кастомная сущность,как узнать ее код и можно ли условие для нее добавить в alllookups.xml?

  • slivka_83 26.04.2009

    Код можно узнать в БД, в строке URL при редактировании сущности вроде должен быть. Или с помощью различных утилит.
    По второму вопросу не подскажу — с CRM 4.0 давно уже не работал.

*

code