Кастомизация
01
Окт
3

Кастомная система прав

Иногда, по тем или иным причинам, требуется выдавать права на кнопки, которые нельзя задать с помощью Ролей безопасности. А CRM 2011 нет поддержки кастомных прав. Обычно для этого создают фиктивные Роли, которые выполняют одну, две или три функции или задействую для идентификации нужного пользователя косвенные права, которые достались пользователю. Но все эти методы либо слишком громоздки, либо не интуитивно понятно.

Поэтому рассмотрим способ расширения «системы» прав с помощью Подключений. Рассмотрим пример, когда требуется раздавать права на закрытие Возможной сделки. В стандарте, все, кто имеют права на обновление записи Возможной сделки, могут ее закрывать. Попробуем более выборочно раздавать их:

  • Создайте объект Кастомные права (customprivilege) с одним текстовым полем – данный объект будет хранить названия кастомных прав. Обязательно включите для этот объекта Подключения. Таким образом, Вы сможете легко расширять набор кастомных прав, а с помощью Полномочий добавлять их пользователям;
  • Создайте две эксклюзивные Роли подключения (и свяжите их друг с другом):
    •  «Право на» для объекта Пользователя;
    •  «Предоставлено» объекта Кастомные права.
  • Создайте JS Веб-ресурс hasprivilege.js с таким кодом:
    function hasPrivilege(privilege) {
        // Определяем Fetch-запрос
        var fetch = '<fetch mapping="logical" count="50" aggregate="true" version="1.0">' +
    	                '<entity name="new_customprivilege">' +
    		                '<attribute name="new_customprivilegeid" aggregate="countcolumn" alias="count" />' +
    		                '<filter>' +
    			                '<condition attribute="new_name" operator="eq" value="' + privilege + '" />' +
    		                '</filter>' +
    		                '<link-entity name="connection" from="record2id" to="new_customprivilegeid">' +
    			                '<filter>' +
    				                '<condition attribute="record1id" operator="eq" value="' + Xrm.Page.context.getUserId().replace("{", "").replace("}", "") + '" />' +
                                '</filter>' +
    		                '</link-entity>' +
    	                '</entity>' +
                    '</fetch>';
    
        // Значение по умолчанию
        hasPriv = false;
    
        // Формируем SOAP-запрос
        var request = '';
        request += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>';
        request += '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">' +
                    '<request i:type="b:RetrieveMultipleRequest" ' +
                    ' xmlns:b="http://schemas.microsoft.com/xrm/2011/Contracts" ' +
                    ' xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' +
                    '<b:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic">' +
                    '<b:KeyValuePairOfstringanyType>' +
                    '<c:key>Query</c:key>' +
                    '<c:value i:type="b:FetchExpression">' +
                    '<b:Query>';
        request += fetchEncode(fetch); // Кодируем Fetch-запрос
        request += '</b:Query>' +
                    '</c:value>' +
                    '</b:KeyValuePairOfstringanyType>' +
                    '</b:Parameters>' +
                    '<b:RequestId i:nil="true"/>' +
                    '<b:RequestName>RetrieveMultiple</b:RequestName>' +
                    '</request>' +
                    '</Execute>';
        request += '</s:Body></s:Envelope>';
    
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open("POST", Xrm.Page.context.getServerUrl() + "/XRMServices/2011/Organization.svc/web", false);
        xmlhttp.setRequestHeader("Accept", "application/xml, text/xml, */*");
        xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
        xmlhttp.onreadystatechange = function () {
            if (xmlhttp.readyState == 4) {
                if (xmlhttp.status == 200) {
                    var names = "";
                    // Вытаскиваем результат
                    var results = xmlhttp.responseXML.getElementsByTagName('b:value')[2];
                    hasPriv = results.textContent > 0;
                } else {
                    alert(xmlhttp.responseText + " " + xmlhttp.statusText);
                }
            }
        };
        xmlhttp.send(request); // Отправляем запрос
    
        // Возвращаем результат
        return hasPriv;
    }
    
    // Функция кодирует Fetch-запрос
    function fetchEncode(FetchXML) {
        var c;
        var HtmlEncode = '';
    
        for (var cnt = 0; cnt < FetchXML.length; cnt++) {
            c = FetchXML.charCodeAt(cnt);
    
            if (((c > 96) && (c < 123)) || ((c > 64) && (c < 91)) || (c == 32) || ((c > 47) && (c < 58)) || (c == 46) || (c == 44) || (c == 45) || (c == 95)) {
                HtmlEncode = HtmlEncode + String.fromCharCode(c);
            } else {
                HtmlEncode = HtmlEncode + '&#' + c + ';';
            }
        }
    
        return HtmlEncode;
    }
    

    В данном коде представлен Fetch-запрос, который получает на входе название Кастомного права и проверяет, имеется ли таковое (связь через Подключения) у текущего Пользователя;

  • Выгрузите Решение с Возможной сделкой и дополните четыре кнопки закрывающие ВС новым правилом EnableRule, которое вызывает функцию hasPrivilege и передает ему называние искомого «права»:
    <RibbonDiffXml>
    	<CustomActions />
    	<Templates>
    		<RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
    	</Templates>
    	<CommandDefinitions>
    		<CommandDefinition Id="Mscrm.HomepageGrid.opportunity.MarkAsWon">
    			<EnableRules>
    				<EnableRule Id="Mscrm.SelectionCountExactlyOne" />
    				<EnableRule Id="Mscrm.VisualizationPaneNotMaximized" />
    				<EnableRule Id="Privilege.CanCloseOpportunity" />
    			</EnableRules>
    			<DisplayRules>
    				<DisplayRule Id="Mscrm.CanWriteOpportunity" />
    			</DisplayRules>
    			<Actions>
    				<JavaScriptFunction Library="/_static/_common/scripts/ribbonactions.js" FunctionName="Mscrm.OpportunityActions.close">
    					<CrmParameter Value="SelectedControlSelectedItemReferences" />
    					<BoolParameter Value="true" />
    					<CrmParameter Value="SelectedControl" />
    				</JavaScriptFunction>
    			</Actions>
    		</CommandDefinition>
    		<CommandDefinition Id="Mscrm.HomepageGrid.opportunity.MarkAsLost">
    			<EnableRules>
    				<EnableRule Id="Mscrm.SelectionCountExactlyOne" />
    				<EnableRule Id="Mscrm.VisualizationPaneNotMaximized" />
    				<EnableRule Id="Privilege.CanCloseOpportunity" />
    			</EnableRules>
    			<DisplayRules>
    				<DisplayRule Id="Mscrm.CanWriteOpportunity" />
    			</DisplayRules>
    			<Actions>
    				<JavaScriptFunction Library="/_static/_common/scripts/ribbonactions.js" FunctionName="Mscrm.OpportunityActions.close">
    					<CrmParameter Value="SelectedControlSelectedItemReferences" />
    					<BoolParameter Value="false" />
    					<CrmParameter Value="SelectedControl" />
    				</JavaScriptFunction>
    			</Actions>
    		</CommandDefinition>
    		<CommandDefinition Id="Mscrm.Form.opportunity.MarkAsLost">
    			<EnableRules>
    				<EnableRule Id="Mscrm.CanWritePrimary" />
    				<EnableRule Id="Privilege.CanCloseOpportunity" />
    			</EnableRules>
    			<DisplayRules>
    				<DisplayRule Id="Mscrm.CanWriteOpportunity" />
    				<DisplayRule Id="Mscrm.OpportunityIsOpen" />
    			</DisplayRules>
    			<Actions>
    				<JavaScriptFunction Library="/_static/sfa/opps/opps.js" FunctionName="complete">
    					<BoolParameter Value="false" />
    				</JavaScriptFunction>
    			</Actions>
    		</CommandDefinition>
    		<CommandDefinition Id="Mscrm.Form.opportunity.MarkAsWon">
    			<EnableRules>
    				<EnableRule Id="Mscrm.CanWritePrimary" />
    				<EnableRule Id="Privilege.CanCloseOpportunity" />
    			</EnableRules>
    			<DisplayRules>
    				<DisplayRule Id="Mscrm.CanWriteOpportunity" />
    				<DisplayRule Id="Mscrm.OpportunityIsOpen" />
    			</DisplayRules>
    			<Actions>
    				<JavaScriptFunction Library="/_static/sfa/opps/opps.js" FunctionName="complete">
    					<BoolParameter Value="true" />
    				</JavaScriptFunction>
    			</Actions>
    		</CommandDefinition>
    	</CommandDefinitions>
    	<RuleDefinitions>
    		<TabDisplayRules />
    		<DisplayRules />
    		<EnableRules>
    			<EnableRule Id="Privilege.CanCloseOpportunity">
    				<CustomRule Default="false" FunctionName="hasPrivilege" Library="$webresource:new_hasprivilege.js">
    					<StringParameter Value="Закрытие Возможной сделки" />
    				</CustomRule>
    			</EnableRule>
    		</EnableRules>
    	</RuleDefinitions>
    	<LocLabels />
    </RibbonDiffXml>
    

Собственно и все. Создайте Кастомное право с названием «Закрытие Возможной сделки» и подключите его к нужному пользователю.




Комментарии (3)
  • Леон 01.10.2013

    Не подскажите есть ли у crm возможность ограничить правами доступ к некой группе контактов?

  • slivka_83 01.10.2013

    Если отвечать в лоб, то да.
    Если нужна конкретика то все зависит от тога как спроектирована система.

  • Леон 01.10.2013

    Пока додумался разделить правами по отделам, но там нюанс с тем что один отдел должен видеть свои контакты и все, а остальные все кроме привелегированых.

*

code