What Does This Thing Do?
It puts a searchable grid and a modal form on any staff page, wired to a Defined Type of your choosing. Staff manage the values; the app enforces the rules.
- Add, edit, and delete Defined Values from a friendly grid, no Admin Tools required
- Type-to-search filters the list as you type
- Deletes require typing
yes delete, so nobody nukes a value by accident
- Re-sorts the Defined Type automatically after every save and delete (longest-first, A-Z, or Z-A, your pick)
- Attribute fields render from config: text, textarea, boolean, single-select, multi-select, and defined-value lookups
- One install manages many Defined Types; each one is just an entry in a config array
Pairs Great with the Translation Recipe
This is the companion piece to The Rosetta Stone - Translate anything. That recipe stores translation pairs in a Defined Type and needs them sorted longest-first; this one gives your staff a guarded UI for those pairs and handles the sort automatically on every save. No more re-running the sort SQL. And if you would rather skip maintaining pairs by hand entirely, my OmniLingua plugin on the Rock Shop does full page translation with less setup and better performance.
How It Works
The Moving Parts
- A Lava Application holds four shared endpoints: list, edit, save, delete.
- Configuration Rigging (a JSON blob on the application) describes each Defined Type you want to manage: labels, fields, sort behavior.
- A Lava Application Content block on any staff page renders the grid and picks which config entry to use via a
cfgKey.
- HTMX handles the partial updates; the page never fully reloads.
What Makes It Nice
- Adding another Defined Type later is a config entry and a content block; zero endpoint changes.
- Access is controlled by the application's security, not by Rock admin rights.
- The grid, modal, and forms use Rock's own styling, so it looks at home in your admin site, dark mode included.
- Sort logic runs server-side after every change, so the order is always right.
Before You Start
Lava Applications require Rock v17 or later. You'll also want a Defined Type to manage; if you're coming from the translation recipe, that's your Page Translation type and you're all set.
The Setup in 4 Steps
Work through these in order. Grab the attached files first; you'll paste one into each endpoint as you go.
Step 1 - Create the Lava Application
- Go to
Admin Tools > CMS Configuration > Lava Applications
- Create a new application:
- Name: Manage Defined Values
- Slug:
manage-defined-values
- Active: checked
- Paste the contents of the attached
ConfigurationRigging.json into the Configuration Rigging field. We'll customize it in Step 2.
- On the application's Security, grant Edit to the roles that should manage values. The endpoints all run in Application Edit mode, so this one security setting is the whole access story.
Step 2 - Customize the Configuration Rigging
The rigging is a Configs array; each object describes one Defined Type you want to manage. The content block picks an entry by its Key. Here's a minimal entry:
{
"Configs": [
{
"Key": "spanish-translation",
"DefinedTypeId": "123",
"Title": "Page Translations",
"Description": "Manage English-to-Spanish translations.",
"ValueLabel": "Text to be Translated",
"ValuePlaceholder": "Enter English text",
"DescriptionDefault": "es",
"ShowDescription": "false",
"ShowActive": "true",
"Sort": "length-desc",
"Attributes": [
{
"Key": "TranslatedText",
"Label": "Spanish Translation",
"Placeholder": "Enter Spanish translation",
"Required": "true",
"Type": "text"
}
]
}
]
}
Swap 123 for your Defined Type Id. The fields:
| Field |
What It Does |
Key |
Unique slug the content block uses to pick this config. |
DefinedTypeId |
The Defined Type this entry manages. |
Title / Description |
Heading and subheading shown above the grid. |
ValueLabel / ValuePlaceholder |
Label and placeholder for the Value field; always shown, always required. |
ShowDescription / DescriptionLabel |
"true" shows a Description field and column. When "false", DescriptionDefault is silently saved as the Description instead; the translation recipe uses this to stamp every pair with its language code. |
ShowActive |
"true" shows the Active toggle and column; "false" hides them and new values default to active. |
Sort |
How values re-order after every save and delete: length-desc (longest first, what the translation recipe needs), alpha-asc, alpha-desc, or none. |
Attributes |
One object per Defined Type attribute you want on the form. Key must exactly match the attribute key in Rock (case-sensitive). Type is one of text, textarea, boolean, single-select, multi-select, defined-value, defined-value-multi. Select types take an Options array of { "Value": "...", "Text": "..." }; defined-value types take a DefinedTypeId and query live values at render time. |
The attached rigging file includes a second example entry (a Schools list with a multi-select Grades attribute) so you can see a fuller shape. Delete what you don't need.
Step 3 - Create the Four Endpoints
Add four endpoints to the application, one per attached file. Same drill each time: set the name, slug, and settings from the table, then paste the matching file into the Code Template.
| Name |
Slug |
Method |
Security Mode |
Enabled Lava Commands |
File |
| List |
list |
Get |
Application Edit |
Rock Entity |
ListEndpoint.lava |
| Edit |
edit |
Get |
Application Edit |
Rock Entity |
EditEndpoint.lava |
| Save |
save |
Get |
Application Edit |
Rock Entity, Rock Entity Modify, Sql |
SaveEndpoint.lava |
| Delete |
delete |
Get |
Application Edit |
Rock Entity, Rock Entity Delete, Sql |
DeleteEndpoint.lava |
The Lava commands and Application Edit security mode are load-bearing; if an endpoint silently returns nothing, a missing command or a viewer without Edit rights on the application is the usual suspect.
Step 4 - Add the Content Block
- Go to the staff page where you want the manager to live (an internal page your team already visits works great)
- Add a block of type Lava Application Content
- Point it at the Manage Defined Values application
- Paste the contents of the attached
ContentBlock.lava into the template
- At the top of the paste, set the key to the config entry you want this page to manage:
{% assign cfgKey = 'spanish-translation' %}
That's the whole wiring. Want the same manager for a different Defined Type on another page? Add a config entry to the rigging, drop another content block, and change that one line.
Using It Day to Day
Once it's on the page, here's the whole workflow your staff needs to know. It's pretty straighforward it works like every other grid in Rock.
| To do this |
Do this |
| Find a value |
Type in the search box above the grid. Rows filter as you type, across every column; clear the box to see everything again. |
| Add a value |
Click the Add button in the panel header, fill in the form, click Save. Required fields are marked; the save won't go through without them. |
| Edit a value |
Click the pencil on its row. The form opens pre-filled, including select and multi-select attributes; change what you need and click Save. |
| Deactivate a value |
Edit it and uncheck Active. This is the gentle option; the value stays in the list (shown without the green check) and can be flipped back on later. |
| Delete a value |
Click the trash icon, then type yes delete in the confirmation box and click Permanently Delete. Anything else typed in the box refuses to delete; that's the guardrail doing its job. |
Two things happen quietly on every save and delete, and they're the reason this beats editing the Defined Type directly:
- The sort runs itself. Whatever
Sort your config entry specifies is re-applied to the whole Defined Type after each change. For translation pairs that means longest-first ordering is always correct and the "First Nombre" matching bug can't creep in, no SQL, no remembering.
- Hidden defaults are stamped. If the config hides the Description field, every value staff create still gets the configured default saved behind the scenes (the translation setup uses this to stamp
es on every pair). Staff can't forget a field they never see.
For the translation workflow specifically: when a ministry asks for a new phrase, staff search first to make sure it isn't already there, add the English text and its translation, and they're done. The page picks up the new pair on its next load. The only rule worth repeating from the translation recipe is to leave trailing periods out of the English text.
Troubleshooting
- Grid loads but buttons do nothing, or the modal shows the wrong form. One of the endpoints is on an older paste than the others. These five files share element ids, so update all of them together, not just the one you changed.
- Endpoint returns nothing. Check the enabled Lava commands against the table in Step 3, and confirm the viewer has Edit on the application.
- Attribute fields come back empty. The
Key in the rigging must match the attribute key on the Defined Type exactly, including case.
- Values save but land in the wrong order. Check the
Sort setting on your config entry; none leaves Rock's manual ordering untouched.
- Delete refuses to run. That's the guardrail; the confirmation box needs
yes delete typed into it.
- Changes don't show for other staff. Defined Values are cached; a browser refresh gets the new list, since the grid queries live on every load.
That's It!
Four steps and you've got a guarded Defined Value manager your staff can actually be trusted with. Hand them the page, keep your admin rights to yourself, and never re-run that sort SQL again.