Bitrix Lakukan audit sendiri

Halo semuanya.

Ketika saya mencari informasi tentang pencatatan (audit peristiwa) di Bitrix, tidak ada apa-apa tentang Habr, tetapi ada sesuatu yang lain dalam sisanya, tetapi siapa yang akan menemukannya di sana?

Untuk mengisi kembali basis pengetahuan, saya memutuskan untuk menulis artikel ini: berbagi pengalaman saya dan memperingatkan tentang kemungkinan penggaruk.

Perumusan masalah


Tugas saya adalah mengembangkan sistem akuntansi paling sederhana untuk struktur periklanan, berdasarkan ketentuan kontrak negara, sistem tersebut harus bekerja berdasarkan Bitrix (versi 15).

Dimungkinkan untuk bersepeda segala sesuatu di sisi Bitrix, tetapi saya memutuskan bahwa itu akan terlalu tidak jujur ​​dalam kaitannya dengan pelanggan, fungsi Bitrix digunakan secara maksimal:

  • otentikasi pengguna
  • sistem penyimpanan data (EAV)
  • editor data
  • penangan acara audit
  • model peran untuk mengotorisasi tindakan pengguna
  • manajemen pengguna
  • bekerja dengan direktori

Artikel ini terutama tentang acara audit, saya juga akan berbicara sedikit tentang otorisasi dan menambahkan bookmark kustom ke kartu catatan blok info.

Acara Audit


Dalam sistem yang dikembangkan, bekerja dengan data dilakukan melalui API (menambahkan objek baru ke peta dan mengubah koordinatnya), parameter lain dari objek diedit melalui editor Bitrix, karena API tidak memproses semua permintaan perubahan data, maka audit hanya dalam API tidak akan lengkap, yang berarti kami membutuhkan audit di sisi Bitrix.

Bukannya saya tidak tahu apa itu audit, tetapi saya tidak harus sering menulisnya. Biasanya ini diselesaikan dengan menulis versi lama dari baris (data lama) ke dalam tabel terpisah, dan implementasi dilakukan sepenuhnya di sisi DBMS. Selain itu, kami secara bersamaan memiliki status sebelum perubahan dan status setelah perubahan.

Untuk menulis audit tentang pemicu DBMS, atau untuk menulis audit tentang peristiwa Bitrix, perbedaannya tidak besar.
Acara untuk pemrosesan terdaftar dalam skrip "bitrix / php_interface / init.php ", jika tidak ada file, maka Anda harus membuatnya:

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'));
//     ,  -    ,              ,   

Ini adalah contoh dari satu penangan, dalam implementasi saya ada beberapa di antaranya, kode dapat dilihat di repositori, tautannya ada di akhir artikel.

Tanda tangan dari metode untuk memproses acara untuk setiap acara memiliki sendiri, yang secara khusus kita lihat dalam dokumentasi Bitrix atau debutnya dan lihat sumbernya (lebih jelas, semua peristiwa dan momen penangan panggilan juga dapat dilihat di sumber selama debugging).

Dan di sini kita dihadapkan dengan fakta bahwa ketika bekerja dengan DBMS, kita memiliki data saat ini (lama) dan data yang akan direkam (baru), dan ketika bekerja dengan Bitrix, kita memiliki data lama atau data baru, tetapi tidak ketika tidak akan ada yang lama dan baru pada saat yang sama.

Masalah lain adalah ada beberapa peristiwa sebelum mengubah data, dan saya tidak bisa memahami perbedaan di antara mereka, cerita yang sama adalah ketika beberapa peristiwa dipicu setelah data diubah, mata menatap dari banyak pilihan, sebenarnya itu hanyalah ilusi pilihan.

pendapat saya yang sederhana tentang Bitrix
, , _ , ( Delphi PHP), .

depricated «», .

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

, :)

, , , , , , , ? .


Menggunakan negara global


Untuk menyimpan dan melewati status antara penangan acara, Anda harus memiliki variabel global. Variabel global adalah sesuatu yang tidak dapat di refactored, jadi saya menggunakan properti statis kelas:

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;
}

