github austinginder/minn-admin v0.29.0

4 hours ago

The product release. Orders became real pages a few cycles ago and products follow now, further than orders went. A product used to be a modal with a handful of fields, so almost every real edit still finished in WooCommerce. It is a page at its own URL now, and it holds on one screen what WooCommerce spreads across the Product data tabs: pricing with its sale schedule and tax class, inventory down to GTIN, backorders and a low stock threshold, shipping, the product image and gallery as tiles you drag to reorder, categories, tags and brands, upsells and cross-sells, attributes, and variations that a variable product can generate from its own attributes. The product type is a control here too, so marking something downloadable or external rearranges the page around it without losing anything you had already typed. The long description opens in Minn's own editor rather than sending you back. The page settled a house rule while it grew, one that now holds across all of it: every choice is one of Minn's own comboboxes and every yes or no is a switch, so no operating-system menu punches a pale hole through a dark screen. Around the store work, licensing and connections keep filling in. Every Brainstorm Force product gets its own key and renewal date, SureRank joins the SEO panel, OttoKit's outgoing webhooks become something you can search and retry, and SureCart's store connection appears beside the rest. Smaller comforts run through the release as well: older dates name their year, the content list sorts by title, a post carrying unsaved edits wears a quiet dot instead of a second status, and the caret stays where you put it when a list finishes loading underneath you.

Added

  • Every Brainstorm Force product can be licensed from Minn. Astra Pro, Ultimate Addons for Beaver Builder and for Elementor, Convert Pro, Schema Pro, WP Portfolio, Premium Starter Templates and Spectra Blocks Pro used to appear on the Licenses tab as a single read-only list. Each is now its own row with its own key: paste a key to activate, free the seat again with Deactivate, and ask Brainstorm Force to confirm a license with Re-verify, all through the plugin's own activation code. Licensed products show their renewal date once they have been verified, and a product Minn has never heard of is covered the day Brainstorm Force ships it.

  • SureRank joins the SEO panel. Sites running SureRank now edit their SEO title, meta description, focus keyword and social thumbnail from the editor, the same panel Yoast, Rank Math, AIOSEO, SEOPress and SiteSEO already fill. Clearing a field really clears it: SureRank treats an empty box as "use the site-wide template" and writes that template into the post, so Minn removes the value instead of storing the template as though you had typed it.

  • OttoKit's outgoing requests are visible in Minn. OttoKit runs your automations from its own cloud, but every webhook this site fires at it is recorded here, and that record is what you want when an automation did not run. The new Automation surface lists those requests with their response code and error, filtered by delivered or failed, searchable by endpoint, with the payload on the card and Retry available one at a time or in bulk. Retrying goes through OttoKit's own retry, so the attempt count and status stay its own. Workflows and run history live in OttoKit and Minn links out to them rather than showing a stale copy.

  • SureCart shows whether your store is connected. SureCart keeps every order, customer and product in its own service, reached with a site token, so that connection is the whole local story. It now appears with your other site connections on the Licenses tab: connected or not, with a link to SureCart's own setup when it is not. The token itself is only checked for presence and never read.

