Social: Enhancements and Fixes for Social Network Features - refs BT#21101

pull/5502/head
christianbeeznst 6 months ago
parent 3387a6072d
commit b09d91d45a
  1. 10
      assets/css/app.scss
  2. 2
      assets/vue/components/message/MessageLayout.vue
  3. 2
      assets/vue/components/personalfile/Layout.vue
  4. 12
      assets/vue/components/social/MyGroupsCard.vue
  5. 9
      assets/vue/components/user/Layout.vue
  6. 2
      assets/vue/components/usergroup/Layout.vue
  7. 2
      assets/vue/components/userreluser/Layout.vue
  8. 38
      assets/vue/composables/sidebarMenu.js
  9. 24
      assets/vue/views/account/Home.vue
  10. 1
      assets/vue/views/message/MessageList.vue
  11. 47
      assets/vue/views/social/SocialLayout.vue
  12. 9
      assets/vue/views/social/SocialWall.vue
  13. 8
      assets/vue/views/userreluser/Invitations.vue
  14. 20
      src/CoreBundle/Controller/SocialController.php

@ -619,6 +619,16 @@ form .field {
display: none; display: none;
} }
.sub-item-indent {
padding-left: 20px;
}
.sub-item-indent.active {
background-color: #e0e0e0;
color: #333;
font-weight: bold;
}
//@import 'primevue-md-light-indigo/theme.css'; //@import 'primevue-md-light-indigo/theme.css';
//@import '~primevue/resources/primevue.min.css'; //@import '~primevue/resources/primevue.min.css';
//@import '~primeflex/primeflex.css'; //@import '~primeflex/primeflex.css';

@ -2,7 +2,6 @@
<div class="message-layout flex"> <div class="message-layout flex">
<div class="sidebar"> <div class="sidebar">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu />
</div> </div>
<div class="content flex-grow"> <div class="content flex-grow">
<router-view></router-view> <router-view></router-view>
@ -11,7 +10,6 @@
</template> </template>
<script setup> <script setup>
import UserProfileCard from "../social/UserProfileCard.vue" import UserProfileCard from "../social/UserProfileCard.vue"
import SocialSideMenu from "../social/SocialSideMenu.vue"
import { onMounted, provide } from "vue" import { onMounted, provide } from "vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"
import { useSecurityStore } from "../../store/securityStore" import { useSecurityStore } from "../../store/securityStore"

@ -2,7 +2,6 @@
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col"> <div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu />
</div> </div>
<div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6"> <div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6">
<h2 v-t="'My files'" class="mr-auto" /> <h2 v-t="'My files'" class="mr-auto" />
@ -13,7 +12,6 @@
</template> </template>
<script setup> <script setup>
import UserProfileCard from "../social/UserProfileCard.vue" import UserProfileCard from "../social/UserProfileCard.vue"
import SocialSideMenu from "../social/SocialSideMenu.vue"
import { onMounted, provide } from "vue" import { onMounted, provide } from "vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"

