вторник, 26 мая 2015 г.

Интеграция корпортала Битрикс24 (коробка) с AD. О подводных камнях и о том, как я их обходила.

С детства я очень везучий человек - это передо мной не открываются автоматические двери, это я всегда вытягиваю число 13 при любой жеребьевке и это я сажусь на единственный сломанный стул из множества. Поэтому, когда я внедряю какой-либо программный продукт, обстоятельства обязательно складываются так, что всплывают, если не все его "баги" и "фичи", то добрые 90%. Я уже привыкла к этому - и это делает мою жизнь похожей на фильм.

Мое сказочное везение не подвело меня и на моем недавнем внедрении коробки Битрикс24. Внедряла в  строительной компании средней величины. Пользователей AD (Active Directory) около 400 человек, и 105 из них являются так же пользователями корпортала, остальным корпортал для работы не нужен.

Как известно, корпортал коробка лицензируется по количеству активных пользователей, поэтому импортировать в портал лишних пользователей было нельзя. Это обстоятельство в корпортативном портале Битрикс24, слава богу, предусмотрено - в настройках AD/LDAP интеграции можно исключить определенные группы пользователей AD из импорта. Исключили, пользователи синхронизировались отлично, однако структура компании построилась не правильно:



Присутствовали пустые департаменты, у пользователей - тесок - перемешались подчиненные, а один отдел был создан 2 раза.

Стала разбираться, вставлять отладочные скрипты, писать в техническую поддержку и даже Юрию Волошину (спасибо ему за оперативные ответы).

Оказалось, что с тесками - известный баг, а у меня - первый такой реальный случай в истории. (Хотя это странно. Полные тески на руководящих должностях - далеко не редкость, особенно, когда они - отец и сын. Когда я работала в одном из подразделений РЖД, у нас там чуть ли не у каждого крупного начальника был сын, названный в честь отца, работающий начальником помельче. Мы обычно называли их между собой "старший" и "младший", но ведь в документах так писать не будешь, поэтому программное обеспечение должно предусматривать такие случаи).

С тесками мы расправились, добавив пробел к имени одного из них на стороне AD.

Дальше - интереснее. Стала я отлаживать скрипт импорта пользователей из AD, и поняла, от куда появляются пустые департаменты. Это департаменты, в которых работают пользователи, которые входят в те группы AD, которые в настройках интеграции записаны в исключения.

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

Скопировала скрипт /bitrix/modules/main/admin/user_import.php, переименовала его в user_customimport.php, в скрипте /bitrix/admin/user_import.php подключила свой скрипт вместо стандартного. глубоко переписывать алгоритм не стала - просто вставила после импорта всех пользователей удаление пустых департаментов:

$arUsersDeps=array();
$rsDepUsers = CUser::GetList(); 

while ($arDepUser = $rsDepUsers->Fetch()) 
{
$arUsersDeps[]=$arDepUser['WORK_DEPARTMENT'];    
}

$arDepFilter = Array('IBLOCK_ID'=>$ib_id);
$db_Deplist = CIBlockSection::GetList(Array($by=>$order), $arDepFilter, true);
 
$id_section_fd=array();
while($arDepSection = $db_Deplist->Fetch())
{
if (!in_array($arDepSection['NAME'],$arUsersDeps) && $arDepSection['ID']!=773){ 
$id_section_fd[]=$arDepSection['ID']; 
}

}
 
foreach($id_section_fd as $dep_id){ 

$DB->StartTransaction();
if(!CIBlockSection::Delete($dep_id))
{
$strWarning .= 'Error.';
$DB->Rollback();
}
else
$DB->Commit();
}

Вместо одного отдела ITC упорно создавались 2, сколько мы ни перепроверяли на стороне AD заполненность менеджеров и департаментов для каждого пользователя по всей иерархии. Да. на каком-то этапе мы с админом нашли множество ошибок на стороне AD, но их исправление так и не повлияло на импорт структуры.

Тогда в своем скрипте импорта пользователей я завела класс-наследник

class CLDAPCustom extends CLDAP
     {...}

И переопределила функцию GetDepartmentIdForADUser
функция бешеная - нечитабельная, а кроме того - рекурсивная, легче умереть, чем ее отладить, поэтому я просто изменила механизм проверки перед вставкой нового департамента. В оригинале поиск совпадения департамента велся только в определенном поддереве (а искало, как показала отладка, не в том поддереве, почему - так и осталось не ясно). Так как в моей компании все подразделения названы уникальными именами, сделала, чтобы искало совпадение по всей структуре.

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



На самом деле получилось все равно не так, как было задумано. В компании есть департаменты, в которых нет начальника, а все сотрудники - равноправны и подчиняются напрямую сотруднику другого отдела. Система же в таких случаях "назначает" департаменту случайного руководителя. С этим решили смириться, техподдержка Битрикс пообещала учесть это как пожелание при будущих обновлениях продукта.



