Skip to content

Developer notes

app/addons/kv_vendor_holiday_mode/
├── addon.xml scheme 3.0, priority 2100, MULTIVENDOR only
├── init.php hook registration
├── func.php API functions and hook handlers
├── controllers/backend/kv_vendor_holiday_mode.php admin + vendor panel pages
├── controllers/backend/kv_vendor_holiday_mode_license.php "Check now" / cron license check
├── controllers/frontend/checkout.pre.php "block" mode: redirect checkout → cart
├── controllers/frontend/checkout.post.php cart/checkout warnings
├── controllers/frontend/call_requests.pre.php refuses "Buy now with one click" requests (Call requests add-on)
├── schemas/menu/menu_multivendor.post.php Vendors → Vacation mode (admin, vendor panel without configurator)
├── schemas/menu/menu_vendor.post.php Seller tools → Vacation mode (Vendor panel configurator)
├── schemas/permissions/admin.post.php view_/manage_kv_vendor_holiday_mode privileges
├── schemas/permissions/vendor_multivendor.post.php vendor access (only if "Allow vendors" is on)
├── schemas/block_manager/blocks.post.php block cache invalidation (product/vendor blocks, main content block)
├── schemas/block_manager/block_cache_properties.post.php license state in every block cache key
└── Tygh/Addons/KvVendorHolidayMode/
├── Enum/{ProductModes,CheckoutModes,HolidayStates}.php
├── License/Client.php license check client (generated from the shared Kavio Labs template)
└── Upgrade/Synchronizer.php self-healing in-place upgrades
design/backend/templates/addons/kv_vendor_holiday_mode/
├── views/kv_vendor_holiday_mode/{manage,update}.tpl
├── components/state_label.tpl
├── license/info.tpl License tab
└── hooks/companies/{list_extra_links,tools_list}.post.tpl
var/themes_repository/responsive/templates/addons/kv_vendor_holiday_mode/
├── components/{banner,unavailable}.tpl
└── hooks/{companies/products.pre, companies/view.pre, products/main_info_title.post,
products/product_additional_info.post, products/add_to_cart.override, buttons/add_to_cart.override,
call_requests/call_request_button.override, index/styles.post}.tpl
var/themes_repository/responsive/css/addons/kv_vendor_holiday_mode/styles.less
var/langs/en/addons/kv_vendor_holiday_mode.po

The add-on uses its own tables instead of new columns in cscart_companies:

  • core tables stay untouched, so CS-Cart upgrades, vendor import/export and the Vendor data premoderation profile diff are not affected;
  • the multilingual message needs a separate descriptions table anyway;
  • uninstalling drops two tables instead of running ALTER TABLE on a large core table.

cscart_kv_vendor_holiday_mode (MyISAM, utf8)

Column Type Notes
company_id int unsigned, PK Vendor
enabled char(1) Y/N: the vendor’s switch
start_date int unsigned Timestamp, 00:00:00 of the start day; 0 = immediately
end_date int unsigned Timestamp, 23:59:59 of the last day; 0 = until turned off
is_active char(1) Last known computed state. Kept in sync automatically (see below)
locked char(1) Y = only administrators may change it
updated_at int unsigned Timestamp of the last change
updated_by int unsigned User ID of the last change

cscart_kv_vendor_holiday_mode_descriptions: company_id, lang_code, message (text), PK (company_id, lang_code).

The record is deleted when the vendor is deleted (delete_company hook).

fn_kv_vendor_holiday_mode_get_state() derives the state (active, scheduled, ended, off) from enabled and the dates. fn_kv_vendor_holiday_mode_get_active_holidays() loads the records in effect once per request (static cache). It is called from before_dispatch on every storefront request. When the computed state differs from is_active, it runs an UPDATE on the add-on table. CS-Cart then marks the table as changed and drops block caches whose update_handlers include kv_vendor_holiday_mode. schemas/block_manager/blocks.post.php adds both add-on tables to every cache definition that already depends on products or companies: block types such as products and vendors, and the per-dispatch caches of the main content block (products.view, categories.view, companies.products). Saving a vacation in the vendor or admin panel writes to the same tables, so the storefront updates right away. Dates take effect without cron and without stale cached blocks.

