Bitrix24 Case: Adding Files from a Task Attached to a Deal to a Custom Field of the Deal Itself
This post automatically translated
Today we will consider a solution to a real problem, implemented by our specialists and which can be useful for developers involved in finalizing and expanding the functionality of the boxed version of Bitrix24 for specific tasks.
The task is as follows: When saving a task linked to a deal, duplicate the files attached to this task in the deal in a custom field with multiple values of the “File” type. This feature should be available only to users of a specific department, assigned as responsible for this task.
To solve this problem, we will use the events of the Tasks module:
onTaskAdd - adding a task;
onTaskUpdate - updating a task;
At first glance, the task is simple, you need to find where the files are stored in the task and transfer them to the deal using a combination of:
CFile::MakeFileArray($ID_file) - prepare files;
Transfer files to the deal via the Update method. But, unfortunately, this method is useless here, since the task stores files using a different principle using WebDav (according to the mail server principle) in the form of name hashes without an extension. This system serves to save space on the resource, since with this method, when loading a new file, a hash is created and compared with the existing hashes in the system and, if such a hash already exists, then the duplicate is not loaded, and the file hash is linked to the task. Accordingly, when deleting a file from a task, only the hash is unlinked. While the data in user fields of the “File” type is stored according to the old scheme.
Thus, we need to get a file from the disk and attach it to the transaction according to the classic scheme, having previously checked for duplicates, while preserving the files already attached to the transaction, since when updating the field, the previous files are deleted.
To get data from WebDav, D7 and working with the disk \Bitrix\Disk\Driver come to our aid and, after a little digging in the official documentation and on the network, we came to the following option:
$userFieldManager = \Bitrix\Disk\Driver::getInstance()->getUserFieldManager();
$userFieldManager->loadBatchAttachedObject($files);
foreach ($files as $attachedId){
//...
$attachedObject = $userFieldManager->getAttachedObjectById($attachedId);
//...
}
$files – array of identifiers in the WebDav system received from the task;
$attachedObject – returns an object with information and methods about the attached file in the WebDav system. In $attachedObject there is a function getFileId() that returns the file identifier in the classic version digestible by the CFile class. Having this identifier, you can use the classic processing method CFile::MakeFileArray() plus a check for the presence of such a file in the transaction.
UF_CRM_1492767878 - the identifier of the custom field is generated after the field is created.
We get the code:
$userFieldManager = \Bitrix\Disk\Driver::getInstance()->getUserFieldManager();
$userFieldManager->loadBatchAttachedObject($files);
foreach ($files as $attachedId){
$key = false;
$attachedObject = $userFieldManager->getAttachedObjectById($attachedId);
if(sizeof($arFields['UF_CRM_1492767878']) > 0){
$key = array_search($attachedObject->getName(), array_column($arFileInfo, 'ORIGINAL_NAME'));
}
if ($key === false) {
$arFields['UF_CRM_1492767878'][] = CFile::MakeFileArray($attachedObject->getFileId());
}
}
Now the received field must be written into the deal:
if(isset($arFields['UF_CRM_1492767878']) && sizeof($arFields['UF_CRM_1492767878']) > 0) {
$crm = new CCrmDeal(false);
$crm->Update($dealId, $arFields, true, true, ['DISABLE_USER_FIELD_CHECK' => true, 'REGISTER_SONET_EVENT' => true]);
}
We also need to save the files we’ve already received. We did it like this:
$obDeal = CCrmDeal::GetList([], ['ID' => $dealId], ['*', 'UF_*']);
$deal = $obDeal->GetNext();
$dealFiles = $deal['UF_CRM_1492767878'];
if(sizeof($dealFiles) > 0){
foreach ($dealFiles as $f){
$arFields['UF_CRM_1492767878'][] = $f;
$arFileInfo[] = CFile::GetByID($f)->GetNext();
}
}
Here:
$arFileInfo – contains an array of files already existing in the transaction. We will need it to check by file name. Also added a check by department.
The final version of the entire handler:
AddEventHandler("tasks", "OnTaskAdd", ["TaskEvent", "OnTaskAddHandler"]);
AddEventHandler("tasks", "OnTaskUpdate", ["TaskEvent", "OnTaskUpdateHandler"]);
class TaskEvent {
function OnTaskAddHandler($ID)
{
self::syncDealFiles($ID);
}
function OnTaskUpdateHandler($ID)
{
self::syncDealFiles($ID);
}
private static function syncDealFiles($ID)
{
CModule::IncludeModule('tasks');
CModule::IncludeModule('crm');
$obTask = CTasks::GetList([], ['ID' => $ID], ['*', 'UF_*']);
$task = $obTask->GetNext();
list(, $dealId) = explode('_', array_shift($task['UF_CRM_TASK']));
$files = $task['UF_TASK_WEBDAV_FILES']; $arFileInfo = [];
$user = CUser::GetList(($by = 'id'), ($order = 'asc'), ['ID' => $task['RESPONSIBLE_ID'], 'UF_DEPARTMENT' => 28], ['SELECT' => ['*', 'UF_*']])->GetNext();
if (is_numeric($dealId) && sizeof($files) > 0 && isset($user['ID']))
{
$obDeal = CCrmDeal::GetList([], ['ID' => $dealId], ['*', 'UF_*']);
$deal = $obDeal->GetNext();
$dealFiles = $deal['UF_CRM_1492767878'];
if(sizeof($dealFiles) > 0)
{
foreach ($dealFiles as $f)
{
$arFields['UF_CRM_1492767878'][] = $f;
$arFileInfo[] = CFile::GetByID($f)->GetNext();
}
}
$userFieldManager = \Bitrix\Disk\Driver::getInstance()->getUserFieldManager();
$userFieldManager->loadBatchAttachedObject($files);
foreach ($files as $attachedId)
{
$key = false;
$attachedObject = $userFieldManager->getAttachedObjectById($attachedId);
if(sizeof($arFields['UF_CRM_1492767878']) > 0)
{
$key = array_search($attachedObject->getName(), array_column($arFileInfo, 'ORIGINAL_NAME'));
}
if ($key === false)
{
$arFields['UF_CRM_1492767878'][] = CFile::MakeFileArray($attachedObject->getFileId());
}
}
if(isset($arFields['UF_CRM_1492767878']) && sizeof($arFields['UF_CRM_1492767878']) > 0)
{
$crm = new CCrmDeal(false);
$crm->Update($dealId, $arFields, true, true, ['DISABLE_USER_FIELD_CHECK' => true, 'REGISTER_SONET_EVENT' => true]);
}
}
}
}