| @@ -70,8 +70,60 @@ | ||
| 70 | 70 | { |
| 71 | 71 | return ['published', 'unlisted']; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | + /** | |
| 75 | + * Row types that opt IN to comments through meta.enable_comments, mapped to the value | |
| 76 | + * assumed when the key is absent. | |
| 77 | + * | |
| 78 | + * A feed post uses the opposite convention - meta.comments_disabled, absent meaning on - | |
| 79 | + * so it is deliberately not listed here and falls through to the permissive default. | |
| 80 | + * | |
| 81 | + * The fallbacks match each model's getDefaultMeta(): a lesson written before the | |
| 82 | + * setting existed keeps its thread, a page does not. Guessing one value for both | |
| 83 | + * would silently switch off every legacy lesson discussion. | |
| 84 | + * | |
| 85 | + * @return array<string, string> | |
| 86 | + */ | |
| 87 | + public static function getOptInCommentTypes() | |
| 88 | + { | |
| 89 | + return apply_filters('fluent_community/opt_in_comment_types', [ | |
| 90 | + 'course_lesson' => 'yes', | |
| 91 | + 'space_page' => 'no', | |
| 92 | + ]); | |
| 93 | + } | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * Whether a row accepts comments at all, by its own settings. | |
| 97 | + * | |
| 98 | + * This is the setting check only - it says nothing about who the current user is. | |
| 99 | + * Space membership and the course level kill switch are separate, in | |
| 100 | + * CommentsController::verifySpacePermission(). | |
| 101 | + * | |
| 102 | + * Both the read and the write path go through here so they cannot disagree. They used | |
| 103 | + * to: the write path only ever read meta.comments_disabled, which pages and lessons | |
| 104 | + * do not set, so a POST landed a comment on a page whose thread the UI was hiding. | |
| 105 | + * | |
| 106 | + * @param \FluentCommunity\App\Models\Feed $feed | |
| 107 | + * @return bool | |
| 108 | + */ | |
| 109 | + public static function commentsEnabled($feed) | |
| 110 | + { | |
| 111 | + $meta = $feed->meta; | |
| 112 | + | |
| 113 | + if (Arr::get($meta, 'comments_disabled') === 'yes') { | |
| 114 | + return false; | |
| 115 | + } | |
| 116 | + | |
| 117 | + $optIn = self::getOptInCommentTypes(); | |
| 118 | + | |
| 119 | + if (isset($optIn[$feed->type])) { | |
| 120 | + return Arr::get($meta, 'enable_comments', $optIn[$feed->type]) === 'yes'; | |
| 121 | + } | |
| 122 | + | |
| 123 | + return true; | |
| 124 | + } | |
| 125 | + | |
| 74 | 126 | public static function getLastFeedId() |
| 75 | 127 | { |
| 76 | 128 | $lastItem = Feed::where('status', 'published') |
| 77 | 129 | ->byUserAccess(get_current_user_id()) |
| @@ -559,9 +611,9 @@ | ||
| 559 | 611 | } |
| 560 | 612 | |
| 561 | 613 | public static function sanitizeAndValidateData($data) |
| 562 | 614 | { |
| 563 | - $message = CustomSanitizer::unslashMarkdown(trim(Arr::get($data, 'message'))); | |
| 615 | + $message = CustomSanitizer::unslashMarkdown(trim((string) Arr::get($data, 'message', ''))); | |
| 564 | 616 | |
| 565 | 617 | // Decode HTML entities and strip all whitespace for validation |
| 566 | 618 | $messageForValidation = html_entity_decode($message, ENT_QUOTES | ENT_HTML5, 'UTF-8'); |
| 567 | 619 | $messageForValidation = preg_replace('/\s+/u', '', $messageForValidation); |
| @@ -1075,8 +1127,24 @@ | ||
| 1075 | 1127 | } |
| 1076 | 1128 | |
| 1077 | 1129 | $spaceSettings = $feed->space ? $feed->space->settings : []; |
| 1078 | 1130 | $feed->default_comment_sort_by = Arr::get($spaceSettings, 'default_comment_sort_by', ''); |
| 1131 | + | |
| 1132 | + // Feed::withPublicRelations() eager-loads the space with its raw settings, and | |
| 1133 | + // those settings carry links scoped to logged-in members or to specific | |
| 1134 | + // memberships. BaseSpace::formatSpaceData() filters them for the space | |
| 1135 | + // endpoints; nothing filtered them here, so every feed response handed all of | |
| 1136 | + // a space's links - titles and URLs - to any caller, anonymous included. | |
| 1137 | + if ($feed->space && Arr::get($spaceSettings, 'links')) { | |
| 1138 | + $currentUser = Helper::getCurrentUser(); | |
| 1139 | + | |
| 1140 | + $spaceSettings['links'] = Helper::filterAccessibleLinks( | |
| 1141 | + Arr::get($spaceSettings, 'links', []), | |
| 1142 | + $currentUser ? $currentUser : null | |
| 1143 | + ); | |
| 1144 | + | |
| 1145 | + $feed->space->settings = $spaceSettings; | |
| 1146 | + } | |
| 1079 | 1147 | |
| 1080 | 1148 | self::setCurrentRelatedUserId($feed->user_id); |
| 1081 | 1149 | |
| 1082 | 1150 | return apply_filters('fluent_community/rendering_feed_model', $feed, $config); |