Title: Surf7 Simple Catalog
Author: Ben Leow
Published: <strong>August 25, 2026</strong>
Last modified: August 25, 2026

---

Search plugins

![](https://ps.w.org/surf7-simple-catalog/assets/banner-772x250.png?rev=3664599)

![](https://ps.w.org/surf7-simple-catalog/assets/icon-256x256.png?rev=3664599)

# Surf7 Simple Catalog

 By [Ben Leow](https://profiles.wordpress.org/surf7net/)

[Download](https://downloads.wordpress.org/plugin/surf7-simple-catalog.1.64.1.zip)

 * [Details](https://wordpress.org/plugins/surf7-simple-catalog/#description)
 * [Reviews](https://wordpress.org/plugins/surf7-simple-catalog/#reviews)
 *  [Installation](https://wordpress.org/plugins/surf7-simple-catalog/#installation)
 * [Development](https://wordpress.org/plugins/surf7-simple-catalog/#developers)

 [Support](https://wordpress.org/support/plugin/surf7-simple-catalog/)

## Description

**Surf7 Simple Catalog** is a clean, focused catalog plugin for WordPress built 
by Surf7.net.

If you sell by enquiry, quotation or conversation rather than checkout — auto parts,
machinery, furniture, jewellery, wholesale, custom manufacturing, made-to-order,
property, vehicles, boats — this plugin is built for you. There is no cart, no checkout,
no payment and no stock, by design.

#### Core features

 * **Catalog post type** `scat_product` — REST-enabled, revisions, featured image,
   gallery, custom REST endpoints, `WP_Query` friendly.
 * **Three taxonomies**: hierarchical **Categories** (thumbnail image, long description,
   display order, hide-from-filters), flat **Labels** (colour chips, display order),
   and **Brands** (logo, website URL, exactly one brand per product, with an A–Z
   brand index at `/brand/`).
 * **Product meta** from a single typed schema: gallery, price, a price range derived
   from the product’s own options, price mode (show / on request / hide), availability(
   Available, Reserved, Sold, Coming soon) with configurable text and colour per
   status, featured star with an AJAX toggle, a specifications table with paste-
   from-spreadsheet, files and downloads, video URL, stock note, custom badge text,
   and per-product enquiry overrides.
 * **Five enquiry channels** — WhatsApp, Email, Phone, Telegram and a custom link.
   Email either opens the visitor’s mail client or uses the built-in enquiry form.**
   One message template drives every channel**, with placeholders including `{product_name}`,`{
   product_code}`, `{product_url}`, `{price}`, `{selected_options}`, `{visitor_message}`
   and `{site_name}`. Optional enquiry log in its own table, with CSV export, BCC
   copies and an auto-reply to the visitor carrying a configurable Reply-To address.
 * **Spam guard**: hidden honeypot field, three-second time trap, hourly per-IP 
   rate limit, and optional Google reCAPTCHA v3 or Cloudflare Turnstile.
 * **Options engine**: up to 10 global option sets, 100 values each, in five display
   types — dropdown, swatch, checkbox, package and free text. Per-value price adjustment
   or absolute price, and per-product groups that update the displayed price without
   a reload. Options are informational and enquiry-affecting only; they never create
   variations or touch stock.
 * **Front-end templates**, every one overridable from your theme’s `simple-catalog/`
   folder. Archive as grid, list or masonry; category, label and brand archives;
   product page with gallery, lightbox, breadcrumbs, tabs and related products; 
   filters for category, label, availability, price range and sort order; classic
   pagination or an AJAX **Load more** button. Responsive and RTL-aware, and verified
   to render with full site chrome on block themes as well as classic ones.
 * **Six blocks and seven shortcodes**, plus a **shortcode generator** under Catalog
   Settings that builds a shortcode from real controls and copies it to the clipboard.
 * **Admin UX**: drag-to-reorder gallery via `wp.media`, quick edit, bulk actions(
   draft, publish, sold, available, featured, duplicate), CSV import and export,
   and a JSON full-site export/import for staging  production deployments.
 * **WooCommerce migration**: bring an existing WooCommerce catalogue across in 
   place. Non-destructive, re-runnable, preserves category hierarchy, brands, term
   images and every affiliate link.
 * **External shop buttons**: per-product links to Shopee, Lazada, Amazon and anywhere
   else, each with its own colours and inline SVG icon.
 * **REST API** at `wp-json/simple-catalog/v1/` — `products`, `enquiry`, `taxonomy-
   index`, `nonce`.
 * **WP-CLI**: `wp simple-catalog activate|deactivate|flush|version|purge-enquiries
   |export|import|export-csv-products|import-csv-products|caps|regenerate-thumbs`.
 * **Security**: `map_meta_cap` capabilities granted to administrators and editors,
   a nonce **and** a capability check on every write path, REST permission callbacks,
   a sanitise callback on every registered meta key, escaping at the point of output,
   and prepared statements on every custom-table query.
 * **SEO friendly**: configurable permalink bases, Product JSON-LD, BreadcrumbList,
   ItemList on archives, and Open Graph / Twitter cards that step aside when Yoast,
   Rank Math or SEOPress is active.
 * **Performance**: CSS and JS enqueued only on catalogue pages, five configurable
   image sizes, lazy loading with an optional blur-up placeholder, CSS custom properties
   instead of repeated inline styles, and an enquiry nonce that can be fetched at
   interaction time so full-page caching cannot serve a stale one.
 * **Internationalisation**: full `surf7-simple-catalog` text domain with a bundled`.
   pot`, an RTL stylesheet, and default button labels that re-translate when the
   site’s locale changes.

#### Privacy

The optional enquiry log stores the name, email address, phone number and message
a visitor submits. **It is off by default.** IP addresses are never stored in the
clear — only a salted SHA-256 hash, used for rate limiting. Retention is configurable(
never, 90, 180 or 365 days) and enforced by a daily job.

The plugin registers with WordPress’s own privacy tools, so **Tools  Export Personal
Data** returns a visitor’s enquiries and **Tools  Erase Personal Data** anonymises
them — clearing the name, email, phone, message, originating page, browser and IP
hash while leaving the date and product, which identify nobody. Both work whether
or not logging is currently switched on, because turning it off never deletes what
was already stored. Suggested policy wording is offered under **Settings  Privacy**.

The plugin makes no outbound requests except the ones described under External services
below, each of which you choose to enable. Nothing phones home, and no data is sent
anywhere by default.

### External services

Out of the box this plugin contacts no external service. The following are used 
only after you deliberately switch them on, and each is listed with what is sent
and when.

**Google reCAPTCHA v3** — used only if you select it under Catalog  Settings  Spam
and enter a site key and secret key.

 * On any page showing the enquiry form, the visitor’s browser loads `https://www.
   google.com/recaptcha/api.js`. Google receives the visitor’s IP address, browser
   details and the page they are on, as with any third-party script.
 * When an enquiry is submitted, your server sends your secret key, the reCAPTCHA
   token produced by the visitor’s browser, and **the visitor’s IP address** to `
   https://www.google.com/recaptcha/api/siteverify` to confirm the submission is
   not automated.
 * Terms of service: https://policies.google.com/terms — Privacy policy: https://
   policies.google.com/privacy

**Cloudflare Turnstile** — used only if you select it under Catalog  Settings  Spam
and enter a site key and secret key.

 * On any page showing the enquiry form, the visitor’s browser loads `https://challenges.
   cloudflare.com/turnstile/v0/api.js`. Cloudflare receives the visitor’s IP address,
   browser details and the page they are on.
 * When an enquiry is submitted, your server sends your secret key, the Turnstile
   token, and **the visitor’s IP address** to `https://challenges.cloudflare.com/
   turnstile/v0/siteverify`.
 * Terms of service: https://www.cloudflare.com/website-terms/ — Privacy policy:
   https://www.cloudflare.com/privacypolicy/

**Image sideloading during import** — used only if you tick “Download remote images”
on the Import screen. Your server then fetches the image URLs contained in the file
_you_ uploaded, so the destinations are whichever hosts those URLs point to. No 
third party is involved beyond the hosts you supplied.

**JSON import from a URL** — used only if you call the `simple-catalog/v1/import-
export/import/json` REST endpoint with a `url` parameter, which requires an administrator
or editor login. Your server downloads that address and imports it. No such request
is made from the admin screens.

#### Outbound links (no data is sent)

Two features render ordinary links to other websites. The plugin makes **no request**
to any of them: nothing is contacted unless a visitor clicks, and when they do it
is their own browser that navigates, exactly as with any link you place in a post.
No data is transmitted by this plugin, and nothing is embedded, tracked or loaded
from these hosts.

**Enquiry channels and share buttons.** If you switch on WhatsApp or Telegram enquiries,
the button is a link to `https://api.whatsapp.com/send` or `https://t.me/`. If you
switch on share buttons under Catalog  Settings  Display, each is a link to that
network’s own share page — Facebook, X, WhatsApp, Telegram, Pinterest, LinkedIn,
or a `mailto:` link. The product title and its URL travel in the link’s query string,
which is what the network needs to prefill the share; both are already public.

Terms and privacy for the networks involved:

 * WhatsApp — https://www.whatsapp.com/legal/terms-of-service and https://www.whatsapp.
   com/legal/privacy-policy
 * Telegram — https://telegram.org/tos and https://telegram.org/privacy
 * Facebook — https://www.facebook.com/terms.php and https://www.facebook.com/privacy/
   policy
 * X — https://x.com/en/tos and https://x.com/en/privacy
 * Pinterest — https://policy.pinterest.com/terms-of-service and https://policy.
   pinterest.com/privacy-policy
 * LinkedIn — https://www.linkedin.com/legal/user-agreement and https://www.linkedin.
   com/legal/privacy-policy

**External shop buttons.** Per-product links to marketplaces such as Shopee, Lazada
or Amazon. Every destination is a URL you enter yourself on the product; the plugin
ships no addresses of its own and contacts nothing.

If you select a CAPTCHA provider, tell your visitors: sending their IP address to
Google or Cloudflare is a disclosure you are responsible for under GDPR and similar
laws. The suggested privacy-policy text under Settings  Privacy does not cover it,
because whether it applies depends on a setting only you can see.

## Screenshots

[⌊A product page: gallery with thumbnails, breadcrumbs, availability badge with 
your own wording, buttons through to the marketplaces that stock it, and a share
row.⌉⌊A product page: gallery with thumbnails, breadcrumbs, availability badge with
your own wording, buttons through to the marketplaces that stock it, and a share
row.⌉[

A product page: gallery with thumbnails, breadcrumbs, availability badge with your
own wording, buttons through to the marketplaces that stock it, and a share row.

[⌊The catalogue, with filters for category, label, availability and price range,
a category sidebar, and availability badges on every card.⌉⌊The catalogue, with 
filters for category, label, availability and price range, a category sidebar, and
availability badges on every card.⌉[

The catalogue, with filters for category, label, availability and price range, a
category sidebar, and availability badges on every card.

[⌊The product editor — gallery with drag-to-reorder, product options, a specifications
table you can paste straight from a spreadsheet, and price modes including "price
on request".⌉⌊The product editor — gallery with drag-to-reorder, product options,
a specifications table you can paste straight from a spreadsheet, and price modes
including "price on request".⌉[

The product editor — gallery with drag-to-reorder, product options, a specifications
table you can paste straight from a spreadsheet, and price modes including “price
on request”.

[⌊Catalog → Settings: one message template drives WhatsApp, Telegram, phone, email
and the enquiry form, so the wording is written once.⌉⌊Catalog → Settings: one message
template drives WhatsApp, Telegram, phone, email and the enquiry form, so the wording
is written once.⌉[

Catalog  Settings: one message template drives WhatsApp, Telegram, phone, email 
and the enquiry form, so the wording is written once.

[⌊The shortcode generator. Set the options, copy the shortcode. Anything left at
its default is left out, so what you paste stays readable.⌉⌊The shortcode generator.
Set the options, copy the shortcode. Anything left at its default is left out, so
what you paste stays readable.⌉[

The shortcode generator. Set the options, copy the shortcode. Anything left at its
default is left out, so what you paste stays readable.

## Blocks

This plugin provides 3 blocks.

 *   Enquiry Buttons Enquiry channels for a product: WhatsApp, email, phone, Telegram,
   custom.
 *   Catalog Grid Display a responsive grid of catalog products.
 *   Single Product Show a single catalog product by ID.

## Installation

 1. Upload the `surf7-simple-catalog` folder to `/wp-content/plugins/`.
 2. Activate the plugin through the **Plugins** screen in WordPress.
 3. Walk through the four-step onboarding wizard (slugs, currency, channels, display,
    sample products) or go straight to **Catalog  Settings**.
 4. Start creating products via **Catalog  Add New**.

## FAQ

### Does Simple Catalog add a cart or checkout?

No. Simple Catalog is catalog-only by design. Visitors browse and then contact you.
If you need a cart and checkout, use WooCommerce.

### Can I import products from a CSV?

Yes. Go to **Catalog  Import / Export**. Download the template CSV, fill it in and
upload it — there is a dry-run mode that validates everything and writes nothing.
Ticking the sideload option downloads any remote images into your media library.

### Can I move a WooCommerce catalogue across?

Yes, from the same Import / Export screen. WooCommerce products are read and never
modified, and the migration is re-runnable: a second pass updates what it created
rather than duplicating it.

### Does it work with my theme?

Almost certainly. The templates are minimal semantic HTML driven by CSS custom properties
you can override, and every one can be replaced by copying it from `wp-content/plugins/
surf7-simple-catalog/templates/` to `wp-content/themes/{your-theme}/simple-catalog/`.
Block themes and classic themes are both supported.

### Does it support Multisite?

Partly. The uninstaller and the WooCommerce importer loop every site on the network,
so uninstalling cleans up properly. Multisite as a whole has not been formally tested—
treat it as unsupported for now and test on staging first.

### Where is the full changelog?

CHANGELOG.md in the plugin folder carries the complete history, including what was
verified on a live site for each release.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Surf7 Simple Catalog” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ Ben Leow ](https://profiles.wordpress.org/surf7net/)

[Translate “Surf7 Simple Catalog” into your language.](https://translate.wordpress.org/projects/wp-plugins/surf7-simple-catalog)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/surf7-simple-catalog/),
check out the [SVN repository](https://plugins.svn.wordpress.org/surf7-simple-catalog/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/surf7-simple-catalog/)
by [RSS](https://plugins.trac.wordpress.org/log/surf7-simple-catalog/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.64.1

Follow-up to the WordPress.org review. One fix.

 * The “Load more” button and the enquiry form no longer fall back to a guessed 
   address when the plugin’s own script data is missing. Both endpoints were already
   resolved properly in PHP and passed to the script, but each carried a hardcoded
   fallback path that would have been wrong on a site installed in a subdirectory,
   on multisite, with a relocated admin directory, or using plain permalinks. The
   fallbacks are gone; the resolved values are the only source.

#### 1.64.0

Addresses the WordPress.org plugin review. Mostly hardening; one setting has been
removed.

**Removed**

 * The **Custom CSS** box under Catalog  Settings  Developer is gone. Plugins in
   the directory may not accept arbitrary CSS, JavaScript or PHP — WordPress has
   its own error-checking CSS editor in the Customizer and the Site Editor, and 
   that is where site-wide styling belongs. Any CSS you had saved there is discarded
   on update; move it to **Appearance  Customise  Additional CSS** before updating
   if you still need it.

**Security**

 * The single-product REST endpoint no longer serves unpublished products. `wp-json/
   simple-catalog/v1/products/{id}` returned drafts, pending, private and password-
   protected products in full to anyone who guessed an ID, while the collection 
   endpoint alongside it correctly showed only published ones. Reading a product
   that is not published now requires permission to edit it.
 * The JSON import-from-URL endpoint now uses WordPress’s safe HTTP client, so it
   can no longer be pointed at addresses on the site’s own network.
 * Options selected on the enquiry form are sanitised before they reach the enquiry
   email, the enquiry log and the CSV export.
 * The duplicate-handling and import-mode values on the Import screen are checked
   against the list the form actually offers instead of being stored as sent.
 * Nonces are sanitised before verification, and the browser and IP values read 
   from the request are sanitised on the way in.

**Import / Export**

 * **CSV import can now update your products instead of duplicating them.** The 
   exported file has always started with an ID column, but the importer never read
   it — it matched only on `sku/code`. So exporting your catalogue, changing a price
   and importing it back created a second copy of every product that had no product
   code. Rows are now matched on ID first, then on `sku/code`, and only then created.
   An ID that does not belong to a product on this site is ignored rather than trusted,
   so a file from another site cannot overwrite the wrong product.
 * **“Duplicate handling” on the Import screen now does something.** Skip, Update
   and Always create new previously all behaved the same way, because the setting
   was never passed to the importer. The default is now Update, and the option that
   claimed to match “by slug / title” has been reworded to describe what actually
   happens.
 * **Import results now appear on the Import tab.** Uploading a file returned you
   to the Import / Export screen with the Export tab showing, so the preview — and
   the completion report, and the WooCommerce migration report — were all rendered
   onto a panel you could not see until you clicked Import.
 * `wp simple-catalog import-csv-products` accepts `--duplicate=update|skip|create`.

**Under the hood**

 * Uploaded import files now go through WordPress’s own upload handler rather than
   being moved by hand, so host filesystem policies and security filters apply to
   them.
 * Exports written by WP-CLI, and the guard files in the plugin’s uploads folder,
   go through the WordPress filesystem API.
 * The plugin’s theme colours and the two admin fallbacks for the brand metabox 
   are enqueued rather than printed as literal `<style>` and `<script>` tags, so
   they can be dequeued, reordered and minified like any other asset.
 * Video thumbnails fall back to the provider’s lower-resolution image through the
   plugin’s own script instead of an inline `onerror` attribute, and every URL in
   the gallery is escaped for the context it is printed into.
 * The enquiry form records the page it was submitted from using WordPress’s resolved
   address rather than request headers.
 * Coding-standards config now matches the plugin’s text domain, so translator-facing
   checks actually run.

No action is required after updating unless you were using the Custom CSS box.

#### 1.63.1

A housekeeping release. No functional changes to the catalogue itself.

 * The plugin’s text domain and folder now match its WordPress.org slug, so translations
   are loaded rather than silently ignored.
 * First-run setup wizard: the welcome text no longer prints a raw escape code where
   an apostrophe belongs, and the four step markers are no longer numbered twice.

#### 1.63.0

A security and correctness release. Updating is recommended.

**Catalogue display**

 * Sold products now move to the back of the catalogue. Products are always grouped
   Available, then Coming Soon, then Reserved, then Sold, no matter which sort you
   or your visitor chooses — the chosen sort still applies within each group. This
   covers the main catalogue, category, label and brand archives, search results,
   and grids placed with the Catalog Grid block or the `[catalog_grid]` shortcode.
   Sold items stay visible and linkable; if you would rather hide them completely,
   the existing “Hide sold products” setting is unchanged.
 * The Catalog Grid block’s Sort setting, and the `orderby` attribute of the `[catalog_grid]`
   shortcode, now actually work. Both were being ignored, so every grid came out
   newest-first whatever you chose.
 * Product and archive pages now carry a proper meta description if you have no 
   SEO plugin installed. It is built from the product’s excerpt or description with
   all HTML, shortcodes and entities stripped out, so a catalogue imported from 
   a marketplace no longer risks showing raw list markup in search results. If you
   do run Yoast, Rank Math or SEOPress, nothing changes — the plugin leaves the 
   description to them.
 * Brand archives now appear in structured data. They were producing none at all,
   unlike category and label archives.
 * Brand archives now behave like the rest of the catalogue. Pages such as `/brand/
   your-brand/` were skipping the plugin’s own settings entirely — products per 
   page, the sort order, and the filters all had no effect there.

**Security**

 * Fixed an SQL injection on the Enquiries screen. The sort order taken from the
   URL was written into the query without being restricted to real column names,
   and the screen is available to editors as well as administrators. Only sites 
   with enquiry logging switched on were affected.
 * Hardened the import upload. The file was saved using the extension supplied by
   whoever uploaded it, into a folder with no access protection. The extension is
   now decided by the plugin, and the folder is protected on creation.
 * Fixed the product structured data (JSON-LD) so a product title or specification
   containing HTML can no longer break out of the script block.
 * The hourly enquiry rate limit can no longer be bypassed by forging an `X-Forwarded-
   For` header, which previously handed the sender a fresh allowance on every request.
 * The anti-spam timestamp is now signed, and a submission that omits it is rejected
   rather than skipping the check entirely.

**Privacy**

 * Registered with WordPress’s own privacy tools. **Tools  Export Personal Data**
   now returns a visitor’s enquiries, and **Tools  Erase Personal Data** anonymises
   them — clearing name, email, phone, message, originating page, browser and IP
   hash, and keeping only the date and the product. Both work even if logging has
   since been switched off, because turning it off never deleted what was already
   stored.
 * Suggested privacy-policy wording is offered under **Settings  Privacy**, and 
   it states your configured retention period.

**Import / Export**

 * The CSV import now shows you what happened. Counts, any file-level problems, 
   and a table of the rows that need attention with the reason for each. Previously
   the import ran and returned you to a blank upload form, so per-row reasons were
   recorded and then discarded.
 * A dry run now offers an **Import for real** button, so you can check the file
   and then commit it without uploading twice.
 * Fixed dry-run counts. A preview of new products reported them as updates rather
   than as creations.

**Fixes**

 * Ticked availability filters now show as ticked. The filter always applied correctly;
   only the chips failed to reflect it.
 * The category and label filters now read the whole term ID rather than its first
   digit, so any ID of 10 or above selects the right term in the dropdown.
 * The Delete confirmation on Option Sets now appears. It was silently broken, so
   an option set was deleted on a single click with no prompt.
 * Removed a JavaScript error thrown on every product edit screen.
 * When an enquiry email cannot be sent, the enquiry is saved first and flagged 
   with the mail server’s actual reason, so nothing is lost and the cause is visible.

**Under the hood**

 * Confirmed compatible with PHP 8.1 through 8.4.
 * Removed unused code, including a helper class that nothing referenced.
 * Added a development toolchain — coding standards, static analysis and tests —
   with the results recorded in `docs/hardening-audit.md`.
 * No database changes. Nothing to do after updating.

#### 1.62.3

 * Fixed: the availability filter ignored the badge wording, so renaming a status
   under Settings  Display left the badge and the filter calling the same status
   different things. Both now read the same setting.
 * Known issue: a ticked availability filter applies correctly but does not render
   as ticked.

#### 1.62.2

 * Fixed: a category image or brand logo could not be removed once set. Both term
   forms now carry a Remove control, shown only when there is something to remove.

#### 1.62.0

 * Added: a shortcode generator tab under Catalog  Settings. One card per shortcode,
   with real controls, a self-updating shortcode string and a Copy button. Attributes
   left at their default are omitted.

#### 1.61.0

 * Fixed: catalog pages rendered with no site header or footer on block themes. 
   Both are now drawn through the `core/template-part` block, in the same position
   the theme’s own templates use.

Earlier releases are documented in `CHANGELOG.md`.

## Meta

 *  Version **1.64.1**
 *  Last updated **2 days ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.4 or higher **
 *  Tested up to **7.1**
 *  PHP version ** 8.1 or higher **
 * Tags
 * [catalog](https://wordpress.org/plugins/tags/catalog/)[enquiry](https://wordpress.org/plugins/tags/enquiry/)
   [product catalog](https://wordpress.org/plugins/tags/product-catalog/)[showcase](https://wordpress.org/plugins/tags/showcase/)
   [whatsapp](https://wordpress.org/plugins/tags/whatsapp/)
 *  [Advanced View](https://wordpress.org/plugins/surf7-simple-catalog/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/surf7-simple-catalog/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/surf7-simple-catalog/reviews/)

## Contributors

 *   [ Ben Leow ](https://profiles.wordpress.org/surf7net/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/surf7-simple-catalog/)