Livo Story

Description

Livo Story lets you attach short vertical videos (“stories”) to your WooCommerce products and display them as a swipeable slider anywhere on your site via shortcode. Visitors can watch a product story and add the product to their cart directly from the story card — including variable products, without leaving the story.

Features

  • Vertical, swipeable video stories for WooCommerce products, desktop and mobile.
  • Add-to-cart card inside the story, with native WooCommerce variation support.
  • Extensible via the livostory_extra_cart_fields filter, so a theme or another plugin can add store-specific fields (e.g. a required date) to the add-to-cart card without modifying Livo Story itself.
  • Admin dashboard to manage stories per product, reorder them, and track basic view stats.

Screenshots

Installation

  1. Upload the plugin files to /wp-content/plugins/livo-story, or install it directly through the WordPress plugins screen.
  2. Activate the plugin through the “Plugins” screen in WordPress.
  3. WooCommerce must be installed and active.
  4. Go to the Livo Story admin menu to add a story to a product.
  5. Add the [livostory-stories] shortcode to any page or post to display the stories slider.

FAQ

Does this work with variable products?

Yes. When a story’s product has variations, the add-to-cart card expands to let the shopper pick the attributes before adding it to the cart.

Can I collect extra information before add-to-cart (e.g. a required date)?

Yes, via the livostory_extra_cart_fields filter. It receives the current field list, the WC_Product, and the story, and expects an array of field definitions (name, type, label, required, and options for a select field). Livo Story ships with no fields registered by default.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Livo Story” is open source software. The following people have contributed to this plugin.

Contributors

Translate “Livo Story” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

1.8.4

  • Added the plugin’s icon, banner and screenshots for the WordPress.org listing page.

1.8.3

  • Hidden (inactive) stories could still accumulate view records and cart-event attribution through the two public tracking endpoints, even though they are invisible everywhere on the storefront; both now check the story’s active status the same way the storefront itself does.
  • The per-visitor throttle on both tracking endpoints keyed off an IP address read from client-controlled headers (X-Forwarded-For, Client-IP), which a script can set to a different value on every request to defeat it entirely. The throttle now keys off REMOTE_ADDR, the address the webserver itself sees, which a client cannot spoof; the more lenient, header-preferring lookup is kept only for the IP stored in analytics, which is a display value, not a security control.
  • Fixed the root cause behind the story modal not opening at all on some pages, reported as “no data for container … livostoryStoriesData itself is missing” in the console: the per-instance data was attached via wp_add_inline_script() from inside the shortcode itself, which only works if the target script has not printed yet — not guaranteed when the shortcode renders from a widget, footer area, or page builder that runs after the main content. That data is now collected while the shortcode renders and printed on wp_footer, ahead of WordPress’s own script-printing, regardless of where on the page the shortcode ran from.
  • Fixed a PHP warning (“Undefined property: stdClass::$thumbnail_url”) logged on every story render: the stories table has never had a thumbnail_url column, and nothing in the plugin ever writes one, so the five places reading it now treat it as always absent instead of triggering a warning.
  • Removed the public “get stories” AJAX endpoint. Nothing in the plugin called it — storefront stories are rendered server-side by the shortcode — so it was only an unauthenticated database-query surface.
  • Removed the unused admin “get story” AJAX endpoint for the same reason.
  • Every remaining AJAX handler now checks its nonce with check_ajax_referer() and, where the action is privileged, current_user_can(), inline and before reading any input, instead of through a shared helper.
  • The story add-to-cart attribution now verifies its nonce before reading any other request field.
  • Fixed a database error logged on every activation and version bump: the story tables were created with a FOREIGN KEY clause dbDelta() cannot parse, which made it emit invalid SQL trying to “fix” the table. The constraint was removed (it was never actually created by dbDelta() in the first place) and its cascade-delete behavior is now done directly in code when a story is deleted.
  • Fixed the admin “copy shortcode” button throwing “Cannot read properties of undefined (reading ‘writeText’)” on any install not served over HTTPS. Two other copies of that function, both missing the non-HTTPS fallback the working one already had, were loading after it and silently overwriting it; both were removed and the two admin bundles now have an explicit load-order dependency instead of relying on registration order.
  • Fixed the front-end story click handler firing twice on a single click, the second time with the wrong id, throwing “Cannot read properties of undefined” and restarting the video/double-counting the view. The duplicate handler was removed rather than corrected, since only one of the two ever had the right data to begin with.
  • The story modal now logs a diagnostic warning and does nothing instead of throwing if its data isn’t available yet at click time (a stale cache, a slow-loading footer script), rather than breaking every other click handler on the page.

