Skip to content

Developer notes

Overview · Installation · Configuration · Usage · FAQ

app/addons/kv_multichannel_feeds/
addon.xml, init.php, func.php
controllers/backend/kv_multichannel_feeds.php feeds, preview, report (+CSV), categories, taxonomy search, generate step, console cron
controllers/backend/kv_multichannel_feeds_license.php "Check now" / cron license check
controllers/frontend/kv_multichannel_feeds.php feed URL (get) and web cron (cron)
resources/taxonomy-with-ids.en-US.txt Google product taxonomy (bundled)
schemas/menu/menu.post.php, menu_vendor.post.php
schemas/permissions/admin.post.php, vendor_multivendor.post.php
Tygh/Addons/KvMultichannelFeeds/
Channels.php channel templates: columns, requirement, max length, value type, enumerations, formats
Fields.php canonical fields, mapping sources, enumerations, GTIN check
Feeds.php repository, defaults, settings merge, tokens, file paths, URLs, schedule
ProductSource.php keyset-paginated batches with SQL filters; categories, features, variation groups, sales
ItemBuilder.php product -> canonical item (mapping, labels, availability, prices, shipping, UTM, rules)
Formatter.php canonical item -> channel row + validation issues
Writer.php CSV / TSV / XML (RSS 2.0 + g: or plain) / JSON streaming writer, resumable
Generator.php resumable job steps, publish (gzip, atomic rename), preview, cron
Validator.php text length and truncation helpers
Taxonomy.php taxonomy lookup and search
License/Client.php license check client (generated from the shared Kavio Labs template)
design/backend/templates/addons/kv_multichannel_feeds/views/kv_multichannel_feeds/{manage,update,preview,report,categories}.tpl
design/backend/templates/addons/kv_multichannel_feeds/views/kv_multichannel_feeds/components/{nav,taxonomy_picker,generate_button}.tpl
design/backend/templates/addons/kv_multichannel_feeds/settings/{overview,cron}.tpl
design/backend/templates/addons/kv_multichannel_feeds/hooks/index/{scripts,styles}.post.tpl
design/backend/templates/addons/kv_multichannel_feeds/license/{info,card,status_bar,badge}.tpl, hooks/addons/{tabs_content.pre,manage.post}.tpl
design/backend/css/addons/kv_multichannel_feeds/{styles,license}.less
js/addons/kv_multichannel_feeds/admin.js
Table Content
?:kv_mcf_feeds Feeds: channel, company, format, gzip, country, language, currency, token, schedule, JSON settings, statistics of the last run
?:kv_mcf_jobs Running generation per feed: run token, last product ID, totals, counters (resume state)
?:kv_mcf_issues Validation issues per run token (feed, product, column, code, severity); only the last finished run is kept
?:kv_mcf_category_map Store category → Google category ID
  1. Generator::step($feed, $budget) takes a non-blocking lock per feed (feed_<id>.lock). A concurrent call gets busy.
  2. A new job writes the file header to <file>.tmp, empties <file>.ids and stores the total count. A running job continues from its last product ID.
  3. A storefront-like context is set: guest auth, runtime.company_id = storefront (Ultimate) or 0 (Multi-Vendor).
  4. ProductSource::nextIds() selects product_id > last ORDER BY product_id LIMIT batch with SQL filters; load() calls fn_get_products() and fn_gather_additional_products_data() and loads features, variation groups and sales of the batch with one query each.
  5. ItemBuilder::build() makes the canonical item, Formatter::format() the channel row with its issues; duplicate IDs are found through <file>.ids (also across steps). Rows are appended, issues inserted per batch, the job row updated per batch.
  6. When the time budget is used up the step returns partial. The last step writes the footer, gzips when asked, renames the file over the published one and drops the issues of older runs.

Memory stays flat: 50,000 products peaked at 104 MB in a console run (54 MB of it is CS-Cart itself).

The templates follow the published specifications (checked 2026-10-23):

Channel Specification
Meta Catalog fields reference
TikTok Catalog product parameters
Pinterest Data source specification
Microsoft Products resource, Merchant Center feed files (tab-delimited text or Google XML)
Snapchat Dynamic Product Ads guide
Criteo Product feed parameters
idealo CSV importer

Value conventions: prices 12.00 USD (Meta, TikTok, Pinterest, Microsoft, Snapchat) or 12.00 (Criteo, idealo, custom); availability values per channel (backorder becomes available for order on TikTok and Snapchat and in stock elsewhere); CSV is RFC 4180 with every cell quoted and CRLF line ends; list values are comma-separated (idealo: ;); XML for the shopping channels is RSS 2.0 with xmlns:g="http://base.google.com/ns/1.0".

What How
Feed index.php?dispatch=kv_multichannel_feeds.get&feed_id=N&key=TOKEN (If-None-Match / If-Modified-Since → 304; wrong token or disabled feed → 404; not generated yet → 503)
Web cron index.php?dispatch=kv_multichannel_feeds.cron&cron_key=KEY[&feed_id=N][&force=Y]
Console cron php admin.php --dispatch=kv_multichannel_feeds.cron [--feed_id=N] [--force=Y] [--budget=SECONDS]
Generate step (admin, AJAX) POST kv_multichannel_feeds.generate with kv_mcf_step=1 → {kv_mcf_step: {status, done, processed, total, ...}}
Hook Purpose
delete_product_post Remove report rows of the deleted product
delete_category_after Remove the category mapping
Template index:scripts / index:styles (backend) Admin JS and LESS
before_dispatch (admin area and console only; License\Client::onBeforeDispatch) Runs the due license check and shows license notices
addons:tabs_content (pre), addons:manage (post) License card and badge
  • Programmatic generation: (new \Tygh\Addons\KvMultichannelFeeds\Generator())->generate(\Tygh\Addons\KvMultichannelFeeds\Feeds::get($feed_id));
  • A new column: add it to the channel in Channels::getAll() with a canonical field from Fields::getAll(); the writers and the Fields tab pick it up.

Tygh\Addons\KvMultichannelFeeds\License\Client verifies the license against the Kavio Labs license server. The client, the “Check now”/cron controller controllers/backend/kv_multichannel_feeds_license.php and the license templates are generated from the shared Kavio Labs template; don’t edit them by hand.

  • fn_kv_multichannel_feeds_is_licensed($feed = []) (func.php) is the gate. It reads the stored state only and never contacts the server. In the admin area and cron on Ultimate it checks the host of the feed’s storefront.
  • Gated: Generator::step() (Generate now, AJAX steps, console cron, web cron) returns the status paused and touches nothing. Not gated: the feed URL (serves the last file), the editor, preview, report and category mapping.
  • The status bar is the first line inside the mainbox capture of every own admin page. The “what is switched off” sentence is kv_multichannel_feeds.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"}; the answer is accepted only with a valid Ed25519 signature and the echoed nonce.
  • State: storage key kv_license_kv_multichannel_feeds (?:storage_data), or var/kv_storage/kv_multichannel_feeds/license.json while the full page cache is active, protected by an HMAC.
  • Cron: php admin.php --dispatch=kv_multichannel_feeds_license.check.

Tygh/Addons/KvMultichannelFeeds/License/Ed25519.php is derived from paragonie/sodium_compat, ISC License, Copyright (c) 2016-2023 Paragon Initiative Enterprises and Copyright (c) 2013-2019 Frank Denis. resources/taxonomy-with-ids.en-US.txt is the Google product taxonomy as published by Google for merchant feeds.