Tygh\Addons\KvVendorHolidayMode\License\Client verifies the license against the Kavio Labs license server.

  • fn_kv_vendor_holiday_mode_is_licensed() is the storefront gate. Every storefront entry point reads vacations through fn_kv_vendor_holiday_mode_get_storefront_holidays(), which returns no vacations when the license does not cover the storefront: get_storefront_holiday() (all banners, notes and button overrides), get_blocked_product_holiday() (call requests), get_cart_holidays() (cart/checkout warnings, checkout redirect, pre_place_order), get_products (hide mode) and pre_add_to_cart. The storefront state sync in before_dispatch is skipped too. It reads the stored state only (one storage read per request) and never contacts the server.
  • Not gated: the vendor panel and admin pages (vacation settings, list, end, lock, delete), the e-mail to the administrator, fn_kv_vendor_holiday_mode_get_active_holidays() and fn_kv_vendor_holiday_mode_is_on_holiday().
  • The license state is part of every block cache key (block_cache_properties.post.php, callable_handlers kv_vhm_license), so cached product and vendor blocks switch at once. Pages stored by the 4.21 full page cache keep their output until they expire (180 s).
  • Client::boot() (init.php) registers a before_dispatch handler for the admin area and the console only. On admin page views and console requests it runs the check when it is due (key changed, or 24 hours after the last check; 1 hour after a failure) and shows the admin notice once per session.
  • Admin license UI (generated): the License card at the top of the add-on page (license/card.tpl through the addons:tabs_content pre hook), the badge in Add-ons > Manage (license/badge.tpl, addons:manage post hook), the status bar (license/status_bar.tpl, first line inside the mainbox capture of views/kv_vendor_holiday_mode/{manage,update}.tpl; nothing for vendors) and css/addons/kv_vendor_holiday_mode/license.less (index:styles post hook). The off-effect sentence is the language variable kv_vendor_holiday_mode.license_off_effect.
  • Request: POST https://cs-cart.kaviolabs.com/index.php?dispatch=kv_licenses.check with JSON {"v":1, "license", "addon", "version", "hosts":[…], "platform":{"version", "edition"}, "nonce"}, sent through Tygh\Http (3 s connect, 5 s total; kept out of the CS-Cart request log). The answer is accepted only when its Ed25519 signature matches a public key embedded in the client and it echoes the nonce.
  • State: storage key kv_license_kv_vendor_holiday_mode (?:storage_data), or var/kv_storage/kv_vendor_holiday_mode/license.json while the full page cache is active, protected by an HMAC with the store’s crypt key.
  • Cron: php admin.php --dispatch=kv_vendor_holiday_mode_license.check.
  • Development stores only (http_host and https_host are development hosts): the constants KV_LICENSE_SERVER_URL, KV_LICENSE_TEST_HOST (act as if the development storefronts ran on that host) and KV_LICENSE_TEST_TIME_OFFSET (seconds). They are ignored on production hosts.

The license check endpoint (kv_vendor_holiday_mode_license.check) accepts only CSRF-protected POST requests of administrators, and console requests. Vendors cannot use it.

Hook Handler Purpose
before_dispatch fn_kv_vendor_holiday_mode_before_dispatch Sync the stored state with the dates (storefront, while licensed)
before_dispatch (admin area and console only) static License\Client::onBeforeDispatch Run the due license check and show license notices
get_products fn_kv_vendor_holiday_mode_get_products Select products.company_id on the storefront; exclude vendors on vacation in hide mode
pre_add_to_cart fn_kv_vendor_holiday_mode_pre_add_to_cart Remove products of vendors on vacation from add-to-cart requests (not from quantity updates)
pre_place_order fn_kv_vendor_holiday_mode_pre_place_order Refuse orders in block mode (storefront only)
delete_company fn_kv_vendor_holiday_mode_delete_company Cleanup

The add-on priority is 2100, so pre_add_to_cart runs after Common products for vendors (1510) has replaced a common product with a vendor offer.

/**
* Before a vacation record is saved. Change $data or set $can_update = false to cancel.
*/
fn_set_hook('kv_vendor_holiday_mode_update_holiday_pre', $company_id, $data, $auth, $current, $can_update);
/**
* After a vacation record is saved. $row is the saved record, $current the record before the update.
*/
fn_set_hook('kv_vendor_holiday_mode_update_holiday_post', $company_id, $row, $current, $auth);
/**
* After storefront display data of a vendor on vacation is prepared (cached per request).
* Change $holiday to alter the banner, e.g. set add_to_cart_disabled for some vendors only.
*/
fn_set_hook('kv_vendor_holiday_mode_get_storefront_holiday_post', $company_id, $lang_code, $holiday);

Example: let a specific vendor keep selling while others are blocked:

func.php
// init.php of your add-on
fn_register_hooks('kv_vendor_holiday_mode_get_storefront_holiday_post');
function fn_my_addon_kv_vendor_holiday_mode_get_storefront_holiday_post($company_id, $lang_code, &$holiday)
{
if ($company_id == 5) {
$holiday['product_mode'] = 'notice';
$holiday['add_to_cart_disabled'] = false;
}
}

The server-side add-to-cart check follows the global setting; extend pre_add_to_cart in your add-on (priority above 2100) if you need per-vendor server rules.

Function Returns
fn_kv_vendor_holiday_mode_is_on_holiday($company_id) bool: vendor on vacation right now
fn_kv_vendor_holiday_mode_get_active_holidays($reset = false) [company_id => row] of vacations in effect
fn_kv_vendor_holiday_mode_get_holiday($company_id, $lang_code = DESCR_SL) Record (or default off record) with message and state
fn_kv_vendor_holiday_mode_get_storefront_holiday($company_id, $lang_code = CART_LANGUAGE) Display data or []: company_name, start_date, end_date, show_return_date, message, product_mode, add_to_cart_disabled
fn_kv_vendor_holiday_mode_update_holiday($company_id, array $data, array $auth, $lang_code = DESCR_SL) bool. $data: enabled (Y/N), start_date, end_date (store date format), message, locked (ignored for vendor users)
fn_kv_vendor_holiday_mode_end_holidays(array $company_ids, $user_id = 0) Number of updated records
fn_kv_vendor_holiday_mode_delete_holidays(array $company_ids) —
fn_kv_vendor_holiday_mode_get_holidays(array $params, $items_per_page = 0, $lang_code = DESCR_SL) [$holidays, $params] for the admin list. Params: state, q, company_id, page, sort_by (company, start_date, end_date, updated_at), sort_order
fn_kv_vendor_holiday_mode_get_cart_holidays(array $cart) Vendors on vacation in the cart with product names (empty without a valid license)
fn_kv_vendor_holiday_mode_get_storefront_holidays() Like get_active_holidays(), but empty without a valid license: what the storefront applies
fn_kv_vendor_holiday_mode_is_licensed() bool: the license covers this storefront (see License check)

To skip the hide filter for one fn_get_products() call, pass kv_vendor_holiday_mode_skip => true in the params.

Template hooks used (storefront, responsive)

Section titled “Template hooks used (storefront, responsive)”
Hook File Output
companies:products (pre) views/companies/products.tpl Banner on the vendor’s product list
companies:view (pre) views/companies/view.tpl Banner on the vendor information page
products:main_info_title (post) product templates (default, big picture) Banner under the product title
buttons:add_to_cart (override) buttons/add_to_cart.tpl “Vendor is on vacation until…” instead of the button. Outputs nothing otherwise, so the default button is kept
products:product_additional_info (post) cart items Per-item note (only on checkout.* pages)
index:styles (post) common/styles.tpl styles.less

In templates, use the modifier: {$kv_holiday = $product.company_id|fn_kv_vendor_holiday_mode_get_storefront_holiday} and {include file="addons/kv_vendor_holiday_mode/components/banner.tpl" kv_holiday=$kv_holiday kv_context="product"}.

All CSS is scoped under .kv-vendor-holiday-mode* classes (__banner, __title, __message, __note, __unavailable, __cart-note). Override them in your theme’s custom CSS.

companies:list_extra_links (post) and companies:tools_list (post) add a Vacation mode link to the vendor list and the vendor profile.

kv_vendor_holiday_mode.manage (admin list; vendors are redirected to their own page), .update (GET/POST), .end, .m_end, .delete, .m_delete (POST). Vendors and administrators switched to a vendor are always limited to runtime.company_id. The bulk and delete modes are refused for them. Vendor users cannot change locked, and cannot save or end a locked record.

  • Texts: change language variables kv_vendor_holiday_mode.* in Administration → Languages → Translations.
  • Position of the product banner: copy components/banner.tpl usage into another hook from your own add-on and turn off Show a banner on the vendor’s product pages.
  • Email: Design → Email templates → Vendor vacation mode: vendor went on vacation.

Tygh/Addons/KvVendorHolidayMode/License/Ed25519.php (checks the signature of the license server’s answers on servers whose PHP has no sodium extension) is derived from paragonie/sodium_compat, ISC License, Copyright (c) 2016-2023 Paragon Initiative Enterprises and Copyright (c) 2013-2019 Frank Denis. The full license notice is in the file header.