1.8.2

  • The nonce added in 1.8.1 to gate story-attributed cart events was necessary but not sufficient: WordPress issues the same nonce value to every logged-out visitor, and it is printed in plain sight on every page, so a script could still fetch it and replay it. A per-visitor, per-story throttle now caps that at one attributed event per short window, matching the same protection already in place for view tracking.

1.8.1

  • The public “get stories” AJAX endpoint no longer accepts an unbounded limit: zero, negative, absent or absurdly large values are all clamped to a fixed range instead of loading every active story or scanning every product in a category.
  • Add-to-cart events attributed to a story now require a nonce generated for that page load. The classic WooCommerce add-to-cart form this rides on carries no nonce of its own, so without this a script could replay a real (story_id, product_id) pair to inflate a story’s “added to cart” count without ever visiting the story.

1.8.0

  • New ids shortcode attribute: [livostory-stories ids="7,3,12"] embeds just those stories, in exactly that order. Pages can now show different, hand-picked sets, and set their own order without touching the global one.
  • New “Build a shortcode” panel on the All Stories screen. Search a story by title or product, click to add it, and the shortcode is written for you with a copy button. The search covers every story, not only the twenty rows on the current page.
  • Copy buttons now work on installs served over plain HTTP. They relied on navigator.clipboard, which browsers only expose in a secure context, and failed outright everywhere else.
  • The two JavaScript globals the front end relied on (productStoriesConfig, productStoriesData) are now prefixed (livostoryConfig, livostoryStoriesData) to avoid colliding with another plugin or theme’s global scope.
  • Removed a leftover wp_localize_script() call that wrote to the same global name a wp_add_inline_script() call immediately overwrote; it was pure dead weight.
  • The public, logged-out view-tracking endpoint now bounds all self-reported values (duration is clamped to a sane range, device type is checked against a fixed list) and throttles repeated submissions from the same visitor for the same story, so a script replaying the endpoint’s own public nonce can no longer flood the views table with arbitrary rows.

1.7.1

  • The story form is now saved on admin_init, before any output. It used to be handled from the page callback, which runs after the admin header is printed, so the redirect could never use wp_safe_redirect() and fell back to an inline script.
  • All remaining inline script and style blocks were moved into enqueued files: the per-instance story payload now attaches to the front-end script handle, and the Add Story preview rules live in the admin stylesheet.
  • The shortcode now escapes the “No story found” message it returns.
  • Product prices and variation markup handed to the front-end script are filtered through wp_kses_post() before they reach the page, and variation image URLs through esc_url(). They are rendered into the DOM by JavaScript, so escaping them server-side is the only place it can happen.
  • The story buy button no longer carries a hardcoded Portuguese label; it uses the translated string the plugin already passes to the front end.
  • Dropped a hardcoded /wp-admin/ path from the upload handler; it uses ABSPATH only.
  • Removed load_plugin_textdomain(), which WordPress has not needed for directory-hosted plugins since 4.6, and stopped bundling translation files. Translations now come from translate.wordpress.org.
  • The “WooCommerce required” notice is limited to this plugin’s own screens and the Plugins list, instead of appearing throughout wp-admin.
  • The package now ships the un-minified sources behind dist/, plus the build configuration, and the readme documents how to rebuild them.

1.7.0

  • Fixed a fatal error that hit every WooCommerce product page: the social-sharing meta tag code called a class that does not exist anywhere in the plugin.
  • WooCommerce is now a declared dependency (“Requires Plugins”), and every entry point that touches a WooCommerce API checks for it first, so the plugin can no longer fatal on a site where WooCommerce is missing or deactivated.
  • Removed the unused video compression subsystem. It never actually ran — its namespace sat outside the plugin’s autoload map, so the class was never loaded — and it was the only reason the plugin shipped php-ffmpeg and its dependency tree. The packaged plugin drops from ~2.5 MB of bundled vendor code to none.
  • “Requires PHP” now reads 8.2, which is what the plugin already required. It previously claimed 7.4, which let sites on older PHP install the plugin and then hit a fatal error on every request, wp-admin included.
  • Security and coding-standards pass over the whole plugin: all output is escaped, translated strings use the escaping translation functions, every database query goes through $wpdb->prepare(), and the admin redesign’s CSS/JS load through wp_enqueue_* instead of raw / tags.
  • Video uploads now land straight in uploads/product-stories through the upload_dir filter, instead of being written to the default month folder and then moved with rename() — a move that could fail silently and leave the upload orphaned.
  • Removed debug logging that wrote form data to the PHP error log on every story save.
  • The admin stylesheet no longer pulls the Outfit typeface from Google Fonts at runtime. WordPress.org plugins must not load third-party assets, and the stylesheet already falls back to the system UI font stack.
  • Removed three unprefixed AJAX actions (delete_story, toggle_story_status, update_story_order). They collided with the generic namespace WordPress.org asks plugins to stay out of, and they were unreachable anyway — the admin screens use the livostory_-prefixed handlers.
  • Added the GPLv3 LICENSE file the plugin header has always pointed at.
  • The shortcode and the front-end AJAX endpoint no longer carry two copies of the same query; both now call one method on the stories provider.

