---
title: Upvote Overlord | Feature Request Board for YOOtheme Pro
description: Turn user feedback into ranked feature requests with Upvote Overlord. A Joomla voting board with moderation, roadmap and anti-spam tools.
url: https://www.yoozecase.com/upvote-overlord
---

# Upvote Overlord

**Latest Version**

**Release date**

**Made for**

Joomla

**Price**

[Demo page](https://www.yoozecase.com/upvote-overlord-demo)

Your visitors have opinions. Left unchecked, they scatter them across emails, DMs, and the void. Upvote Overlord summons them into one arena, ranks their demands by votes, and lets you rule the outcome. They beg for features. The crowd decides which are worthy. You ship, and you take the credit. Install it, and never wonder what your people want again, because they will tell you, in order, whether you asked or not.

![Upvote Overlord](https://www.yoozecase.com/files/upvote-overlord.png)

---

## Description

Upvote Overlord. A feature-request board for those who prefer to rule.
So. You have a website, and you have users, and the users have ideas. Adorable. Until now those ideas arrived as scattered emails, buried comments, and the occasional angry tweet you pretended not to see. No structure. No ranking. No throne. That ends today.

Upvote Overlord gives your visitors a single arena to submit their requests and upvote each other's. The best ideas rise to the top on their own, vote by vote, while the weak ones sink where they belong. A top-voted box crowns the current favorite for all to see. Your people do the sorting for you. This is delegation, and it is beautiful.

You are not merely a spectator to this democracy. You are its Overlord. A frontend moderation toolbar lets you approve, hide, and command without ever touching the backend. Every ruling is written to an audit log, because a wise ruler remembers. Set a review deadline and the round closes itself, then reveals the winners with appropriate ceremony. Anti-spam and rate limiting keep the rabble and their bots in line, so the votes that count are the ones cast by real subjects.

When it is time to govern openly, flip the board into Roadmap view and every request marches into its column: Open, Planned, In Progress, Shipped, Declined. Your users watch their wishes advance through your kingdom in real time, which keeps them patient, and keeps them off your inbox. A backend dashboard module reports the state of your realm at a glance from the Joomla Control Panel.

And because an Overlord must look the part, the board arrives dressed for the occasion, with designed themes including Leaderboard Arena, Neon Stage, and Retro Pixel Arcade. It speaks six languages, so no corner of your empire is left out.

Your competitors are still reading feature requests one email at a time. You could be watching yours line up and salute. Install Upvote Overlord. Let them vote. You decide.

---

#### Top voted

';
 }
 html += '

';
 if (oc.title) {
 html += '

##### ' + escapeHtml(oc.title) + '

';
 }
 if (oc.text) {
 html += '

' + escapeHtml(oc.text).replace(/\n/g, '
') + '

';
 }
 html += '

';
 }

 // Auto winner(s) - top N from the already vote-sorted features
 if (mode === 'auto_winner' || mode === 'both') {
 var n = oc.winnerCount | 0;
 if (n ';
 for (var i = 0; i ' +
 (w.image_url ? '

![](https://www.yoozecase.com/' + escapeHtml(w.image_url) + ')

' : '') +
 '' + (i + 1) + '' +
 '' + escapeHtml(w.title) + '' +
 '' + getIcon(this.cfg.voteIcon) + ' ' + (w.votes | 0) + '' +
 '';
 }
 html += '';
 }
 }

 if (html === '') {
 this.elOutcome.hidden = true;
 return;
 }
 this.elOutcome.innerHTML = html;
 this.elOutcome.hidden = false;
 };

 /**
 * Roadmap view (v3.2.0): renders the items as status columns, in the
 * canonical status order from cfg.statuses. Each column is an  inside
 * the existing .yc-fb-list ';
 }
 return html;
 };

 Board.prototype.renderItem = function (f, index) {
 var disabled = this.cfg.isClosed ? 'disabled' : '';
 var voted = f.voted ? ' yc-voted' : '';
 var descHtml = f.description
 ? '

' + escapeHtml(f.description).replace(/\n/g, '
') + '

'
 : '';
 var thumbHtml = f.image_url
 ? '

![](https://www.yoozecase.com/' + escapeHtml(f.image_url) + ')

'
 : '';

 // Status workflow (v3.0.0): badge for any non-default status. The
 // roadmap view (v3.2.0) skips the badge - the column already
 // communicates the status, a badge would be redundant noise.
 var st = (f.status || 'open');
 var labels = this.cfg.statusLabels || {};
 var statusBadge = (!this.isRoadmap && st && st !== 'open' && labels[st])
 ? '' + escapeHtml(labels[st]) + ''
 : '';

 // Design themes (v3.1.0): arena / neon / pixel render extra per-item
 // furniture - rank number, avatar (image or initials), a "leader" pill
 // on rank 1, and a "votes" label. Classic/colour themes are untouched.
 var t = this.cfg.theme || 'light';
 var themed = (t === 'arena' || t === 'neon' || t === 'pixel');
 var rank = (typeof index === 'number') ? (index + 1) : 0;
 var rankHtml = '', leaderHtml = '', avatarHtml = '', votesLabel = '';
 if (themed) {
 rankHtml = '' + pad2(rank) + '';
 if (rank === 1) {
 leaderHtml = '' + escapeHtml(this.cfg.leaderLabel || 'Leader') + '';
 }
 if (f.image_url) {
 avatarHtml = '

![](https://www.yoozecase.com/' + escapeHtml(f.image_url) + ')

';
 } else {
 avatarHtml = '

' + escapeHtml(initialsOf(f.title)) + '

';
 }
 votesLabel = '' + escapeHtml(this.cfg.votesLabel || 'votes') + '';
 }

 // Moderation overlay: subtle corner buttons, only when canModerate.
 // Buttons appear on hover/focus (styled in CSS). Even in customizer
 // preview we render them when canModerate is true so the moderator
 // can see what visitors with moderation rights will see.
 var modHtml = '';
 if (this.cfg.canModerate) {
 var parts = [];
 var hideTitle = escapeHtml(this.t('hideTitle', 'Hide this feature'));
 var deleteTitle = escapeHtml(this.t('deleteTitle', 'Delete this feature'));
 if (this.cfg.allowSoftHide) {
 parts.push(
 ''
 );
 }
 if (this.cfg.allowHardDelete) {
 parts.push(
 ''
 );
 }
 if (parts.length || (this.cfg.statuses && this.cfg.statuses.length)) {
 var sParts = parts.slice();
 if (this.cfg.statuses && this.cfg.statuses.length) {
 var opts = '';
 for (var si = 0; si ' + escapeHtml(sl) + '';
 }
 var setStatus = escapeHtml(this.t('setStatus', 'Set status'));
 sParts.push(
 ''
 );
 }
 modHtml = '';
 }
 }

 var mediaHtml = themed ? avatarHtml : thumbHtml;
 return (
 '' +
 leaderHtml +
 rankHtml +
 '' +
 mediaHtml +
 '

' +
 '

##### ' + escapeHtml(f.title) + '

' +
 statusBadge +
 descHtml +
 '

' +
 modHtml +
 ''
 );
 };

 /**
 * Detects whether a JSON response looks like a CSRF/token rejection.
 * Two cases:
 * 1. com_ajax-level rejection (Joomla's auto-token-check on POST): top
 * level message contains the JINVALID_TOKEN_NOTICE text and HTTP
 * status is typically 403.
 * 2. Plugin-level rejection from requireToken(): payload.error === "Invalid token".
 */
 Board.prototype.isTokenError = function (status, raw, payload) {
 if (status === 403) return true;
 var hay = '';
 try { hay = JSON.stringify(raw || {}) + '|' + JSON.stringify(payload || {}); } catch (e) {}
 return /invalid (security )?token/i.test(hay);
 };

 /**
 * Fetch a fresh CSRF token from the server. Updates this.cfg.csrfToken so
 * the next call (and any future calls in this Board's lifetime) use the
 * correct one. Cache-bust with a timestamp so no upstream CDN caches the
 * response.
 */
 Board.prototype.fetchFreshToken = function () {
 var self = this;
 var url = this.cfg.ajaxUrl + '&task=token&_=' + Date.now();
 return fetch(url, {
 method: 'GET',
 credentials: 'same-origin',
 headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache' }
 })
 .then(function (r) { return r.json(); })
 .then(function (data) {
 var p = self.unwrapAjax(data);
 if (p && p.ok === true && typeof p.token === 'string' && p.token.length >= 16) {
 self.cfg.csrfToken = p.token;
 return p.token;
 }
 throw new Error('No fresh token in response');
 });
 };

 /**
 * Generic POST-with-token helper. Builds FormData from a plain object,
 * adds the current token, posts. On 403 / token-error response, fetches a
 * fresh token and retries exactly once.
 *
 * @param {string} task The task name (appended to URL and body)
 * @param {object} fields Key-value form fields (no token)
 * @return {Promise

## Changelog

---

v3.2.0
 Breaking 15/07/2026

Added

- **Roadmap view.** New "Board view" setting on the Display tab: "List (sorted
- **"Roadmap empty columns" setting** (Display tab): hide status columns that
- **36 new frontend language keys** in all six languages (en-GB, nl-NL, de-DE,

Changed

- **Frontend JavaScript is now fully localized (closes the YC.6 JS i18n gap).**
- In the roadmap view, item cards no longer show the status badge - the column

Fixed

- Removed a duplicate `ERR_NO_BOARD` language key that shipped in all six
- Removed a duplicate `pad2()` helper definition in the frontend JS.

v3.1.3
 Breaking 15/07/2026

Changed

- **"Board ID mode" is now "Board scope"**, and its options describe the result
- **Board scope now appears above Board ID** in the Content tab. It is the
- **Board ID help text rewritten.** It previously recommended mapping the field
- **Vote icon moved from the Voting tab to the Display tab**, next to Card style

v3.1.2
 Breaking 03/07/2026

Added

- **Board ID mode: "Auto (current page / item)".** A new option on the element.

Changed

- Default is still Manual, so existing boards are unaffected. Auto is opt-in.

v3.1.1
 Breaking 03/07/2026

Added

- **Dynamic Board ID.** The Board ID field is now dynamic-content mappable. Drop

Changed

- Board ID sanitisation now slugifies: any run of disallowed characters becomes

v3.1.0
 Breaking 30/06/2026

Added

- **Three design themes**, selectable per board from the element's **Theme**
- **Leaderboard Arena** - light cream surface, coral accent, hard offset
- **Neon Stage** - dark, volt-lime accent, vote button that fills and glows
- **Retro Pixel Arcade** - dark purple, 8-bit type, thick pixel borders, hard
- Per-item furniture for these themes: a rank number (Arena), an avatar that
- **Self-hosted web fonts** under `media/plg_system_yc_upvote_overlord/fonts/`

Changed

- Fully backward-compatible: the default theme is unchanged; existing boards keep
- Next stages: v3.2.0 Roadmap view (status columns); v3.3.0 Comments.

v3.0.0
 Breaking 26/06/2026

Added

- **Status workflow.** Every item now has a status: Open (default), Planned,
- **Backend dashboard module (`mod_yc_upvote_overlord`).** A Joomla
- New AJAX endpoints on the plugin: `moderate_status` (frontend moderator) and
- `status` column on `#__yc_upvote_overlord_features` (added on install and via

Changed

- Fully backward-compatible: existing items default to status Open.
- Next stages: v3.1.0 Roadmap view (status columns); v3.2.0 Comments.

v2.2.3
 26/06/2026

Fixed

- **"Unexpected token ... is not valid JSON" on the board.** When another

v2.2.2
 26/06/2026

Changed

- **YC conformance pass** (no functional changes). Brought the package to the
- The element icon is referenced via `${url:images/icon.svg}` rather than an
- Local CSS/JS assets are inlined via `file_get_contents()` on `__DIR__`-relative

v2.2.1
 10/06/2026

Fixed

- **"Unknown column 'image_url'" error on the board (list/vote).** On some
- **Element settings: "Messages" and "Advanced" tabs were unclickable.** With

v2.2.0
 10/06/2026

Added

- **Maker-defined candidates.** A new "Candidates" tab lets you pre-populate the
- **Closed-round outcome reveal.** A new "Outcome" tab controls what appears once
- Candidate and decision images render as thumbnails in the list and outcome block.

Changed

- The features table gains `image_url` and `seed_key` columns plus a unique

Fixed

- Two-column layout wrapper was reusing the `.yc-fb-body` class already used by
