Developing a bulletin board using 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1189
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    813
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Developing a Classifieds Board on 1C-Bitrix

A classifieds board is more complex than it first appears. It is not simply a "list of products." Users post their own listings, moderators review content, there must be search with multi-parameter filters, management of listing statuses and expiry dates, paid promotion, and a seller's personal account. Bitrix provides a good foundation — information blocks, the user module, faceted search — but the specific board logic must be built on top.

Information Block Structure for Listings

The info block is the primary storage. Symbolic code: classifieds. Type: ADS.

Required info block properties:

Property Code Type
Price PRICE Number
Deal type DEAL_TYPE List (Sell/Buy/Exchange/Give away)
City CITY Reference (HL block)
Seller phone PHONE String
Status AD_STATUS List (Active/Moderation/Rejected/Expired)
Expiry date EXPIRE_DATE Date
Views VIEW_COUNT Number
VIP IS_VIP Flag
User USER_ID Number (FK on b_user)
Photos PHOTOS File (multiple)

The info block section is the listing category (Vehicles, Real Estate, Electronics, etc.). The section hierarchy is a tree via b_iblock_section.

User-Submitted Listings

The listing submission form is at /ads/add/. Key point: the user is uploading content, so spam protection and mandatory moderation are required.

// /local/components/local/ads.add/class.php

namespace Local\Ads;

class AdsAddComponent extends \CBitrixComponent
{
    public function executeComponent(): void
    {
        if (!$GLOBALS['USER']->IsAuthorized()) {
            LocalRedirect('/auth/?backurl=/ads/add/');
            return;
        }

        if ($this->request->isPost() && check_bitrix_sessid()) {
            $this->addAd();
        }

        $this->includeComponentTemplate();
    }

    private function addAd(): void
    {
        $el = new \CIBlockElement();

        // Process uploaded photos
        $photos = [];
        if (!empty($_FILES['PHOTOS']['tmp_name'])) {
            foreach ($_FILES['PHOTOS']['tmp_name'] as $i => $tmpName) {
                if (is_uploaded_file($tmpName)) {
                    $photos[] = [
                        'name'     => $_FILES['PHOTOS']['name'][$i],
                        'size'     => $_FILES['PHOTOS']['size'][$i],
                        'tmp_name' => $tmpName,
                        'type'     => $_FILES['PHOTOS']['type'][$i],
                    ];
                }
            }
        }

        $adId = $el->Add([
            'IBLOCK_ID'      => CLASSIFIEDS_IBLOCK_ID,
            'NAME'           => htmlspecialchars($this->request->getPost('title')),
            'DETAIL_TEXT'    => htmlspecialchars($this->request->getPost('description')),
            'IBLOCK_SECTION_ID' => (int)$this->request->getPost('category_id'),
            'ACTIVE'         => 'N', // Initially inactive, pending moderation
            'PROPERTY_VALUES' => [
                'PRICE'      => (float)$this->request->getPost('price'),
                'DEAL_TYPE'  => $this->request->getPost('deal_type'),
                'PHONE'      => htmlspecialchars($this->request->getPost('phone')),
                'USER_ID'    => $GLOBALS['USER']->GetID(),
                'AD_STATUS'  => 'MODERATION',
                'EXPIRE_DATE' => date('d.m.Y', strtotime('+30 days')),
                'VIEW_COUNT'  => 0,
                'PHOTOS'     => $photos,
            ],
        ]);

        if ($adId) {
            $this->arResult['SUCCESS'] = true;
            $this->arResult['AD_ID']   = $adId;
            // Notify moderators
            $this->notifyModerators($adId);
        } else {
            $this->arResult['ERROR'] = $el->LAST_ERROR;
        }
    }
}

Moderation

The moderator page is the standard info block element list, filtered by AD_STATUS = MODERATION. The moderator's actions change the status and active flag:

// Approve a listing
$el = new \CIBlockElement();
$el->Update($adId, ['ACTIVE' => 'Y']);
\CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, 'ACTIVE', 'AD_STATUS');

