Skip to content

Commit e86532e

Browse files
Merge pull request #8613 from christianbeeznest/migration-foros-02
Forum: Restore basic forum settings
2 parents c170a46 + c563150 commit e86532e

6 files changed

Lines changed: 178 additions & 33 deletions

File tree

assets/vue/views/forum/ForumList.vue

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
<div class="flex flex-col gap-3 border-b border-gray-20 bg-gray-15 p-4 md:flex-row md:items-center md:justify-between">
9494
<div class="flex items-center gap-2">
9595
<BaseIcon
96-
icon="folder-generic"
96+
icon="folder-open"
9797
size="normal"
9898
/>
9999
<h2 class="text-lg font-semibold text-gray-90">{{ t("General") }}</h2>
@@ -147,6 +147,15 @@
147147
>
148148
{{ t("Hidden") }}
149149
</span>
150+
<BaseButton
151+
v-if="canFoldCategories"
152+
:label="isCategoryFolded(category) ? t('Show') : t('Hide')"
153+
:icon="isCategoryFolded(category) ? 'unfold' : 'fold'"
154+
only-icon
155+
size="small"
156+
type="plain"
157+
@click="toggleCategoryFold(category)"
158+
/>
150159
</div>
151160
<p
152161
v-if="category.catComment"
@@ -221,7 +230,10 @@
221230
</div>
222231
</div>
223232

