LessonThread
Lesson Q&A forum β thread/reply payload validation + 1-level reply depth + action policy (Layer 3 Domain).
Statusβ
| Key | Value |
|---|---|
| Layer | domain |
| Tier | L1 |
| Status | wip |
| Version | 0.9.0 |
| Price | Free (free) |
| Category | Community |
Overviewβ
Overviewβ
LessonThread is multi-saas-kit's first Layer 3 Domain Plugin. It standardizes per-lesson Q&A forum thread/reply payload validation and action policy.
Reuses Annotation's AuthorRole enum for permission branching.
Core Componentsβ
ThreadPayloadValidator (Pure)β
Extracted from academy.how's LessonThreadController::store / storeReply.
Thread validation (validateThread):
- title 1~max(255), trim, empty rejected
- body 1~max(20_000)
- Multibyte-friendly (
mb_strlen)
Reply validation (validateReply):
- Thread
is_lockedrejected - body 1~max(20_000)
- parent_id from different thread β
parent_invalid - parent already a child reply β
reply_depth_exceeded(1-level only, configurable) - Staff reply auto-marked
is_teacher_reply=true - Empty/blank parent_id treated as top-level
ThreadActionPolicy (Pure)β
Extracted from academy's policies. Decides on role + ownership only (tenant/saas isolation is caller's responsibility).
| Action | Policy |
|---|---|
canCreateThread | Role required |
canUpdateThread / canDeleteThread | Owner OR staff |
canTogglePin / canToggleLock | Staff only by default (configurable) |
canCreateReply | Open = anyone, locked = staff |
canUpdateReply / canDeleteReply | Owner OR staff |
canToggleAccept | Staff always. Thread owner also if accept_staff_only=false |
Exceptionβ
InvalidThreadPayloadException β reason field for i18n.
Configurationβ
return [
'enabled' => env('PLG_LESSON_THREAD_ENABLED', true),
'limits' => [
'title_max' => 255,
'thread_body_max' => 20_000,
'reply_body_max' => 20_000,
],
'replies' => [
'max_depth' => 1,
'accept_staff_only' => true,
],
'admin_actions' => [
'pin_staff_only' => true,
'lock_staff_only' => true,
],
];
Usageβ
$validator = ThreadPayloadValidator::fromConfig(config('lesson-thread'));
$policy = ThreadActionPolicy::fromConfig(config('lesson-thread'));
if (! $policy->canCreateThread($role)) abort(403);
$normalized = $validator->validateThread($request->all(), $role);
Originβ
Extracted from academy.how's LessonThread + LessonThreadReply Model + Controller + Policies. Eloquent dependencies removed.
Dependenciesβ
- Annotation β reuses
AuthorRoleenum
Roadmap (Phase 3+)β
- Standard
Thread+ThreadReplyModel + Migration (opt-in) - Filament Resource
- RichEditorSanitizer integration
- Cross-link with Webbook plugin
Licenseβ
MIT
Dependenciesβ
Demosβ
- Platform κ΄λ¦¬μ ν¨λμμ λ©ν νμΈ π Login required
- μ¬μ© μμ (PHP)
π View on Plugin Store: store.codebase.how/plugins/lesson-thread