1.6.0

  • New Dashboard card: “Most added to cart” — tracks how many times each story’s buy button actually added the product to the cart (not just clicks), ranked and filterable by the same date range as the rest of the dashboard.
  • Add-to-cart events are only attributed to a story when the click can be verified against the product that was actually added, so the count can’t be skewed by an unrelated add-to-cart action elsewhere on the site.

1.5.0

  • Full redesign of the plugin’s admin screens (Dashboard, All Stories, Add Story, Settings), matching the new dark/pink-orange visual design.
  • Dashboard: real performance metrics (views, unique viewers, watch time, completion) with a period selector and a “most viewed stories” ranking, replacing the previous chart-only view.
  • All Stories: search, status filters, drag-to-reorder, and per-story view counts in one screen.
  • Fixed a long-standing bug where pagination on the All Stories screen never rendered (a variable-name mismatch between the page controller and the template).
  • Fixed the status filter (Active/Hidden) on All Stories, which rendered but never actually filtered the list.
  • Added pt_BR translations for all of the above.

1.4.4

  • Fixed “Stories Carousel” and “Rounded Slides” being selectable at the same time — they’re the same layout (square vs. rounded corners), so they now share one radio group like the rest of the display options.

1.4.3

  • “Rounded Slides” icon now shows three circles with a gradient ring (matching the front-end story ring), like Instagram-style story avatars, instead of a plain rounded square.

1.4.2

  • “Rounded Slides” is now a card in the same visual grid as the display layout options, instead of a plain checkbox — it’s a real, available option today, so it gets the same styling.

1.4.1

  • Moved the display layout picker into the existing Appearance Settings card, next to Rounded Slides, instead of a separate box.
  • “Stories Carousel” (the layout actually in use today) is now the available/default option; Grid, Carousel and Stories are the “Coming soon” ones.

1.4.0

  • Added a display layout picker to Settings (Grid, Carousel, Stories, Stories Carousel cards). Only Grid is functional today; the others are marked “Coming soon” — no artificial pro/paid gating.

1.3.5

  • Fixed the activation date field rendering as an oversized, apparently empty box on iOS Safari (the native date control there sizes itself from the inherited line-height, unlike desktop).

1.3.4

  • Translated the remaining Portuguese source strings in the admin “Add Story” and “All Stories” screens that were missed in 1.2.0 (unaccented words weren’t caught by the earlier sweep).

1.3.3

  • Fixed Tailwind utilities not applying on elements that carry the .wrap class themselves (e.g. centering on the “Add Story” screen) — the previous ancestor-based scoping never matched same-element cases.

1.3.2

  • Fixed the release/packaging scripts still using a leftover boilerplate slug (coffee-wc-checkout) instead of livo-story, which affected the built asset public path and the release zip’s folder/file name.
  • The release build now bundles readme.txt and the real composer.json (previously a generic placeholder without the actual dependencies).
  • yarn release now runs composer install --no-dev itself, so it alone produces a submission-ready package.

1.3.1

  • Guarded against a fatal error on hosts where the optional php-ffmpeg library isn’t installed (video compression is now skipped gracefully in that case, same as when the ffmpeg binary itself is missing).

1.3.0

  • Tailwind CSS is now compiled and bundled locally instead of loaded from cdn.tailwindcss.com, per WordPress.org guidelines against remote-loaded code.
  • Fixed an admin asset-loading regression (an internal page-slug check was accidentally changed during the 1.2.0 i18n pass).

1.2.0

  • All source strings (PHP and JS) are now in English by default, per WordPress.org guidelines.
  • Added a Portuguese (Brazil) translation (languages/livo-story-pt_BR.mo) so existing installs keep the same UI language.

1.1.6

  • Removed hardcoded references to a specific store’s assets from the bundled checkout styles.
  • Unified the plugin’s text domain to livo-story throughout.
  • Added a readme.txt.

1.1.5

  • Added the livostory_extra_cart_fields filter so store-specific add-to-cart fields don’t need to be hardcoded in the plugin.
  • Fixed variable product add-to-cart matching for custom (non-taxonomy) attributes.
  • In-place add-to-cart card expansion, replacing the previous popup.
  • Removed the external Swiper CSS CDN request in favor of the already-bundled local copy.