Writes user-friendly logger messages in active voice for Simple History event logs. Fixes passive voice issues...
Write clear, user-friendly messages for Simple History event logs.
Write as if someone is telling you what they just did.
ā
DO ā DON'T
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Activated plugin Plugin was activated
Created menu Menu has been created
Updated settings Settings were updated
Published post Post has been published
public function getInfo() {
return [
'messages' => [
'plugin_activated' => __( 'Activated plugin', 'simple-history' ),
'plugin_deactivated' => __( 'Deactivated plugin', 'simple-history' ),
'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),
],
];
}
Keys must be globally unique across all loggers (used as RFC 5424 MSGID).
// ā
Good - descriptive prefix
'plugin_activated', 'theme_switched', 'user_logged_in'
// ā Bad - too generic
'activated', 'updated', 'deleted'
Verify uniqueness: grep -r "'your_key'" loggers/
The message body is a declarative sentence ("what happened?"). Action links (rendered below the message, see the action-links skill) are the canonical "what can I do?" affordance. Wrapping {post_title} (or any other interpolated token) in an <a> tag inside the template puts a CTA mid-sentence and competes with the action row.
Rule: Inline links inside message templates are permitted only when they point somewhere the action row cannot reach. If get_action_links() already covers the destination (Edit, View, Revisions, the overview page, ā¦), the message must be plain text.
// ā Don't ā Edit/View action links already point to the post.
'post_updated' => __( 'Updated post "<a href="...">{post_title}</a>"', 'simple-history' ),
// ā
Do ā plain title, action row handles navigation.
'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),
Deleted items: still plain text. A dead link is worse than no link ā the overview action link (All pages, All plugins) is the right hand-off.
Legitimate exception: the link goes somewhere no action link can express (e.g. an arbitrary external reference). Then an inline link is additive, not redundant.
Not an urgent migration ā apply opportunistically when touching a logger for other reasons.
Prefix all context keys with the entity name to avoid collisions and keep keys self-documenting.
// ā
Good - prefixed with entity
'plugin_name', 'plugin_current_version', 'theme_new_version'
'site_health_status', 'site_health_label', 'site_health_badge_label'
// ā Bad - too generic
'test', 'label', 'status', 'name', 'version'
Use the Event Details API for get_log_row_details_output(). Never build raw HTML with SimpleHistoryLogitem__keyValueTable.
use Simple_History\Event_Details\Event_Details_Group;
use Simple_History\Event_Details\Event_Details_Group_Table_Formatter;
use Simple_History\Event_Details\Event_Details_Item;
public function get_log_row_details_output( $row ) {
$group = new Event_Details_Group();
$group->set_formatter( new Event_Details_Group_Table_Formatter() );
$group->add_items(
array(
// Read value directly from context key.
new Event_Details_Item( 'status', __( 'Status', 'simple-history' ) ),
// Read new/prev pair from context (looks for key_new and key_prev).
new Event_Details_Item( array( 'setting_name' ), __( 'Setting', 'simple-history' ) ),
)
);
return $group;
}
Formatters:
Event_Details_Group_Table_Formatter ā key-value table (default)Event_Details_Group_Diff_Table_Formatter ā before/after with diffsEvent_Details_Group_Inline_Formatter ā compact inline textManual values (when context keys don't match conventions):
( new Event_Details_Item( null, __( 'Label', 'simple-history' ) ) )
->set_new_value( $value )
See docs/architecture/event-details.md for full API reference.
When the structured API can't express your output (images, HTML content, color swatches):
Item_RAW_Formatter ā Full custom HTML/JSON for an item (no name column)Item_Table_Row_RAW_Formatter ā Table row with escaped name + raw HTML valueuse Simple_History\Event_Details\Event_Details_Item_Table_Row_RAW_Formatter;
$raw_formatter = ( new Event_Details_Item_Table_Row_RAW_Formatter() )
->set_html_output( sprintf( '<a href="%1$s">%2$s</a>', esc_url( $url ), esc_html( $url ) ) )
->set_json_output( [ 'url' => $url ] );
$item = ( new Event_Details_Item( null, __( 'URL', 'simple-history' ) ) )
->set_formatter( $raw_formatter );
Use RAW formatters sparingly ā only when no structured formatter fits.
Navigational links (Edit, View, Preview) belong in get_action_links(), not inside get_log_row_details_output(). See the action-links skill.
Old loggers often embed <a> tags in the details table (e.g., "View/Edit" comment link, "View plugin info" thickbox). When migrating these loggers:
get_action_links()The only case for a link inside details is when the value itself is a URL (e.g., a plugin's homepage URL displayed as data). Use Item_Table_Row_RAW_Formatter for that.
Many older loggers build HTML manually with SimpleHistoryLogitem__keyValueTable tables. When migrating:
| Old pattern | New approach |
|---|---|
<table class='SimpleHistoryLogitem__keyValueTable'> with <tr>/<td> |
Event_Details_Group + Group_Table_Formatter |
<ins> / <del> for changed values |
Event_Details_Item with set_values() (auto-generates ins/del) |
<span class='SimpleHistoryLogitem__inlineDivided'> |
Event_Details_Group + Group_Inline_Formatter |
Inline <a href> links to edit/view |
Move to get_action_links() |
| Images, color swatches, shortcode output | Item_RAW_Formatter or Item_Table_Row_RAW_Formatter |
Standalone <p> text blocks (not key-value) |
Group_Inline_Formatter with a single item, or RAW formatter |
Value transforms (e.g., true ā "Enabled", locale ā display name): Transform in PHP, then pass to set_new_value() / set_prev_value().
Conditional rows: Don't set values for items you want hidden ā the container auto-removes empty items.