Index

BoardController

MMO\admin\controllers · 1 symbol · generated 5 Oct 2026

class

BoardController

BoardController.php:40

The kanban boards a release follows: each one a <mmo-kanban>'s data, and its tickets' discussions, kept in a JSON file of its own, {folder}/{board}.json (KanbanBoard for the file). {MMO\admin}/templates/board.phtml shows one; a release checklist (ChecklistController) follows one, and labels its verified tickets.

class MMO\admin\controllers\BoardController extends MMO\core\classes\aDataController implements MMO\core\interfaces\iRoutable
Namespace
MMO\admin\controllers
Extends
aDataController
Implements
iRoutable
The page fetches `api/boards/{board}.json` and sends the whole board back on each change, with the `version` it was given: a board saved meanwhile from another tab answers 409, with the board as it is now, rather than losing what the other tab wrote. A ticket's comments are saved on their own routes, into the same file: they are not part of the board's data, which only counts them (`comments` on each ticket). A ticket's description may hold AI prompts: ```` ```claude ````, ```` ```chatgpt ````... blocks (mmo.client's `KanbanPrompts`). `api/boards/{board}/prompts.json` lists them, with their tickets, for an agent to treat them (`?agent=claude`, `&status=todo`, `&done=1` for a done ticket's too); its `markdown` is the same as a brief to read. The agent reports back on the ticket's discussion, and moves the ticket by saving the board. Declared like any controller, with where the files are (mmo.client is needed: the page and the prompts are its): "MMO\\admin\\controllers\\BoardController": {"folder": "{root}/boards", "scope": "admin"} A `folder` of null switches the boards off: every route answers 404 (an environment without them, production). > WARNING: whoever reaches these routes writes the boards. Give a [`$scope`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/controllers/BoardController.md#boardcontroller-scope) on any application served beyond > localhost; [`$anonymous`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/controllers/BoardController.md#boardcontroller-anonymous) opens them to whoever calls (mmo.oauth): only on a development machine, for the agents.
curl http://localhost/my.oviepro/api/boards/my.oviepro-deploy.json curl 'http://localhost/my.oviepro/api/boards/my.oviepro-deploy/prompts.json?agent=claude' curl -X PUT -H 'Content-Type: application/json' -d '{"version": 0, "data": {"tickets": []}}' http://localhost/my.oviepro/api/boards/acme.json

Constants1

NAME

a board's name: what its file is called, nothing that leaves the folder

const NAME = '/^[a-z0-9][a-z0-9._-]{0,99}$/i'

Properties4

anonymous

bool $anonymous = false
Type
bool

folder

?string $folder = '{root}/boards'
Type
null | string

Methods17

answer($route, $b)

BoardController.php:231
protected function answer(MMO\core\server\Route $route, MMO\admin\helpers\KanbanBoard $b): array
$routeRoute
$bKanbanBoard
returnsarraythe board as the page wants it: its tickets saying how many comments they have

comment($c)

BoardController.php:241
protected function comment(array $c): array
$carray
returnsarraya comment as <mmo-kanban> takes it: the viewer may change their own, and every one on a local tool

ConnectRoutes($routes, $config)

staticBoardController.php:57
static function ConnectRoutes(array &$routes, array $config): void
See
$anonymous: every route is mmo.oauth's anonymous when the configuration says so
$routesarray
$configarray
returnsvoid

edit($route, $action, $write)

BoardController.php:209

Run $action on the route's board, locked (the lock the release checklists take too): what it changes is written back when $write, unless it answers an error.

protected function edit(MMO\core\server\Route $route, callable $action, bool $write = false): array
$routeRoute
$actioncallable
$write = falsebool
returnsarraywhat $action answers

guard($route)

BoardController.php:254
protected function guard(MMO\core\server\Route $route): ?array
$routeRoute
returnsnull | arraythe answer refusing the request: no boards here, or a client without $scope; null when it may

routeComment($route)

BoardController.php:144
#[MMO\core\attributes\APIRoute('POST|api/boards/{board}/{ticket}/comments.json', 'Comment on a ticket: {body, parent}', mediaTypes: ['json'])] function routeComment(MMO\core\server\Route $route): array
$routeRoutea JSON body {body, parent}: markdown, and the id of the comment it answers (null for none)
returnsarray{comment}: the new comment

routeComments($route)

BoardController.php:135
#[MMO\core\attributes\APIRoute('GET|api/boards/{board}/{ticket}/comments.json', 'A ticket\'s discussion', mediaTypes: ['json'])] function routeComments(MMO\core\server\Route $route): array
$routeRoute
returnsarray{comments}: the ticket's comments, oldest first

routeDeleteComment($route)

BoardController.php:179
#[MMO\core\attributes\APIRoute('DELETE|api/boards/{board}/{ticket}/comments/{comment}.json', 'Delete a comment', mediaTypes: ['json'])] function routeDeleteComment(MMO\core\server\Route $route): array
$routeRoute
returnsarray{}

routeList($route)

BoardController.php:69
#[MMO\core\attributes\APIRoute('GET|api/boards.json', 'The kanban boards', mediaTypes: ['json'])] function routeList(MMO\core\server\Route $route): array
$routeRoute
returnsarray{boards: [{name, title, version, updatedAt, tickets}]}, by name

routeLoad($route)

BoardController.php:89
#[MMO\core\attributes\APIRoute('GET|api/boards/{board}.json', 'A board\'s data', mediaTypes: ['json'])] function routeLoad(MMO\core\server\Route $route): array
$routeRoute
returnsarray{board, title, version, updatedAt, data}: an unknown board is an empty one, version 0

routePrompts($route)

BoardController.php:124
#[MMO\core\attributes\APIRoute('GET|api/boards/{board}/prompts.json', 'The AI prompts of a board\'s tickets: ?agent=claude&status=todo&done=1', mediaTypes: ['json'])] function routePrompts(MMO\core\server\Route $route): array
$routeRouteits query: agent, status, done (mmo.client's KanbanPrompts::Answer)
returnsarray{board, agents, prompts: [{agent, info, prompt, line, ticket}], markdown}

routeSave($route)

BoardController.php:98
#[MMO\core\attributes\APIRoute('PUT|api/boards/{board}.json', 'Save a board\'s data: {version, data}', mediaTypes: ['json'])] function routeSave(MMO\core\server\Route $route): array
$routeRoutea JSON body {version, data}: the version the page was given, and the board's data now
returnsarrayas routeLoad; 409 with the board as it is when it was saved meanwhile

routeUpdateComment($route)

BoardController.php:160
#[MMO\core\attributes\APIRoute('PUT|api/boards/{board}/{ticket}/comments/{comment}.json', 'Change a comment: {body}', mediaTypes: ['json'])] function routeUpdateComment(MMO\core\server\Route $route): array
$routeRoutea JSON body {body}
returnsarray{comment}: the comment as it is now

text($route)

BoardController.php:247
protected function text(MMO\core\server\Route $route): ?string
$routeRoute
returnsnull | stringthe request's body: the markdown sent, null when there is none
Inherited members (62)
ConstantsDeclared byDescription
ALERTiLogLevelAlert: Action must be taken immediately.
ALLiLogLevelMask for all
ALL_PLUSiLogLeveldebug everything
BACKTRACEiLogLevel
COLORSaDataControllerNamed color palettes for chart/report endpoints, e.g.
CRITICALiLogLevelCritical: Critical conditions.
DEBUGiLogLevelDebug: Detailed debug information.
DEEPiLogLeveldebug everything with arguments
EMERGENCYiLogLevelEmergency: System is unusable.
ERRORiLogLevelError: Runtime errors that do not require immediate action but should typically be logged and monitored.
ERROR_PLUSiLogLevelMask for all error
FATALiLogLevelMask for all fatal error
INFOiLogLevelInfo: Interesting events.
INFO_PLUSiLogLevelMask for all errors/warning/notices/info
MEMORYiLogLevelinclude backtrace object
NOTICEiLogLevelNotice: Normal but significant events.
NOTICE_PLUSiLogLevelMask for all errors/warning/notices/info
TRACEiLogLevelinclude backtrace as origins
WARNINGiLogLevelWarning: Exceptional occurrences that are not errors.
WARNING_PLUSiLogLevelMask for all errors and warnings
PropertiesDeclared byDescription
repositoriesaDataController
verbosityaModule
MethodsDeclared byDescription
alert($message, $context)aModuleAction must be taken immediately.
backtraces($message, $context)aModuleForce-log a message at INFO level together with a full stack trace/memory snapshot (iLogLevel::BACKTRACE), bypassing the verbosity mask entirely (any non-zero verbosity is enough to trigger it).
Configure($config)aModuleMerge $config into this module's configuration in the shared Configuration.
critical($message, $context)aModuleCritical conditions. No-op if CRITICAL is currently filtered out by the verbosity mask.
debug($message, $context)aModuleDetailed debug information.
debugName()aModuleHuman-readable identifier used as the default {origin} in this instance's log messages.
DefaultConfig()aModuleThe config a module starts from before anything loaded via Configuration is merged in.
emergency($message, $context)aModuleSystem is unusable. No-op if EMERGENCY is currently filtered out by the verbosity mask.
error($message, $context)aModuleRuntime errors that do not require immediate action but should typically be logged and monitored.
ExtractRoutes($config)aControllerExtract all the APIRoute from this class, regardless of the fact they are registered in the application
getMainRepository()aDataController
getRepositories()aDataController
getRepository($repo)aDataController
getVerbosity()aModule
HasRoutes()aControllerTells if any APIRoute are registered in the application
info($message, $context)aModuleInteresting events. No-op if INFO is currently filtered out by the verbosity mask.
Instantiate($config)aModule(Re)create the shared instance immediately, configured with $config merged into the class's existing config.
IsShared()aModule
Link($method, $data)aControllerBuild a Link (URL + label/icon/description/title, taken from the route's APIRoute metadata) for a registered route.
log($level, $message, $context)aModuleLogs with an arbitrary level.
notice($message, $context)aModuleNormal but significant events.
offsetExists($key)aModule\ArrayAccess::offsetExists -- tells whether $key is a declared property at all, regardless of whether it is currently readable/writable through array access.
offsetGet($key)aModule\ArrayAccess::offsetGet -- reads a property directly, unless it has a #[Serialize(serialize: 'methodName')] attribute, in which case that zero-argument method is called instead (e.g.
offsetSet($key, $value)aModule\ArrayAccess::offsetSet -- sets a property directly, unless it has a #[Serialize(unserialize: 'methodName')] attribute, in which case that method is called with $value instead (e.g.
offsetUnset($key)aModule\ArrayAccess::offsetUnset -- equivalent to $module[$key] = null.
onRoute($route)aDataControllerSame as aController::onRoute, plus forcing the response to JSON (Response::Json) for every route this controller claims.
Route($method)aControllerGet a registered APIRoute WARNING: if this class was not registered in the application, it will return null.
Routes()aControllerGet a list of registered APIRoutes (from this class) in the application WARNING: if this class was not registered in the application, it will return an empty array.
serialize($path, $serializer)aModule\MMO\core\interfaces\iSerializable::serialize -- exports every property (via
setRepositories($repositories)aDataControllerRegister the repositories this controller can query, keyed by name (e.g.
setVerbosity($verb)aModuleSet the current verbosity mask, replacing whatever it was before.
Shared()aModuleGet the shared instance, creating (and configuring) it on first call.
testVerbosity($level)aModuleTest whether a given level (or set of levels) is currently enabled by this instance's verbosity mask.
traitUnshare($serialize)aModuleRemove the shared instance, so the next Shared() call creates a fresh one.
unserialize($data, $path, $instances)aModule\MMO\core\interfaces\iSerializable::unserialize -- merges $data onto DefaultConfig, runs it through the (possibly overridden) constructor, then applies each resulting value to its property (via
Unshare($serialize)aModuleLike tSingleton::Unshare, but also disconnects this module's instance from every event it was listening to (via Event::DisconnectListeners) before dropping it.
Url($method, $data)aControllerBuild the URL for a registered route, filling in any {placeholder} from $data.
warning($message, $context)aModuleExceptional occurrences that are not errors.
__construct($config)aModuleprotected constructor, so it is not possible to do new Module(...) called by the serialization process just before being injected with data.
__wakeup()aControllerCalled automatically right after the controller's shared instance is built (aModule::unserialize): registers onRoute as a Route listener with a low priority (-10), so it…