среда, 20 мая 2015 г.

Себе на память: как программно добавить событие в календарь пользователя в корпортале битрикс24

$fromTs=MakeTimeStamp($_POST['from'], "MM/DD/YYYYS");
$toTs=MakeTimeStamp($_POST['to'], "MM/DD/YYYYS");


$arFieldsCalend = array(
"CAL_TYPE" =>'user',
"NAME" => $_POST['eventName'],
"DESCRIPTION" => $_POST['description'],
"SKIP_TIME" => date('H:i', $fromTs) == '00:00' && date('H:i', $toTs) == '00:00',
"IS_MEETING" => false,
"RRULE" => false
);
$arFieldsCalend['DT_FROM_TS'] = $fromTs;
$arFieldsCalend['DT_TO_TS'] = $toTs; 

$arFieldsCalend["OWNER_ID"] = 1;
$CalEventId = CCalendar::SaveEvent(
array(
'arFields' => $arFieldsCalend,
'autoDetectSection' => true
)
);

вторник, 19 мая 2015 г.

Себе на память: как опубликовать сообщение в живую ленту корпоративного портала Битрикс24 из внешнего источника.

На стороне битрикс24 делаем так:




На стороне внешнего источника делаем так:

if( $curl = curl_init() ) {
    curl_setopt($curl, CURLOPT_URL, 'https://адрес_портала/bitrix/tools/xdi_livefeed.php');
    curl_setopt($curl, CURLOPT_RETURNTRANSFER,true);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, "hash=f62167de71b87d31fed721a7e80b90a6&title=".htmlspecialchars($_POST['eventName'])."&message=".htmlspecialchars($_POST['description'])."&text_message=".htmlspecialchars($_POST['description']));
    $out = curl_exec($curl);
    echo $out;
    curl_close($curl);
  }

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

CModule::IncludeModule('socialnetwork');

$SocGroup=new CSocNetGroup;
global $USER;

$arFieldsSG=array(
   "NAME"=>$_POST['eventName'],
   "SITE_ID" => "s1",
   "DESCRIPTION"=>$_POST['eventName'],
   "ACTIVE"=>"Y",
   "VISIBLE"=>"Y",
   "OPENED"=>"Y",
   "CLOSED"=>"N",
   "SUBJECT_ID"=>4,
   "OWNER_ID"=>1,
   "INITIATE_PERMS"=>A,
   "SPAM_PERMS"=>"N",
   "SUBJECT_NAME"=>"Management Board"

);

$GroupID=CSocNetGroup::CreateGroup($USER->GetID(),$arFieldsSG);

Себе на память: программный запуск бизнес-процесса в корпортале Битрикс24

Для элемента CRM:

CModule::IncludeModule('bizproc');

CBPDocument::StartWorkflow(
      4,
      array("bizproc","CBPVirtualDocument",$LidID),
      array(),
      $arErrorsTmp
);

Для бизнес-процесса, не привзянного к CRM

$documentId = CBPVirtualDocument::CreateDocument(
    0,
    array(
     "IBLOCK_ID" => 27,
     "NAME" => "Create Notification",
     "CREATED_BY" => "user_".$GLOBALS["USER"]->GetID(),
    )
   );

   $arErrorsTmp = array();

   $wfId = CBPDocument::StartWorkflow(
   27,
    array("bizproc", "CBPVirtualDocument", $documentId),
    array_merge(array(), array("TargetUser" => "user_".intval($GLOBALS["USER"]->GetID()))),
    $arErrorsTmp
   );



Себе на память: программное создание лида в корпортале Битрикс 24

CModule::IncludeModule('crm');

$oLead = new CCrmLead;

$arFields = Array(
    "TITLE" => "EVENT CREATION ".$_POST['eventName'],
    "COMPANY_TITLE" => "EVENT CREATION ".$_POST['eventName'],
// "NAME" => "ИмяКонтакта",
// "LAST_NAME" => "ФамилияКонтакта",
// "SECOND_NAME" => "ОтчествоКонтакта",
// "POST" => "ДолжностьКонтакта",
    "ADDRESS" => $_POST['address'],
// "COMMENTS" => "КомментарийКомментарийКомментарий",
    "SOURCE_DESCRIPTION" => $_POST['description'],
// "STATUS_DESCRIPTION" => "",
//"OPPORTUNITY" => 123456,
"CURRENCY_ID" => "USD",
// "PRODUCT_ID" => "PRODUCT_1",
"SOURCE_ID" => "SELF",
    "STATUS_ID" => "NEW",
    "ASSIGNED_BY_ID" => $_POST['owner'],
    "UF_CRM_1431935906" => true,
    "UF_CRM_1431950264" => $eventID,
);

$LidID=$oLead->Add($arFields);