// Reject with a reason
\CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, [
    'AD_STATUS'     => 'REJECTED',
    'REJECT_REASON' => $reason,
]);
$el->Update($adId, ['ACTIVE' => 'N']);

// Notify the user
$event = new \Bitrix\Main\Mail\Event([
    'EVENT_NAME' => 'AD_MODERATION_RESULT',
    'LID'        => SITE_ID,
    'C_FIELDS'   => [
        'AD_ID'  => $adId,
        'STATUS' => $status,
        'REASON' => $reason,
    ],
]);
$event->send();

Search and Filtering

The listing filter is a critical UX element. For simple search — the standard CIBlockElement::GetList() with a filter. For high-load projects — Bitrix faceted search (the search module) or ElasticSearch integration.

Example filter with a price range:

$filter = [
    'IBLOCK_ID'  => CLASSIFIEDS_IBLOCK_ID,
    'ACTIVE'     => 'Y',
    'SECTION_ID' => $categoryId,
    '>PROPERTY_PRICE'  => $priceMin,
    '<PROPERTY_PRICE'  => $priceMax,
    'PROPERTY_CITY'    => $cityId,
    'PROPERTY_AD_STATUS' => 'ACTIVE',
];

$sort = ['PROPERTY_IS_VIP' => 'DESC', 'DATE_ACTIVE_FROM' => 'DESC'];

VIP listings always appear at the top via the IS_VIP DESC sort flag.

Listing Expiry

An agent checks for expired listings:

// Register agent in /local/php_interface/init.php
\CAgent::AddAgent(
    'Local\\Ads\\ExpireAgent::run();',
    'local.ads',
    'N',
    86400, // Once per day
);

// Agent method
class ExpireAgent
{
    public static function run(): string
    {
        $today = date('d.m.Y');
        $result = \CIBlockElement::GetList(
            [],
            [
                'IBLOCK_ID'         => CLASSIFIEDS_IBLOCK_ID,
                'ACTIVE'            => 'Y',
                '<PROPERTY_EXPIRE_DATE' => $today,
            ],
            false,
            false,
            ['ID', 'PROPERTY_USER_ID']
        );

        while ($ad = $result->Fetch()) {
            $el = new \CIBlockElement();
            $el->Update($ad['ID'], ['ACTIVE' => 'N']);
            \CIBlockElement::SetPropertyValues($ad['ID'], CLASSIFIEDS_IBLOCK_ID, 'EXPIRED', 'AD_STATUS');
            // Notify the user
        }

        return 'Local\\Ads\\ExpireAgent::run();';
    }
}

User Personal Account

The page /personal/ads/ — a list of the current user's listings:

$myAds = \CIBlockElement::GetList(
    ['DATE_CREATE' => 'DESC'],
    [
        'IBLOCK_ID'       => CLASSIFIEDS_IBLOCK_ID,
        'PROPERTY_USER_ID' => $GLOBALS['USER']->GetID(),
    ],
    false,
    ['nPageSize' => 20],
    ['ID', 'NAME', 'ACTIVE', 'PROPERTY_AD_STATUS', 'PROPERTY_EXPIRE_DATE', 'PROPERTY_VIEW_COUNT']
);

User actions: edit, deactivate, extend (if the listing has expired), delete.

View Counter

Each time the detail page is opened — increment the counter. Via AJAX, to avoid slowing down the initial render and to avoid counting bots:

// /local/ajax/ad_view.php
$adId = (int)$_POST['ad_id'];
if ($adId > 0) {
    $current = (int)\CIBlockElement::GetProperty(CLASSIFIEDS_IBLOCK_ID, $adId, [], ['CODE' => 'VIEW_COUNT'])->GetNext()['VALUE'];
    \CIBlockElement::SetPropertyValues($adId, CLASSIFIEDS_IBLOCK_ID, $current + 1, 'VIEW_COUNT');
}

Development Timelines

Option Composition Duration
Basic board Info block, posting, listing, filter 8–12 days
With moderation and personal account + Moderation, personal account, expiry agent 12–18 days
Full-featured + VIP listings, search, notifications, statistics 20–30 days