224-
<div class="p-4">
233+
<div
234+
v-show="!isCategoryFolded(category)"
235+
class="p-4"
236+
>
225237
<ForumCardList
226238
v-if="category.forums.length"
227239
:can-manage="isAllowedToEdit"
@@ -442,13 +454,11 @@ import ForumCardList from "./ForumCardList.vue"
442454
import { useIsAllowedToEdit } from "../../composables/userPermissions"
443455
import { useNotification } from "../../composables/notification"
444456
import { useConfirmation } from "../../composables/useConfirmation"
445-
import { usePlatformConfig } from "../../store/platformConfig"
446457
447458
const { t } = useI18n()
448459
const route = useRoute()
449460
const notifications = useNotification()
450461
const { requireConfirmation } = useConfirmation()
451-
const platformConfigStore = usePlatformConfig()
452462
const { isAllowedToEdit } = useIsAllowedToEdit({ coach: true, sessionCoach: true })
453463
454464
const isLoading = ref(false)
@@ -459,8 +469,15 @@ const forumFormSubmitted = ref(false)
459469
const isCategoryDialogVisible = ref(false)
460470
const isForumDialogVisible = ref(false)
461471
const csrfToken = ref("")
472+
const forumSettings = ref({
473+
defaultForumView: "flat",
474+
forumFoldCategories: false,
475+
allowForumPostRevisions: false,
476+
hideForumPostRevisionLanguage: false,
477+
})
462478
const categories = ref([])
463479
const forums = ref([])
480+
const foldedCategoryIds = ref(new Set())
464481
465482
const categoryForm = reactive({
466483
id: null,
@@ -500,10 +517,11 @@ const baseQuery = computed(() => ({
500517
gid: gid.value || null,
501518
}))
502519
const defaultForumView = computed(() => {
503-
const value = String(platformConfigStore.getSetting("forum.default_forum_view") || "flat")
520+
const value = String(forumSettings.value.defaultForumView || "flat")
504521
505522
return ["flat", "threaded", "nested"].includes(value) ? value : "flat"
506523
})
524+
const canFoldCategories = computed(() => Boolean(forumSettings.value.forumFoldCategories))
507525
508526
const defaultViewOptions = computed(() => [
509527
{ label: t("Flat"), value: "flat" },
@@ -576,6 +594,27 @@ function isForumVisible(forum) {
576594
return true === forum.forumVisible || 1 === forum.forumVisible || "1" === String(forum.forumVisible)
577595
}
578596
597+
598+
function isCategoryFolded(category) {
599+
return canFoldCategories.value && foldedCategoryIds.value.has(Number(category?.iid || 0))
600+
}
601+
602+
function toggleCategoryFold(category) {
603+
const categoryId = Number(category?.iid || 0)
604+
if (!categoryId) {
605+
return
606+
}
607+
608+
const nextValue = new Set(foldedCategoryIds.value)
609+
if (nextValue.has(categoryId)) {
610+
nextValue.delete(categoryId)
611+
} else {
612+
nextValue.add(categoryId)
613+
}
614+
615+
foldedCategoryIds.value = nextValue
616+
}
617+
579618
function getCategoryIdFromForum(forum) {
580619
if (!forum?.forumCategory) {
581620
return 0
@@ -692,6 +731,10 @@ function openEditForumDialog(forum) {
692731
async function loadToken() {
693732
const response = await forumService.getActionToken()
694733
csrfToken.value = response.token || ""
734+
forumSettings.value = {
735+
...forumSettings.value,
736+
...(response.settings || {}),
737+
}
695738
}
696739
697740
async function loadForums() {

assets/vue/views/forum/ForumPostList.vue

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -137,30 +137,38 @@
137137
:class="['rounded-xl border border-gray-20 bg-white p-4 shadow-sm', getPostLevelClass(post)]"
138138
>
139139
<div class="mb-3 flex flex-col gap-3 border-b border-gray-20 pb-3 md:flex-row md:items-start md:justify-between">
140-
<div class="min-w-0">
141-
<h2 class="truncate text-base font-semibold text-gray-90">{{ post.title }}</h2>
142-
<div class="mt-1 flex flex-wrap gap-2 text-xs text-gray-500">
143-
<span>{{ post.posterFullName || t("Unknown user") }}</span>
144-
<span v-if="post.postDate">{{ formatDate(post.postDate) }}</span>
145-
<span v-if="!isPostVisible(post)">{{ t("Hidden") }}</span>
146-
<span
147-
v-if="showModerationStatus(post)"
148-
:class="getModerationBadgeClass(post)"
149-
>
150-
{{ t(post.statusLabel || getModerationStatusLabel(post)) }}
151-
</span>
152-
<span
153-
v-if="post.revisionRequested"
154-
class="rounded-full bg-blue-100 px-2 py-0.5 text-blue-700"
155-
>
156-
{{ t('Revision requested') }}
157-
</span>
158-
<span
159-
v-if="post.revisionLanguage"
160-
class="rounded-full bg-gray-100 px-2 py-0.5 text-gray-700"
161-
>
162-
{{ t('Revision') }}
163-
</span>
140+
<div class="flex min-w-0 gap-3">
141+
<img
142+
v-if="post.showPosterAvatar && post.posterAvatarUrl"
143+
:alt="post.posterFullName || t('Unknown user')"
144+
:src="post.posterAvatarUrl"
145+
class="h-10 w-10 shrink-0 rounded-full object-cover"
146+
/>
147+
<div class="min-w-0">
148+
<h2 class="truncate text-base font-semibold text-gray-90">{{ post.title }}</h2>
149+
<div class="mt-1 flex flex-wrap gap-2 text-xs text-gray-500">
150+
<span>{{ post.posterFullName || t("Unknown user") }}</span>
151+
<span v-if="post.postDate">{{ formatDate(post.postDate) }}</span>
152+
<span v-if="!isPostVisible(post)">{{ t("Hidden") }}</span>
153+
<span
154+
v-if="showModerationStatus(post)"
155+
:class="getModerationBadgeClass(post)"
156+
>
157+
{{ t(post.statusLabel || getModerationStatusLabel(post)) }}
158+
</span>
159+
<span
160+
v-if="post.revisionRequested"
161+
class="rounded-full bg-blue-100 px-2 py-0.5 text-blue-700"
162+
>
163+
{{ t('Revision requested') }}
164+
</span>
165+
<span
166+
v-if="post.revisionLanguage"
167+
class="rounded-full bg-gray-100 px-2 py-0.5 text-gray-700"
168+
>
169+
{{ t('Revision') }}
170+
</span>
171+
</div>
164172
</div>
165173
</div>
166174

src/CoreBundle/ApiResource/Forum/ForumActionToken.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ final class ForumActionToken
3333
#[Groups(['forum_action_token:read'])]
3434
public string $token = '';
3535

36+
/**
37+
* @var array<string, string|bool>
38+
*/
39+
#[Groups(['forum_action_token:read'])]
40+
public array $settings = [];
41+
3642
public function getId(): string
3743
{
3844
return $this->id;

src/CoreBundle/State/Forum/ForumActionTokenProvider.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use ApiPlatform\Metadata\Operation;
1010
use ApiPlatform\State\ProviderInterface;
1111
use Chamilo\CoreBundle\ApiResource\Forum\ForumActionToken;
12+
use Chamilo\CoreBundle\Settings\SettingsManager;
1213
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
1314

1415
/**
@@ -20,6 +21,7 @@
2021

2122
public function __construct(
2223
private CsrfTokenManagerInterface $csrfTokenManager,
24+
private SettingsManager $settingsManager,
2325
) {}
2426

2527
/**
@@ -30,7 +32,31 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
3032
{
3133
$actionToken = new ForumActionToken();
3234
$actionToken->token = $this->csrfTokenManager->getToken(self::FORUM_ACTION_TOKEN_INTENTION)->getValue();
35+
$actionToken->settings = [
36+
'defaultForumView' => $this->getDefaultForumView(),
37+
'forumFoldCategories' => $this->isTruthySetting(
38+
$this->settingsManager->getSetting('forum.forum_fold_categories', true),
39+
),
40+
'allowForumPostRevisions' => $this->isTruthySetting(
41+
$this->settingsManager->getSetting('forum.allow_forum_post_revisions', true),
42+
),
43+
'hideForumPostRevisionLanguage' => $this->isTruthySetting(
44+
$this->settingsManager->getSetting('forum.hide_forum_post_revision_language', true),
45+
),
46+
];
3347

3448
return $actionToken;
3549
}
50+
51+
private function getDefaultForumView(): string
52+
{
53+
$defaultView = (string) ($this->settingsManager->getSetting('forum.default_forum_view', true) ?? 'flat');
54+
55+
return \in_array($defaultView, ['flat', 'threaded', 'nested'], true) ? $defaultView : 'flat';
56+
}
57+
58+
private function isTruthySetting(mixed $value): bool
59+
{
60+
return \in_array(strtolower(trim((string) $value)), ['1', 'true', 'yes', 'on'], true);
61+
}
3662
}

src/CoreBundle/State/Forum/ForumThreadPostsStateProvider.php

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Chamilo\CoreBundle\Entity\ResourceNode;
1414
use Chamilo\CoreBundle\Entity\Session;
1515
use Chamilo\CoreBundle\Entity\User;
16+
use Chamilo\CoreBundle\Settings\SettingsManager;
1617
use Chamilo\CourseBundle\Entity\CForum;
1718
use Chamilo\CourseBundle\Entity\CForumNotification;
1819
use Chamilo\CourseBundle\Entity\CForumPost;
@@ -37,12 +38,16 @@ final class ForumThreadPostsStateProvider implements ProviderInterface
3738
{
3839
use ForumStateHelperTrait;
3940

41+
/** @var array<int, string> */
42+
private array $posterAvatarUrlByUserId = [];
43+
4044
public function __construct(
4145
private readonly RequestStack $requestStack,
4246
private readonly EntityManagerInterface $entityManager,
4347
private readonly CForumThreadRepository $threadRepository,
4448
private readonly CForumAttachmentRepository $attachmentRepository,
4549
private readonly Security $security,
50+
private readonly SettingsManager $settingsManager,
4651
) {}
4752

4853
/**
@@ -144,6 +149,8 @@ public function getThreadPosts(int $threadId, Request $request): array
144149

145150
$posts = $postsQueryBuilder->getQuery()->getResult();
146151

152+
$showPosterAvatar = $this->arePosterImagesAllowed($course);
153+
147154
return [
148155
'forum' => $this->serializeForum($forum),
149156
'thread' => $this->serializeThread(
@@ -157,7 +164,13 @@ public function getThreadPosts(int $threadId, Request $request): array
157164
'canReply' => $this->canReply($forum, $thread),
158165
'canManageThread' => $canManage,
159166
'posts' => array_map(
160-
fn (CForumPost $post): array => $this->serializePost($post, $forum, $thread, $canManage),
167+
fn (CForumPost $post): array => $this->serializePost(
168+
$post,
169+
$forum,
170+
$thread,
171+
$canManage,
172+
$showPosterAvatar,
173+
),
161174
$posts,
162175
),
163176
];
@@ -189,6 +202,22 @@ private function areForumPostNotificationsHidden(Course $course): bool
189202
return 1 === (int) api_get_course_setting('hide_forum_notifications', $course);
190203
}
191204

205+
private function arePosterImagesAllowed(Course $course): bool
206+
{
207+
if (!\function_exists('api_get_course_setting')) {
208+
return false;
209+
}
210+
211+
return 1 === (int) api_get_course_setting('allow_user_image_forum', $course);
212+
}
213+
214+
private function shouldHideForumPostRevisionLanguage(): bool
215+
{
216+
return $this->isTruthySetting(
217+
$this->settingsManager->getSetting('forum.hide_forum_post_revision_language', true),
218+
);
219+
}
220+
192221
private function isSubscribedToThread(Course $course, User $user, int $threadId): bool
193222
{
194223
return null !== $this->entityManager->getRepository(CForumNotification::class)->findOneBy([
@@ -254,8 +283,13 @@ private function serializeThread(
254283
/**
255284
* @return array<string, mixed>
256285
*/
257-
private function serializePost(CForumPost $post, CForum $forum, CForumThread $thread, bool $canManage): array
258-
{
286+
private function serializePost(
287+
CForumPost $post,
288+
CForum $forum,
289+
CForumThread $thread,
290+
bool $canManage,
291+
bool $showPosterAvatar,
292+
): array {
259293
$canEdit = $this->canEditPost($post, $forum, $thread, $canManage);
260294
$attachments = [];
261295

@@ -275,7 +309,8 @@ private function serializePost(CForumPost $post, CForum $forum, CForumThread $th
275309
$currentUser = $this->security->getUser();
276310
$isAuthor = $currentUser instanceof User && $post->getUser()->getId() === $currentUser->getId();
277311
$revisionRequested = $this->postNeedsRevision($post);
278-
$revisionLanguage = $this->getPostRevisionLanguage($post);
312+
$revisionLanguage = $this->shouldHideForumPostRevisionLanguage() ? '' : $this->getPostRevisionLanguage($post);
313+
$posterAvatarUrl = $showPosterAvatar ? $this->getPosterAvatarUrl($post->getUser()) : '';
279314

280315
return [
281316
'iid' => $post->getIid(),
@@ -287,6 +322,8 @@ private function serializePost(CForumPost $post, CForum $forum, CForumThread $th
287322
'status' => $status,
288323
'statusLabel' => $this->getPostStatusLabel($status),
289324
'posterFullName' => $post->getPosterFullName(),
325+
'posterAvatarUrl' => $posterAvatarUrl,
326+
'showPosterAvatar' => $showPosterAvatar && '' !== $posterAvatarUrl,
290327
'canEdit' => $canEdit,
291328
'canDelete' => $canEdit,
292329
'canApprove' => $canManage && CForumPost::STATUS_VALIDATED !== $status,
@@ -328,6 +365,29 @@ private function getPostRevisionLanguage(CForumPost $post): string
328365
return (string) ($this->getExtraFieldValue('forum_post', (int) $post->getIid(), 'revision_language') ?? '');
329366
}
330367

368+
private function getPosterAvatarUrl(?User $user): string
369+
{
370+
if (!$user instanceof User || !\function_exists('api_get_user_info')) {
371+
return '';
372+
}
373+
374+
$userId = (int) $user->getId();
375+
if (isset($this->posterAvatarUrlByUserId[$userId])) {
376+
return $this->posterAvatarUrlByUserId[$userId];
377+
}
378+
379+
$userInfo = api_get_user_info($userId, false, false, false, false, true);
380+
if (!\is_array($userInfo)) {
381+
$this->posterAvatarUrlByUserId[$userId] = '';
382+
383+
return '';
384+
}
385+
386+
$this->posterAvatarUrlByUserId[$userId] = (string) ($userInfo['avatar_small'] ?? $userInfo['avatar'] ?? '');
387+
388+
return $this->posterAvatarUrlByUserId[$userId];
389+
}
390+
331391
private function isReportAvailableForCurrentRequest(): bool
332392
{
333393
$request = $this->requestStack->getCurrentRequest();

src/CourseBundle/Settings/ForumCourseSettingsSchema.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public function buildSettings(AbstractSettingsBuilder $builder): void
1919
->setDefaults([
2020
'enabled' => 1,
2121
'allow_user_image_forum' => 1,
22+
'hide_forum_notifications' => 0,
2223
])
2324
;
2425
}
@@ -28,6 +29,7 @@ public function buildForm(FormBuilderInterface $builder): void
2829
$builder
2930
->add('enabled', YesNoNumericType::class)
3031
->add('allow_user_image_forum', YesNoNumericType::class)
32+
->add('hide_forum_notifications', YesNoNumericType::class)
3133
;
3234
}
3335
}

0 commit comments

Comments
 (0)