Bitrix 自行审核

大家好。

当我在Bitrix中寻找有关日志记录(事件审计)的信息时,Habr上没有任何内容,但是其余的还有其他内容,但是谁能在那里找到呢?

为了补充知识基础,我决定写这篇文章:分享我的经验并警告可能会发生的情况。

问题的提法


我的任务是开发最简单的广告结构会计系统,根据国家合同的规定,该系统应在Bitrix(版本15)的基础上工作。

可以在Bitrix一侧骑自行车,但我认为相对于客户而言这是不诚实的,Bitrix的功能得到了最大程度的利用:

  • 用户认证
  • 数据存储系统(EAV)
  • 数据编辑器
  • 审核事件处理程序
  • 授权用户动作的角色模型
  • 用户管理
  • 使用目录

本文主要讨论审核事件,我还将介绍一些有关授权以及将自定义书签添加到信息块记录卡的信息。

审核事件


在开发的系统中,通过API处理数据(向地图添加新对象并更改其坐标),并通过Bitrix编辑器编辑对象的其他参数,因为API不会处理所有数据更改请求,因此仅API中的审核将无法完成,这意味着我们需要对Bitrix进行审核。

不是我不知道什么是审核,而是我不必经常编写审核。通常,这可以通过将行的旧版本(旧数据)写入单独的表中来解决,并且实现完全在DBMS一侧执行。而且,我们同时具有更改之前的状态和更改之后的状态。

要对DBMS触发器进行审核,或对Bitrix事件进行审核,两者之间的差异并不大。
用于处理的事件已在脚本“bitrix / php_interface / init.php “,如果没有文件,则需要创建它:

use Topliner\Scheme\Logger;
//  
require_once($_SERVER['DOCUMENT_ROOT'] . '/vendor/autoload.php');
//        
if (!defined('IBLOCK_MODULE')) {
    define('IBLOCK_MODULE', 'iblock');
}
//    
AddEventHandler(IBLOCK_MODULE, 'OnIBlockElementAdd',
    Array(Logger::class, 'OnAdd'));
//     ,  -    ,              ,   

这是一个处理程序的示例,在我的实现中有多个处理程序,可以在存储库中查看代码,链接将在文章结尾。

用于处理每个事件的事件的方法的签名具有自己的签名,我们将在Bitrix文档或首次亮相中专门查看该签名,并查看源代码(更清楚的是,在调试过程中,所有事件和调用处理程序的时刻也可以在源代码中看到)。

在这里,我们面对这样一个事实,当使用DBMS时,我们既有当前(旧)数据又要记录的数据(新),而当使用Bitrix时,我们既有旧数据也有新数据,但没有不会同时存在新旧事物。

另一个问题是,在更改数据之前有多个事件,我无法理解它们之间的区别,同一故事是在更改数据后触发多个事件时,目光注视着选择的财富,实际上这只是选择的一种幻想。

我对Bitrix的拙见
, , _ , ( Delphi PHP), .

depricated «», .

« » ( ) «» (, , ) , . , «» .

, :)

, , , , , , , ? .


使用全局状态


要在事件处理程序之间保存和传递状态,您必须具有全局变量。全局变量是无法重构的,因此我使用类的静态属性:

class Logger
{
    const CHANGE = 'change';
    const REMOVE = 'remove';
    const CREATE = 'create';
    const UNDEFINED = 'undefined';

    public static $operation = self::UNDEFINED;

    const NO_VALUE = [];

    private static $fields = self::NO_VALUE;
    private static $properties = self::NO_VALUE;
    private static $names = self::NO_VALUE;
}

在这里,您需要对“字段”和“属性”的用法进行解释。字段是每个信息块记录所具有的参数(标识符,名称,信息块“父”),属性是特定于特定类型信息块的记录的参数,在EAV模型中,这些是属性及其值。

“名称”是属性的名称,在一种情况下,我们具有属性和名称的标识符,在另一种情况下,我们仅具有标识符,并且为了在日志中保留漂亮的记录,我们需要保存名称(我们当然可以减去标识符,但出于某些原因,您不想这样做)。

“操作”是当前操作,对于使用“字段”,Bitrix会给出特定的事件,当使用“属性”(函数“ SetPropertyValues”和“ SetPropertyValuesEx”)时,操作类型没有事件,在调用之前和之后都有一个事件,但是没有知道执行了什么操作(添加/更新/删除),因此该操作也需要从处理程序转移到处理程序。

也许我没有弄清楚,在Bitrix中您可以同时看到其原样以及它将如何变化,或者也许有必要分别记录之前和之后的状态,但是出于某种原因,我决定日志条目应采用``属性''格式“%name%”是更改前的“ %%”,因此“>”之后变为“%”后的“%”,因此在维护全局状态方面存在很多问题。

确定是否需要更改


奇怪的是,但是将调用我们的处理程序来更改任何信息块的任何记录。
因此,在处理程序中,我们需要了解在参数中接收到的哪个信息块的记录。
一条记录的信息块具有两个值:信息块本身('IBLOCK_ID')和它的段('IBLOCK_SECTION_ID'),段标识符有时位于带有索引'IBLOCK_SECTION_ID'的值的数组中,有时位于'IBLOCK_SECTION'的值的数组中,因此我通过以下方式读取段标识符这两个键的优先级都为“ IBLOCK_SECTION”。