Improved

  • Products open as a page. A product used to be a modal you clicked a row to get, which meant it could not be linked to, bookmarked or opened in a second tab. Clicking a row now opens /minn-admin/products/{id}, a real page with a back button and the same fields the modal had. The modal stays as a quick look: hover a row and click the eye, or right-click and choose Quick view. Both surfaces share one body, so anything added to the page arrives in the quick view too. The long product description now opens in Minn's own editor, with blocks, autosave and revisions, rather than sending you to WooCommerce.

  • The product page covers inventory and shipping. It used to stop at name, SKU, status, visibility, price and stock, which meant most edits still ended in WooCommerce. The page now carries the identifier fields (SKU and GTIN, UPC, EAN or ISBN), the stock policy that goes with tracking quantity (backorders and a low stock threshold), the one-per-order limit, and shipping: weight, dimensions and shipping class picked from the classes your store actually has. The fields are grouped the way WooCommerce groups them, as Basics, Pricing, Inventory and Shipping, but on one page instead of behind tabs. A virtual product hides shipping, the same as WooCommerce does. Leaving the low stock threshold empty means your store-wide setting, not a threshold of zero.

  • Products are organized from the product page. Categories, tags, brands, the slug and the featured flag now live on the page in an Organization card. Each taxonomy shows what is assigned as chips you can click to remove, with a search box under it that looks through the terms your store already has. Tags and brands can be made on the spot: type a name that does not exist and press Enter. Categories are pick-only on purpose, since a typo there would leave a stray category in your shop's navigation. Brands appear only if your WooCommerce has them.

  • Product images are managed on the product page. The page shows the product image and the whole gallery as a row of tiles. Drag a tile to reorder, click one to swap it for something else in your media library, and hover it for the × that removes it. The first tile is labelled, because that is the picture WooCommerce shows in the shop, so promoting a gallery image to the product image is a drag rather than a trip to WooCommerce. Add images opens the media picker and appends what you choose. It all saves with the rest of the page, in one Save changes.

  • The product page covers every product type. The type is a control on the page now, not something you leave for WooCommerce: switch between simple, grouped, external and variable, and mark a product virtual or downloadable. The page follows along. A virtual product drops the shipping fields, a downloadable one gains a Downloads card where you name each file and point it at a URL or pick it from your media library, with the download limit and expiry beside it, and an external product asks for the address and the button text that send shoppers there. Anything you have already typed stays put when the page rearranges itself.

  • Upsells and cross-sells are set on the product page. A Linked products card searches your catalogue by name or SKU and holds what you pick as chips: upsells appear on the product's own page, cross-sells in the cart. A product never offers itself, and reopening the page shows the names of what you linked rather than a row of ids.

  • Product attributes are edited on the product page. Each attribute is a row: its name, its values separated by commas, and two switches for whether shoppers see it and whether a variable product varies by it. Attributes you invent for one product are typed in place; attributes your whole store shares are picked from a list and keep their name as a label, since that name belongs to the store rather than to this product. Creating a brand new store-wide attribute is still WooCommerce's job.

  • Variable products build their variations in Minn. A variable product gets a Variations card listing each variation with its attribute values, SKU, price, sale price and stock status. Generate from attributes creates every combination your attributes allow and skips the ones you already have, or add them one at a time. Everything saves with the page's own Save changes, and saving twice updates your variations rather than duplicating them.

  • Older dates on lists name the year. Once a date is more than a week away, Minn still shows a short month and day (Aug 17). Dates from another year now add the year (Aug 17, 2025), so a long-lived site's content list no longer looks out of order. This year's dates stay compact. The same wording is used everywhere a relative date ages out: comments, orders, users, and plugin surfaces.

  • The content list sorts by title or date. Click the Title or Date header to sort. Date starts newest first, Title A to Z; click again to flip. The default stays newest first, which is what a blog wants. Author and status are not sortable.

  • The theme button is a light/dark switch. Clicking it used to walk System, Light and Dark, so a second click could land you on follow-the-OS when you only wanted the other color. Click now flips light and dark only, locking that choice. Follow the system is still on the right-click menu, and on Your profile.

  • Unsaved edits are a mark on the status, not a second status. A live post with a newer autosave used to wear an amber Modified pill next to Published, which stacked two labels in a narrow column. The status pill now carries a small amber dot instead: one pill, one line, and the same quiet marker the sidebar uses to say something is waiting for you. Hover it for the explanation. The Modified filter is unchanged.

