Version 5.0.4
May 27, 2026
Added
ConditionRuleInterface::renderConfigUi(int $index, array $config): string— third-party conditions can now ship their own value-picker UI in the rule builder.BaseConditionRuledefaults to returning''(no further config).ConditionRuleInterface::isInline(): bool— toggles single-line layout for the variant. Defaults tofalse.
Changed
- Breaking:
ConditionRuleInterfacegained two methods (renderConfigUi,isInline). Conditions extendingBaseConditionRuleare unaffected. Conditions implementing the raw interface need both methods added. - Rule builder no longer hardcodes a
<div class="cnd-variant">block per known condition handle. Each registered condition renders its own variant viarenderConfigUi(), so picker UI for a new third-party condition shows up automatically. RulesControllerno longer fetchessectionOptions/userGroupOptions/ Formie + Freeform form lists / Commerce product lookups for the rule editor — each condition class fetches its own data when rendering.- README now flags the trigger/condition register events as load-order-sensitive — use
Points::getInstance()->triggers->register(...)instead.
Version 5.0.3
May 26, 2026
Added
- Standardised way to add custom triggers — third-party plugins can now ship a trigger and its companion conditions in a single PHP file, registered with one line:
Points::getInstance()->triggers->register(new MyTrigger()). See "Custom triggers (and their conditions)" in the README. TriggerInterface::conditions(): array— return companion conditions; auto-registered with the trigger.TriggerContextvalue object — single payload returned byhandleEvent()carryinguserId, optionalamount/orderId, and ametadatabag.Triggers::register()andConditions::register()sugar APIs.
Changed
- Breaking:
TriggerInterfaceandConditionRuleInterfaceare now instance-based (no more static methods). All built-ins ported. - Breaking: Trigger dispatch collapses from four static methods to one
handleEvent(Event): ?TriggerContext; triggers declare Yii subscriptions viaevents(): arrayand can listen to multiple events. - Breaking:
RegisterTriggersEvent::$triggersandRegisterConditionRulesEvent::$conditionRulesnow hold instances, not class-strings. Triggers::getTriggerClassByHandle()→Triggers::getTriggerByHandle()(returns instance).
Version 5.0.2
May 15, 2026
Added
- Formie integration (Lite) -
Form submittedtrigger andForm iscondition. Auto-registers when Formie is installed. - Freeform integration (Lite) -
Form submittedtrigger andForm iscondition. Auto-registers when Freeform is installed. - Integrations panel in Settings → General, showing which integrations are Installed, Installed but disabled, or Not installed.
- Rules index trigger column now suffixes integrations - e.g.
Form submitted (Formie),Order paid (Commerce)- so shared labels stay distinguishable. Integrationssection in the README listing every supported plugin.- Composer
suggestentries forverbb/formieandsolspace/craft-freeform.
Version 5.0.1
May 15, 2026
Added
PercentReward("% of order total") is now filtered by trigger subject in the rule builder - it only shows up when an Order trigger is selected. Manual / Entry / User triggers don't expose it (without anamounton the trigger the calculation would always be 0). Implemented via a newRewardInterface::appliesToSubjects()static method (defaultnullinBaseReward= applies to all subjects), matching the pattern already used by Conditions.
Changed
- Twig helper
formatMoney()renamed tospendForUser($id?)for consistency withsumForUser/countForUser/levelForUserand the Users index "Available Spend" column.toMoney(points)stays as the lower-level float helper. - README rewritten to be more concise and friendlier - examples consolidated, jargon trimmed, full settings table added, Twig reference split out per-method, Vue/React section folded into the main "Firing a rule" flow.
- Awards element index "Point Award" title column header now reads "Rule" (matches the row content). Implemented via a
displayName()override. - Awards element index Points and Date Created columns are now click-to-sort.
- Latest Awards / Leaderboard dashboard widgets now use Craft's native
tableviewstyling with avatars, configurable column headers that follow the plugin / currency name settings, and a "View all" footer button gated on the matching view permission. - LICENSE.md copyright line updated to
Jason Mayo (ByMayo)to match other ByMayo paid plugins.
Fixed
craft.points.appliedToOrder($orderId)andorderRedemption($orderId)now acceptnullinstead ofTypeError-ing. Fixes the case where a logged-out visitor lands on a page that passescart.id(which can be null) into these helpers.- All single-element Twig lookups (
user,rule,ruleById,ruleByHandle,awardById,levelForPoints,levelById,levelByHandle) are now defensively nullable - return null when given null instead of throwing.
Version 5.0.0
May 14, 2026
First Craft 5 release - complete rewrite from the Craft 2 line (last released at 1.0.3 in 2016). Version jumped to align with Craft 5 itself, the way Craft Commerce and other ecosystem plugins do.
Added
- Plugin editions - Lite (free) and Pro. Lite includes the full gamification feature set (rules, awards, levels, leaderboard, widgets, Entry/Asset/User triggers, Twig + GraphQL APIs). Pro adds Commerce triggers (Orders + Subscriptions), percentage-of-order rewards, Commerce conditions, and order redemptions.
- Visual rule builder (CP) - Zapier-style: WHEN (trigger) → IF (repeatable conditions) → THEN (reward type) → LIMITS (frequency caps) → ACTIVE PERIOD (optional date range).
- Conditions / Limits / Rewards engine - pluggable subsystems, each with a register event so other plugins can ship their own classes.
- Conditions ship: Section, CategoryGroup, Volume, UserGroup, UserLevel, DayOfWeek. Pro adds: OrderTotal, OrderItemCount, OrderHasCoupon, OrderContainsProduct.
- Limits ship: OncePerUser, MaxPerUser (with optional cooldown field).
- Rewards ship: Flat, Deduct. Pro adds: Percent (% of trigger amount). Reward types can declare which trigger subjects they apply to via
appliesToSubjects()- Percent is scoped toorderso it only appears in the rule builder when an Order trigger is selected.
- Triggers - Entry created/updated, Asset created, User registered/logged in/birthday/anniversary on Lite; Order paid/completed/refunded/first-ever and Subscription created/renewed/cancelled/plan-changed on Pro.
PointAwardelement type - awards are first-class elements with a full index, search, source filters, and bulk actions. Row title is the rule name; title column header reads "Rule" (viadisplayName()override) so it matches the content. Points and Date Created columns are click-to-sort. Points snapshotted at award time so editing a rule later doesn't rewrite history.- Levels - tiered loyalty thresholds with optional colour and badge icon (Bronze/Silver/Gold style).
- Leaderboard - CP page (paginated) and dashboard widget showing top users by total balance.
- Latest Awards dashboard widget - the N most recent awards across all users, with user avatars.
- Three ways to fire a Manual rule from the frontend, all sharing the same login-required, CSRF-protected, current-user-only, Manual-only, Limits-enforcing security model:
- HTML form - POST to
points/awards/add(orpoints/awards/remove) withcsrfInput(),actionInput(),redirectInput(), and aruleHandle. - JS API -
{{ craft.points.script() }}defineswindow.Points.addAward(handle)andremoveAward(handle). Cache-safe (CSRF token fetched at runtime), works inside Blitz /{% cache %}/ static cache. - GraphQL mutation -
pointsAddAward(ruleHandle: String!): PointsAddAwardResultfor headless / decoupled SPAs.
- HTML form - POST to
- Order redemptions (Pro + Commerce) - customers spend points against a Commerce order. The
PointsAdjustershows it like a coupon discount. Points deduct onOrder::EVENT_AFTER_ORDER_PAID; refunds restore points per the configured behaviour (proportional / full-only / none). - Awards stamp the originating order (Pro + Commerce) - awards from
OrderPaidTrigger,OrderCompletedTrigger,OrderRefundedTrigger,FirstOrderTriggercarry anorderIdback-reference. OptionalOrdercolumn on the Awards index renders the order as a Craft chip linking to the order edit URL. - Up to four optional columns on the Users element index -
{Currency Plural}(balance) andLevelon Lite;Available SpendandRedeemedon Pro+Commerce. Opt-in via column settings. - Renameable - plugin name (sidebar / breadcrumbs - e.g. "Rewards System") and Reward unit labels (singular / plural - e.g. "Coin" / "Coins") configurable in Points → Settings → General. Plugin handle, URLs, table names, and Twig API namespaces unchanged - display labels only.
- Conversion rate stored as a paired
conversionPointsCount+conversionCurrencyUnitssetting so admins can express ratios like "5 points = 1 unit". Currency itself tracks the Commerce primary store automatically (no symbol setting). - Plugin settings live in their own DB table, not Project Config - admins can rename things on production without staging deploys clobbering them. Devs can pin per-environment values via
config/points.php. - Tabbed Settings page extending
_layouts/cpwith native Craft tabs - General, Commerce (Pro), Rules. - Settings link in the CP sidebar, gated on
points-manageSettings. Craft's gear-menu route redirects to the same page. - Granular permissions - View + Create + Edit + Delete per resource (Awards, Rules, Levels), plus
points-viewLeaderboard(read-only) andpoints-manageSettings. Child permissions only grantable once the parent is granted. The Points sidebar item and each sub-page hide entirely when the user has no relevant permissions. - Dashboard widget visibility - widgets check the user's
view-*permission before appearing in the widget picker. - Plugin events for extensibility:
Awards::EVENT_BEFORE_ADD_AWARD(cancellable; handlers can modify points)Awards::EVENT_AFTER_ADD_AWARDAwards::EVENT_BEFORE_REMOVE_AWARD(cancellable)Awards::EVENT_AFTER_REMOVE_AWARDLevels::EVENT_LEVEL_CHANGED
- Pluggable subsystem events:
Triggers::EVENT_REGISTER_TRIGGERS,Conditions::EVENT_REGISTER_CONDITION_RULES,Limits::EVENT_REGISTER_LIMITS,Rewards::EVENT_REGISTER_REWARDS. - GraphQL - queries
pointsRules,pointsRule,pointsLevels,pointsLevelForUser,pointsAwards,pointsSumForUser,pointsCountForUser,pointsLeaderboard. MutationpointsAddAward. Types:PointsRule,PointsLevel,PointsAward,PointsLeaderboardRow,PointsAddAwardResult. - Twig API -
craft.points.*with helpers for rules, awards, levels, leaderboard, money conversion, and the cache-safescript()helper. Pluscurrency,currencyPlural,symbol,currencyCode,pluginName,isPro. Per-user money helper renamedformatMoney()→spendForUser($id?)for consistency withsumForUser/countForUser/levelForUser(and the Users index "Available Spend" column). All single-element lookups (user,rule,ruleById,ruleByHandle,awardById,levelForPoints,levelById,levelByHandle,orderRedemption,appliedToOrder,spendForUser) accept null and return null / 0 / '' gracefully, so templates can passcurrentUser.id,cart.id,award.ruleIdetc. without guarding against logged-out or cartless visitors. - Plugin / service helpers:
Points::hasCommerce(),getStoreCurrencyCode(),getStoreCurrencySymbol(),formatStoreMoney(), staticpointsToMoney().Awards::getRedeemedPointsForUser().OrderRedemptionsservice (apply,remove,getForOrder,processPaidOrder,processRefund). - Trigger API:
TriggerInterface::isAvailable()(hide triggers from the picker until their dependencies are met - e.g.UserBirthdayTriggerwaits until a Date field handle is set on the user layout) andTriggerInterface::getOrderIdFromEvent()(lets order triggers stamp their award with the source order ID).
Changed (breaking from the Craft 2 plugin)
- "Event" renamed to "Rule" throughout - the word "event" was overloaded with Craft's PHP
Eventconcept. Affects DB tables, class names, service properties, Twig/GraphQL APIs, CP URLs, and permissions. - "Entry" renamed to "Award" throughout to avoid clashing with Craft's own Entry element. Affects DB tables, element class (
PointEntry→PointAward), service (Entries→Awards), Twig/GraphQL APIs, plugin events, permissions, and CP URLs. - Currency is no longer configured in the plugin - tracks Craft Commerce's primary store automatically.
- Money helpers are Pro + Commerce only -
craft.points.toMoney(),spendForUser(),symbol,currencyCode,orderRedemption,appliedToOrder, theAvailable Spend/Redeemedcolumns, and the order-redemption flow all require Commerce installed. - Twig
craft.points.addAward()/removeAward()removed - they bypassed CSRF and accepted arbitraryuserIds. Use the form / JS API / GraphQL mutation from public pages, orPoints::getInstance()->awards->addAward($userId, $handle)from server-side PHP. - Twig
craft.points.addEventremoved - rules are CMS-managed only now. - Permissions overhauled - the Craft 2 plugin's single
points-manageEvents/points-manageEntries/points-manageLevelstier is replaced with View + Create + Edit + Delete per resource, pluspoints-viewLeaderboardandpoints-manageSettings. - Minimum requirements - Craft 5.6+, PHP 8.2+.
- Namespace -
Craft\PointsPlugin→bymayo\points\Points.