@ -69,14 +69,18 @@ function search() {
async function fetchGroups(userId) { async function fetchGroups(userId) {
try { try {
const response = await axios.get( `/social-network/groups/${userId}`) const response = await axios.get(`/social-network/groups/${userId}`)
groups.value = response.data.groups if (response.data) {
goToUrl.value = response.data.groups.find(group => group.go_to)?.go_to || '' groups.value = response.data.items
goToUrl.value = response.data.go_to
}
} catch (error) { } catch (error) {
console.error('Error fetching groups:', error) groups.value = []
goToUrl.value = ''
} }
} }
watchEffect(() => { watchEffect(() => {
if (user.value && user.value.id) { if (user.value && user.value.id) {
fetchGroups(user.value.id) fetchGroups(user.value.id)

@ -1,10 +1,9 @@
<template> <template>
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4 w-full">
<div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col"> <div class="flex flex-col w-full md:w-1/5">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu />
</div> </div>
<div class="md:basis-2/3 lg:basis-1/2 2xl:basis-4/6"> <div class="flex-grow w-full md:w-4/5">
<router-view /> <router-view />
</div> </div>
</div> </div>
@ -12,8 +11,6 @@
<script setup> <script setup>
import UserProfileCard from "../social/UserProfileCard.vue" import UserProfileCard from "../social/UserProfileCard.vue"
import SocialSideMenu from "../social/SocialSideMenu.vue"
import { onMounted, provide } from "vue" import { onMounted, provide } from "vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"

@ -3,7 +3,6 @@
<div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col"> <div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col">
<UserProfileCard v-if="!isLoading && !isGroup" /> <UserProfileCard v-if="!isLoading && !isGroup" />
<GroupInfoCard v-if="!isLoading && isGroup" /> <GroupInfoCard v-if="!isLoading && isGroup" />
<SocialSideMenu v-if="!isLoading && !isGroup" />
<SocialGroupMenu v-if="!isLoading && isGroup" /> <SocialGroupMenu v-if="!isLoading && isGroup" />
</div> </div>
<div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6"> <div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6">
@ -13,7 +12,6 @@
</template> </template>
<script setup> <script setup>
import UserProfileCard from "../social/UserProfileCard.vue" import UserProfileCard from "../social/UserProfileCard.vue"
import SocialSideMenu from "../social/SocialSideMenu.vue"
import { provide } from "vue" import { provide } from "vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"
import SocialGroupMenu from "../social/SocialGroupMenu.vue" import SocialGroupMenu from "../social/SocialGroupMenu.vue"

@ -2,7 +2,6 @@
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col"> <div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu />
</div> </div>
<div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6"> <div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6">
<router-view></router-view> <router-view></router-view>
@ -11,7 +10,6 @@
</template> </template>
<script setup> <script setup>
import UserProfileCard from "../social/UserProfileCard.vue" import UserProfileCard from "../social/UserProfileCard.vue"
import SocialSideMenu from "../social/SocialSideMenu.vue"
import { onMounted, provide } from "vue" import { onMounted, provide } from "vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"

@ -1,19 +1,31 @@
import { useI18n } from "vue-i18n" import { useI18n } from "vue-i18n"
import { computed } from "vue" import { computed, ref } from "vue"
import { useSecurityStore } from "../store/securityStore" import { useSecurityStore } from "../store/securityStore"
import { usePlatformConfig } from "../store/platformConfig" import { usePlatformConfig } from "../store/platformConfig"
import { useEnrolledStore } from "../store/enrolledStore" import { useEnrolledStore } from "../store/enrolledStore"
import { useRouter } from "vue-router" import { useRoute, useRouter } from "vue-router"
import { useSocialMenuItems } from "./useSocialMenuItems"
export function useSidebarMenu() { export function useSidebarMenu() {
const { t } = useI18n() const { t } = useI18n()
const router = useRouter() const router = useRouter()
const route = useRoute()
const securityStore = useSecurityStore() const securityStore = useSecurityStore()
const platformConfigStore = usePlatformConfig() const platformConfigStore = usePlatformConfig()
const enrolledStore = useEnrolledStore() const enrolledStore = useEnrolledStore()
const { items: socialItems } = useSocialMenuItems();
const showTabsSetting = platformConfigStore.getSetting("platform.show_tabs") const showTabsSetting = platformConfigStore.getSetting("platform.show_tabs")
const showCatalogue = platformConfigStore.getSetting("platform.catalog_show_courses_sessions") const showCatalogue = platformConfigStore.getSetting("platform.catalog_show_courses_sessions")
const isActive = (item) => {
if (item.route) {
return route.path === item.route || (item.route.name && route.name === item.route.name)
} else if (item.items) {
return item.items.some(subItem => isActive(subItem))
}
return false
};
const menuItemsBeforeMyCourse = computed(() => { const menuItemsBeforeMyCourse = computed(() => {
const items = [] const items = []
@ -118,11 +130,29 @@ export function useSidebarMenu() {
} }
if (showTabsSetting.indexOf("social") > -1) { if (showTabsSetting.indexOf("social") > -1) {
const styledSocialItems = socialItems.value.map(item => {
const newItem = {
...item,
class: `sub-item-indent${isActive(item) ? ' active' : ''}`
};
if (newItem.isLink && newItem.route) {
newItem.command = () => window.location.href = newItem.route
} else if (newItem.route) {
newItem.command = () => router.push(newItem.route)
} else if (newItem.link) {
newItem.command = () => window.location.href = newItem.link
}
return newItem
});
items.push({ items.push({
icon: "mdi mdi-sitemap-outline", icon: "mdi mdi-sitemap-outline",
label: t("Social network"), label: t("Social network"),
command: () => router.push({ name: "SocialWall" }), items: styledSocialItems,
}) expanded: isActive({ items: styledSocialItems })
});
} }
if (platformConfigStore.plugins?.bbb?.show_global_conference_link) { if (platformConfigStore.plugins?.bbb?.show_global_conference_link) {

@ -2,38 +2,18 @@
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col"> <div class="md:basis-1/3 lg:basis-1/4 2xl:basis-1/6 flex flex-col">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu />
</div> </div>
<div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6"> <div class="md:basis-2/3 lg:basis-3/4 2xl:basis-5/6">
<div id="account-home"> <SocialWall :hidePostForm="true" />
<div class="flex mb-4">
<Avatar
:image="user.illustrationUrl + '?w=80&h=80&fit=crop'"
class="flex-none mr-2"
shape="circle"
size="large"
/>
<div class="flex-1">
<p class="text-body-1">
{{ user.fullName }}
</p>
<p class="text-caption">
{{ user.username }}
</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, provide } from "vue" import { onMounted, provide } from "vue"
import Avatar from "primevue/avatar"
import SocialSideMenu from "../../components/social/SocialSideMenu.vue";
import UserProfileCard from "../../components/social/UserProfileCard.vue" import UserProfileCard from "../../components/social/UserProfileCard.vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"
import SocialWall from "../social/SocialWall.vue"
const { user, isCurrentUser, groupInfo, isGroup, loadUser } = useSocialInfo(); const { user, isCurrentUser, groupInfo, isGroup, loadUser } = useSocialInfo();

@ -179,7 +179,6 @@ import { MESSAGE_STATUS_DELETED, MESSAGE_TYPE_INBOX } from "../../components/mes
import { GET_USER_MESSAGE_TAGS } from "../../graphql/queries/MessageTag" import { GET_USER_MESSAGE_TAGS } from "../../graphql/queries/MessageTag"
import { useNotification } from "../../composables/notification" import { useNotification } from "../../composables/notification"
import { useMessageRelUserStore } from "../../store/messageRelUserStore" import { useMessageRelUserStore } from "../../store/messageRelUserStore"
import SocialSideMenu from "../../components/social/SocialSideMenu.vue"
import { useSecurityStore } from "../../store/securityStore" import { useSecurityStore } from "../../store/securityStore"
const route = useRoute() const route = useRoute()

@ -1,66 +1,55 @@
<template> <template>
<div v-if="!isLoadingPage && hasPermission" class="flex flex-wrap md:flex-nowrap gap-4"> <div v-if="!isLoadingPage && hasPermission" class="flex flex-wrap md:flex-nowrap gap-4">
<div class="flex flex-col w-full md:w-1/4 lg:w-1/6"> <div class="flex flex-col w-full md:w-1/5">
<UserProfileCard /> <UserProfileCard />
<SocialSideMenu /> <MyGroupsCard v-if="!hideSocialGroupBlock" />
</div> <MyFriendsCard />
<div class="flex-grow w-full md:basis-1/2 lg:basis-2/3"> <MySkillsCard />
<component :is="currentComponent" />
</div>
<div class="flex flex-col w-full md:w-1/4 lg:w-1/6" v-if="!isSearchPage">
<MyGroupsCard v-if="!hideSocialGroupBlock" />
<MyFriendsCard />
<MySkillsCard />
</div>
</div> </div>
<div v-if="!isLoadingPage && !hasPermission"> <div class="flex-grow w-full md:w-4/5">
<div class="flex flex-wrap md:flex-nowrap gap-4"> <component :is="currentComponent" />
</div>
</div>
<div v-if="!isLoadingPage && !hasPermission">
<div class="flex flex-wrap md:flex-nowrap gap-4">
<p> {{ t("You do not have permission to view this page") }}</p> <p> {{ t("You do not have permission to view this page") }}</p>
</div>
</div> </div>
</div>
</template> </template>
<script setup> <script setup>
import { onMounted, provide, computed, readonly, ref, watch, watchEffect } from "vue" import { onMounted, provide, computed, ref } from "vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import SocialWall from "./SocialWall.vue" import SocialWall from "./SocialWall.vue"
import SocialSearch from "./SocialSearch.vue" import SocialSearch from "./SocialSearch.vue"
import UserProfileCard from "../../components/social/UserProfileCard.vue" import UserProfileCard from "../../components/social/UserProfileCard.vue"
import SocialSideMenu from "../../components/social/SocialSideMenu.vue"
import MyGroupsCard from "../../components/social/MyGroupsCard.vue" import MyGroupsCard from "../../components/social/MyGroupsCard.vue"
import MyFriendsCard from "../../components/social/MyFriendsCard.vue" import MyFriendsCard from "../../components/social/MyFriendsCard.vue"
import MySkillsCard from "../../components/social/MySkillsCard.vue" import MySkillsCard from "../../components/social/MySkillsCard.vue"
import { useSocialInfo } from "../../composables/useSocialInfo" import { useSocialInfo } from "../../composables/useSocialInfo"
import { useSocialStore } from "../../store/socialStore"
import { useI18n } from "vue-i18n" import { useI18n } from "vue-i18n"
import { useSecurityStore } from "../../store/securityStore" import { useSecurityStore } from "../../store/securityStore"
import { usePlatformConfig } from "../../store/platformConfig" import { usePlatformConfig } from "../../store/platformConfig"
const platformConfigStore = usePlatformConfig() const platformConfigStore = usePlatformConfig()
const hideSocialGroupBlock = "true" === platformConfigStore.getSetting("social.hide_social_groups_block") const hideSocialGroupBlock = "true" === platformConfigStore.getSetting("social.hide_social_groups_block")
const route = useRoute() const route = useRoute()
const { t } = useI18n() const { t } = useI18n()
const securityStore = useSecurityStore() const securityStore = useSecurityStore()
const socialStore = useSocialStore()
const hasPermission = ref(false) const hasPermission = ref(false)
const isLoadingPage = ref(true) const isLoadingPage = ref(true)
const { user, isCurrentUser, groupInfo, isGroup, loadUser } = useSocialInfo() const { user, isCurrentUser, groupInfo, isGroup } = useSocialInfo()
provide("social-user", user) provide("social-user", user)
provide("is-current-user", isCurrentUser) provide("is-current-user", isCurrentUser)
provide("group-info", groupInfo) provide("group-info", groupInfo)
provide("is-group", isGroup) provide("is-group", isGroup)
const profileUserId = computed(() => route.query.id)
onMounted(async () => { onMounted(async () => {
if (profileUserId.value) { isLoadingPage.value = false
await socialStore.checkUserRelation(securityStore.user.id, profileUserId.value) if (securityStore.user.id) {
hasPermission.value = socialStore.isProfileVisible
} else {
hasPermission.value = true hasPermission.value = true
} }
isLoadingPage.value = false
}) })
const isSearchPage = computed(() => route.path.includes('/social/search')) const isSearchPage = computed(() => route.path.includes('/social/search'))

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<SocialWallPostForm @post-created="refreshPosts" class="mb-6" /> <SocialWallPostForm v-if="!hidePostForm" @post-created="refreshPosts" class="mb-6" />
<SocialWallPostList ref="postListRef" class="mb-6" /> <SocialWallPostList ref="postListRef" class="mb-6" />
</div> </div>
</template> </template>
@ -10,6 +10,13 @@ import { ref } from 'vue';
import SocialWallPostForm from "../../components/social/SocialWallPostForm.vue" import SocialWallPostForm from "../../components/social/SocialWallPostForm.vue"
import SocialWallPostList from "../../components/social/SocialWallPostList.vue" import SocialWallPostList from "../../components/social/SocialWallPostList.vue"
const props = defineProps({
hidePostForm: {
type: Boolean,
default: false
}
});
const postListRef = ref(null); const postListRef = ref(null);
function refreshPosts() { function refreshPosts() {

@ -27,8 +27,7 @@
</template> </template>
<script setup> <script setup>
import axios from 'axios' import { inject, onMounted, ref } from "vue"
import { inject, onMounted, ref, watchEffect } from "vue"
import InvitationList from "../../components/userreluser/InvitationList.vue" import InvitationList from "../../components/userreluser/InvitationList.vue"
import BaseButton from "../../components/basecomponents/BaseButton.vue" import BaseButton from "../../components/basecomponents/BaseButton.vue"
import { useRouter } from "vue-router" import { useRouter } from "vue-router"
@ -38,12 +37,9 @@ const receivedInvitations = ref([])
const sentInvitations = ref([]) const sentInvitations = ref([])
const pendingInvitations = ref([]) const pendingInvitations = ref([])
const router = useRouter() const router = useRouter()
const user = inject('social-user') const user = inject('social-user')
const isCurrentUser = inject('is-current-user')
watchEffect(() => { onMounted(() => {
if (user.value && user.value.id) { if (user.value && user.value.id) {
fetchInvitations(user.value.id) fetchInvitations(user.value.id)
} }

@ -312,25 +312,26 @@ class SocialController extends AbstractController
): JsonResponse { ): JsonResponse {
$baseUrl = $requestStack->getCurrentRequest()->getBaseUrl(); $baseUrl = $requestStack->getCurrentRequest()->getBaseUrl();
$cid = (int) $settingsManager->getSetting('forum.global_forums_course_id'); $cid = (int) $settingsManager->getSetting('forum.global_forums_course_id');
$groupsArray = []; $items = [];
$threadsArray = []; $goToUrl = '';
if (!empty($cid)) { if (!empty($cid)) {
$threads = $forumThreadRepository->getThreadsBySubscriptions($userId, $cid); $threads = $forumThreadRepository->getThreadsBySubscriptions($userId, $cid);
foreach ($threads as $thread) { foreach ($threads as $thread) {
$threadId = $thread->getIid(); $threadId = $thread->getIid();
$forumId = (int) $thread->getForum()->getIid(); $forumId = (int) $thread->getForum()->getIid();
$threadsArray[] = [ $items[] = [
'id' => $threadId, 'id' => $threadId,
'name' => $thread->getTitle(), 'name' => $thread->getTitle(),
'description' => '', 'description' => '',
'url' => $baseUrl.'/main/forum/viewthread.php?cid='.$cid.'&sid=0&gid=0&forum='.$forumId.'&thread='.$threadId, 'url' => $baseUrl.'/main/forum/viewthread.php?cid='.$cid.'&sid=0&gid=0&forum='.$forumId.'&thread='.$threadId,
'go_to' => $baseUrl.'/main/forum/index.php?cid='.$cid.'&sid=0&gid=0',
]; ];
} }
$goToUrl = $baseUrl.'/main/forum/index.php?cid='.$cid.'&sid=0&gid=0';
} else { } else {
$groups = $usergroupRepository->getGroupsByUser($userId); $groups = $usergroupRepository->getGroupsByUser($userId);
foreach ($groups as $group) { foreach ($groups as $group) {
$groupsArray[] = [ $items[] = [
'id' => $group->getId(), 'id' => $group->getId(),
'name' => $group->getTitle(), 'name' => $group->getTitle(),
'description' => $group->getDescription(), 'description' => $group->getDescription(),
@ -339,11 +340,10 @@ class SocialController extends AbstractController
} }
} }
if (!empty($threadsArray)) { return $this->json([
return $this->json(['groups' => $threadsArray]); 'items' => $items,
} 'go_to' => $goToUrl
]);
return $this->json(['groups' => $groupsArray]);
} }
#[Route('/group/{groupId}/discussion/{discussionId}/messages', name: 'chamilo_core_social_group_discussion_messages')] #[Route('/group/{groupId}/discussion/{discussionId}/messages', name: 'chamilo_core_social_group_discussion_messages')]

Loading…
Cancel
Save