Fixed

  • Content counts describe the list underneath them. An author signing in saw a sidebar count and an item count taken from every post on the site, above a list holding only their own. The two numbers could sit above an empty list entirely. WordPress hands back the total for a query and then removes the rows the person is not allowed to edit, so Minn now asks only for what that person can actually work on, and the count and the list agree. Authors and contributors get a count of their own writing, which is the useful number for them. Editors and administrators see no change. The same correction covers a page WordPress hides from anyone who cannot manage privacy settings, which used to leave an editor's count one too high.
  • The Stream activity log opens again. Sites running Stream saw a permission error where its activity log should have been, even for administrators. Stream only grants its own view capability on its wp-admin screens, so nothing outside them could read it. Minn now applies Stream's own Role Access setting directly, which means the log opens for exactly the roles Stream is configured to allow: take administrator out of that setting and administrators are refused here too.
  • Newly installed Brainstorm Force products no longer stay invisible. These plugins only add themselves to their shared registry while somebody is looking at the WordPress dashboard, so a product installed over the command line, the way most hosts provision sites, was missing from the Licenses tab entirely. Minn now refreshes that registry itself.
  • Typed web addresses look like web addresses again. The monospaced typeface Minn uses for values ships programming ligatures, and one of them pushed the colon away from the slashes, so an address you typed showed as "https: /example.com" in fields like a redirect's target. Nothing was ever stored wrong and the address always saved correctly, but it read as though Minn had mangled it. Minn shows that typeface for data rather than for writing code, so those substitutions are off now.
  • Selected chips are readable in dark mode. A chip that could be clicked to remove it, the kind used for a post's tags and now for a product's categories, brands and tags, drew its text in the browser's own default colour rather than Minn's. On a dark panel that is near black on near black, so the label was there but almost invisible. Chips now carry their own colour, and a chosen one wears the same accent treatment as everything else that is switched on.
  • The caret stays in the search box after a list finishes loading. Arriving at Media put the caret in its search box and then quietly took it away again a moment later, when the list finished loading and redrew the toolbar. Anyone who paused before typing lost their first keystrokes to nothing. The caret is put back when a redraw is what removed it, and left alone when you have moved somewhere else yourself.

Security

This release closes the findings from a full audit of v0.28.0. Nothing here is known to have been used against a site, and most of it needed an account on the site to begin with, but several were real and are worth naming plainly.

  • One tenant of a network can no longer reach another's. On multisite, an administrator of a single site could turn a plugin off for every site on the network, including its security plugin, and could switch to a theme the network administrator had deliberately withheld from them. They could also read and spend the network owner's paid licenses: wiping the stored credentials so no site received updates, or releasing a seat at the vendor, which cannot be undone from here. Licenses, plugin activation and theme activation now belong to the network administrator, and the Licenses tab is simply absent for anyone else rather than failing when opened. The same boundary was extended to the two database health checks that were still counting other tenants' tables, and promoting somebody to network administrator now asks for the capability WordPress asks for.

  • Editing a post no longer turns a contributor's markup into working code. WordPress stores a contributor's submission safely, but opening it in an editor decodes it again, and Minn rebuilt some blocks (quotes, lists, tables, verse, preformatted text and buttons) in a way that could turn that decoded text back into a live event handler when you saved. The handler would then run for every visitor and for the next person to open the post. Every attribute Minn rebuilds is now escaped on the way out, and a test suite keeps it that way.

  • Logged-out visitors can no longer list which posts have unsaved edits. The flag itself was correctly kept to editors, but the filter that searched on it was not, so anyone could ask the site for exactly the posts whose authors had work in progress.

  • Ordinary accounts can no longer mint themselves a permanent login link, or read another account's settings. WordPress treats "may edit this user" as always true about yourself, which two places relied on: a subscriber could create a one-time login URL for their own account, and that URL kept working after logout and after an administrator reset the password, and a subscriber could read or change another person's hidden-items list by naming them in the request body. Making a link for yourself is refused outright now, re-issuing one revokes the old one, and every per-user route reads the account named in the address bar and nothing else. A subscriber can also no longer make the site send mail from its own address to an inbox they control.

  • A file's alt text can no longer escape into the markup of a post. Alt text can be set by anyone allowed to upload, and a few characters that mean something special to a text replacement were being expanded rather than copied when Minn wrote an image into a post. Carefully chosen alt text could use that to close the attribute it was supposed to sit inside and add markup of its own, which was then saved into the post by whoever was editing. The affected paths were the image swapper and the two places block settings are parked, both of which take values from block settings that WordPress does not inspect. Values are now copied literally wherever they reach a replacement.

  • Smaller hardening. Contributors can no longer enumerate field definitions for post types they cannot edit; editors no longer see the parts of the Aryo activity log its own plugin withholds from them, including administrator actions and visitor IP addresses; a site that forbids editing code from the dashboard now has that respected by the Header Footer Code Manager, FluentSnippets and WPCode paths that had missed it, and by the CSS route that could break out of its own style block; a site created on a subdirectory network can no longer take an address that would break the whole network's REST API; and the network administrator's email address now goes through WordPress's confirmation step instead of changing silently.

Don't miss a new minn-admin release

NewReleases is sending notifications on new releases.