此外,在某些情况下,原则上不能确定段标识符,因此,仅根据信息块标识符来检查是否需要向审核日志中添加条目。

确定信息块和部分后,我们决定需要在日志中注册事件。

更改前保存状态


每个信息块条目均由两部分组成,一部分是“字段”,这些参数可以通过方法进行更改:

  • CIBlockElement ::添加()
  • CIBlockElement ::更新()
  • CIBlockElement ::删除()

另一部分是“属性”方法:
  • CIBlockElement :: SetPropertyValues()
  • CIBlockElement :: SetPropertyValuesEx()

我不记得为什么,但是最好不要在代码中使用CIBlockElement :: SetPropertyValuesEx()。

事件处理程序中数据的每个部分彼此独立,因此,通过单击“保存”按钮,可以在审核日志中创建两个条目。

“字段”和“属性”的事件签名不同,在处理“字段”的部分中,我们保存字段的当前状态并将值设置为“操作”,在处理“属性”的部分中,我们保存属性及其名称。

我们当然是在“之前”处理程序中执行此操作的。

变更注册


在“之后”处理程序中,我们写入日志。

编译记录中的差异列表时,属性可能会出现多个值的问题。记录它们的“之前”和“之后”状态的格式差异很大,以至于无法推断出另一个,因此,在确定是否注册更改时,您总是会得出结论,认为注册是必要的。
存储库中src / Topliner / Scheme / Logger.php文件中审核的源代码

将自定义标签添加到信息块卡(在Bitrix分类器中)


要添加卡,使用类似于审核的方法-我们添加一个处理程序:

AddEventHandler('main', 'OnAdminIBlockElementEdit',
    Array(PermitTab::class, 'OnInit'));

在“ OnInit”方法中,我们定义了在Bitrix编辑器中使用选项卡的其他方法:

class PermitTab
{
    const LINKS = 'links';

    function OnInit($arArgs)
    {
        $permits = BitrixScheme::getPermits();
        $pubPermits = BitrixScheme::getPublishedPermits();
        $blockId = (int)$arArgs['IBLOCK']['ID'];
        $letShow = $blockId === $permits->getBlock()
            || $blockId === $pubPermits->getBlock();
        $result = false;
        if ($letShow) {
            $result = [
                'TABSET' => 'LinksToConstructs',
                'GetTabs' => [static::class, 'GetTabs'],
                'ShowTab' => [static::class, 'ShowTab'],
                'Action' => [static::class, 'Action'],
                'Check' => [static::class, 'Check'],
            ];
        }

        return $result;
    }
}

首先,当然,我们检查该信息块是否需要显示用户选项卡,必要时设置方法。

这里没有什么特别要说的,请参阅src / Topliner / Scheme / PermitTab.php存储库中的源,并阅读文档。

授权用户动作的角色模型


Bitrix的授权方法适用于线性权限范围,也就是说,一个场景可以拥有第一个和第二个,但不能拥有第三个,而另一个可以拥有第二个和第三个,而不能拥有第一个,您不能直接实现Bitrix。

对于Bitrix中这种棘手的场景,只需检查一些权限,然后在执行操作的代码中即可查看用户是否具有权限,并允许一个角色的第一个和第二个角色以及该角色的第二个和第三个角色。

        $constSec = BitrixScheme::getConstructs();
        $isAllow = CIBlockSectionRights::UserHasRightTo(
            $constSec->getBlock(), $constSec->getSection(),
            BitrixPermission::ELEMENT_ADD, false);
        if (!$isAllow) {
            $output['message'] = 'Forbidden, not enough permission;';
        }

  1. $ constSec-> getBlock()-信息块标识符
  2. $ constSec-> getSection()-段标识符
  3. BitrixPermission :: ELEMENT_ADD-权限代码

在必要的信息块和部分的分类器(信息块目录)中,为角色分配适当的权限。将角色分配给用户,一切都在您的控制之下。

认证方式


如果您需要某些页面的用户身份验证,只需添加:

<?php
define("NEED_AUTH", true);
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/header.php");
?>

//      

<?php
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/footer.php");
?>

当加载这样的页面时,如果用户尚未登录,Bitrix将给出一个登录表单。

如果您只需要使用某种脚本来连接Bitrix,则:

require_once($_SERVER['DOCUMENT_ROOT']
    . '/bitrix/modules/main/include/prolog_before.php');

使用目录


Bitrix中的“新”(截至2019年11月)目录称为“ HighloadBlock”,使用它们有点不标准。

每个Hyload块都可以存储在单独的表中,因此,每次需要确定要读取的表的名称时,就可以访问目录。为了不经常这样做,您需要在Bitrix中创建一个类的实例来访问数据。在两行中创建一个实例:

CModule::IncludeModule('highloadblock');
$entity = HighloadBlockTable::compileEntity('ConstructionTypes');
$reference = $entity->getDataClass();

其中“ ConstructionTypes”是参考代码。为了

不经常写这两行,您可以编写一个类来为我们创建参考实例(src / Topliner / Bitrix / BitrixReference.php)。

接下来,我们将与常规Bitrix ORM类(D7)一起使用实例:

/* @var $reference DataManager */
$data = $reference::getList(array(
    'select' => array('UF_NAME', 'UF_XML_ID'),
    'filter' => array('UF_TYPE_ID' => 0)
    ));

存储库(小心,很多复制粘贴操作)

感谢您的关注。

All Articles