Di sini Anda perlu membuat penjelasan tentang penggunaan "bidang" dan "properti". Bidang adalah parameter yang dimiliki oleh setiap catatan infoblock (pengidentifikasi, nama, “induk” infoblock), properti adalah parameter khusus untuk rekaman jenis infoblock tertentu, dalam model EAV, ini adalah atribut dan nilainya.

"Nama" adalah nama atribut, di satu peristiwa kami memiliki pengidentifikasi atribut dan nama, di acara lain kami hanya memiliki pengidentifikasi, dan untuk catatan yang indah di jurnal kita perlu menyimpan nama (tentu saja kita dapat mengurangi pengidentifikasi, tetapi untuk beberapa alasan Anda tidak ingin).

"Operasi" adalah operasi saat ini, untuk bekerja dengan "bidang" Bitrix memberikan peristiwa spesifik, ketika bekerja dengan "properti" (fungsi "SetPropertyValues" dan "SetPropertyValuesEx"), tidak ada peristiwa dengan jenis operasi, ada acara sebelum dan sesudah panggilan, tetapi tidak Diketahui operasi apa yang dilakukan (tambah / perbarui / hapus), oleh karena itu operasi juga perlu ditransfer dari handler ke handler.

Mungkin saya tidak mengetahuinya, dan dalam Bitrix Anda dapat secara bersamaan melihat nilai-nilai seperti apa dan bagaimana nantinya, atau mungkin perlu untuk secara terpisah merekam keadaan sebelum dan keadaan setelah, tetapi untuk beberapa alasan saya memutuskan bahwa entri log harus dalam format “properti % name% adalah% nilai sebelum perubahan% => itu menjadi% nilai setelah% ”dan karena itu ada banyak masalah dengan mempertahankan keadaan global.

Menentukan apakah perubahan diperlukan


Cukup aneh, tetapi pawang kami akan dipanggil untuk mengubah catatan blok informasi apa pun.
Oleh karena itu, dalam handler, kita perlu memahami catatan blok informasi mana yang kami terima dalam parameter.
Infoblock catatan ditandai oleh dua nilai: infoblock itu sendiri ('IBLOCK_ID') dan bagiannya ('IBLOCK_SECTION_ID'), dan pengenal bagian kadang-kadang terletak pada array nilai dengan indeks 'IBLOCK_SECTION_ID', dan kadang-kadang 'IBLOCK_SECTION', jadi terkadang saya membaca pengenal bagian oleh kedua kunci dengan prioritas untuk 'IBLOCK_SECTION'.

Selain itu, dalam beberapa situasi, pengidentifikasi bagian tidak dapat ditentukan pada prinsipnya, oleh karena itu, pemeriksaan untuk kebutuhan untuk menambahkan entri ke log audit harus dilakukan hanya berdasarkan pengidentifikasi blok informasi.

Setelah menentukan blok dan bagian informasi, kami memutuskan perlunya mendaftarkan acara di jurnal.

Menyimpan status sebelum perubahan


Setiap entri infoblock terdiri dari dua bagian, satu bagian adalah "bidang", dan parameter ini diubah dengan metode:

  • CIBlockElement :: Tambah ()
  • CIBlockElement :: Perbarui ()
  • CIBlockElement :: Delete ()

Bagian lainnya adalah metode "properti":
  • CIBlockElement :: SetPropertyValues ​​()
  • CIBlockElement :: SetPropertyValuesEx ()

Saya tidak ingat mengapa, tetapi lebih baik jangan menggunakan CIBlockElement :: SetPropertyValuesEx () dalam kode saya.

Setiap bagian dari data dalam pengendali eventnya terpisah dari yang lain, oleh karena itu, dengan mengklik tombol "Simpan", dua entri dalam log audit dapat dibuat.

Tanda tangan acara untuk "bidang" dan "properti" berbeda, di bagian dengan memproses "bidang" kami menyimpan keadaan saat ini untuk bidang dan mengatur nilai untuk operasi, di bagian dengan memproses "properti" kami menyimpan properti dan namanya.

Kami melakukan ini tentu saja di pawang "sebelum".

Ubah Registrasi


Dalam penangan "setelah", kita menulis ke log.

Saat menyusun daftar perbedaan dalam rekaman, ada masalah dengan atribut yang dapat mengambil banyak nilai. Format untuk merekam keadaan mereka "sebelum" dan menyatakan "setelah" sangat berbeda sehingga satu tidak dapat menyimpulkan yang lain, jadi ketika menentukan apakah akan mendaftar perubahan, Anda akan selalu menyimpulkan bahwa pendaftaran diperlukan.
Kode sumber audit dalam repositori di file src / Topliner / Scheme / Logger.php .

Menambahkan tab khusus ke kartu infoblock (di penglasifikasi Bitrix)


Untuk menambahkan kartu, pendekatan yang mirip dengan audit digunakan - kami menambahkan penangan:

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

Dalam metode 'OnInit', kami mendefinisikan metode lain untuk bekerja dengan tab di editor 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;
    }
}

Pertama, tentu saja, kami memeriksa bahwa untuk blok informasi ini kami perlu menunjukkan tab pengguna kami, jika perlu, kami menetapkan metode.

Tidak ada yang istimewa untuk diceritakan di sini, lihat sumber di repositori src / Topliner / Skema / PermitTab.php , baca dokumentasi.

Model peran untuk mengotorisasi tindakan pengguna


Metode Bitrix untuk otorisasi bekerja dengan skala hak linear, yaitu, skenario di mana seseorang dapat memiliki yang pertama dan kedua, tetapi bukan yang ketiga, dan yang lain bisa menjadi yang kedua dan ketiga, tetapi bukan yang pertama, Anda tidak dapat mengimplementasikan Bitrix secara langsung.

Untuk skenario rumit seperti itu di Bitrix, hanya ada pemeriksaan beberapa izin, dan dalam kode saat melakukan operasi, Anda melihat apakah pengguna memiliki izin atau tidak, dan Anda mengizinkan peran pertama dan kedua, dan yang kedua dan ketiga memungkinkan peran lain.

        $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 () - pengidentifikasi blok informasi
  2. $ constSec-> getSection () - pengidentifikasi bagian
  3. BitrixPermission :: ELEMENT_ADD - kode izin

Di pengklasifikasi (direktori blok informasi) di blok dan bagian informasi yang diperlukan, Anda menetapkan hak yang sesuai untuk peran tersebut. Bagikan peran kepada pengguna dan semuanya akan berada di bawah kendali Anda.

Autentikasi


Jika Anda memerlukan otentikasi pengguna untuk beberapa halaman, cukup tambahkan:

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

//      

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

Saat memuat halaman seperti itu, Bitrix akan memberikan formulir login jika pengguna belum masuk.

Jika Anda hanya perlu menghubungkan Bitrix dalam beberapa jenis skrip, maka:

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

Bekerja dengan direktori


Direktori "baru" (per November 2019) di Bitrix disebut "HighloadBlock", bekerja dengan mereka sedikit tidak standar.

Setiap blok Hyload dapat disimpan dalam tabel terpisah, oleh karena itu, untuk mengakses direktori, setiap kali Anda perlu menentukan nama tabel untuk dibaca. Agar tidak terus-menerus melakukan ini, dalam Bitrix Anda harus membuat instance kelas untuk mengakses data. Sebuah instance dibuat dalam dua baris:

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

Di mana 'ConstructionTypes' adalah kode referensi. Agar

tidak terus-menerus menulis dua baris ini, Anda dapat menulis kelas yang akan membuat instance referensi untuk kita ( src / Topliner / Bitrix / BitrixReference.php ).

Selanjutnya, kami bekerja dengan instance seperti pada kelas ORM Bitrix biasa (D7):

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

Repositori (hati-hati, banyak copy-paste)

Terima kasih atas perhatian Anda.

All Articles