diff --git a/src/addons/mod/forum/classes/forum-discussions-source.ts b/src/addons/mod/forum/classes/forum-discussions-source.ts index 5b095d07f..f94712b52 100644 --- a/src/addons/mod/forum/classes/forum-discussions-source.ts +++ b/src/addons/mod/forum/classes/forum-discussions-source.ts @@ -25,7 +25,8 @@ import { AddonModForumSortOrder, } from '../services/forum'; import { AddonModForumOffline, AddonModForumOfflineDiscussion } from '../services/forum-offline'; -import { ADDON_MOD_FORUM_DISCUSSIONS_PER_PAGE } from '../constants'; +import { ADDON_MOD_FORUM_DISCUSSIONS_PER_PAGE, AddonModForumType } from '../constants'; +import { CoreSites } from '@services/sites'; export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource { @@ -168,8 +169,6 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource /** * Load some specific data for current group. - * - * @returns Promise resolved when done. */ async loadSelectedGroupData(): Promise { if (!this.usesGroups) { @@ -203,6 +202,15 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource discussions.push(...onlineDiscussions); + // If the user has already posted in a Each user posts a single discussion forum, don't allow to post again. + // This check is only needed in offline mode. + if (this.canAddDiscussionToGroup && this.forum?.type === AddonModForumType.EACHUSER) { + const userId = CoreSites.getCurrentSiteUserId(); + + this.canAddDiscussionToGroup = !discussions.some((discussion) => + this.isOfflineDiscussion(discussion) || !this.isNewDiscussionForm(discussion) && discussion.userid === userId); + } + return { items: discussions, hasMoreItems: canLoadMore, @@ -250,7 +258,7 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource } // Hide author for first post and type single. - if (this.forum.type === 'single') { + if (this.forum.type === AddonModForumType.SINGLE) { for (const discussion of discussions) { if (discussion.userfullname && discussion.parent === 0) { discussion.userfullname = false; @@ -297,7 +305,7 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource const promises = offlineDiscussions.map(async (offlineDiscussion) => { const discussion = offlineDiscussion as unknown as AddonModForumDiscussion; - if (discussion.parent === 0 || forum.type === 'single') { + if (discussion.parent === 0 || forum.type === AddonModForumType.SINGLE) { // Do not show author for first post and type single. return; } @@ -322,8 +330,6 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource /** * Invalidate cache data. - * - * @returns Promise resolved when done. */ async invalidateCache(): Promise { const promises: Promise[] = []; @@ -341,8 +347,6 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource /** * Invalidate list cache data. - * - * @returns Promise resolved when done. */ async invalidateList(): Promise { if (this.forum) { diff --git a/src/addons/mod/forum/components/index/index.ts b/src/addons/mod/forum/components/index/index.ts index f9d91c658..adcbd68d1 100644 --- a/src/addons/mod/forum/components/index/index.ts +++ b/src/addons/mod/forum/components/index/index.ts @@ -62,6 +62,7 @@ import { ADDON_MOD_FORUM_PREFERENCE_SORTORDER, ADDON_MOD_FORUM_REPLY_DISCUSSION_EVENT, ADDON_MOD_FORUM_SEARCH_PAGE_NAME, + AddonModForumType, } from '@addons/mod/forum/constants'; import { CoreSearchGlobalSearch } from '@features/search/services/global-search'; import { CoreToasts } from '@services/toasts'; @@ -411,11 +412,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom this.dataRetrieved.emit(forum); switch (forum.type) { - case 'news': - case 'blog': + case AddonModForumType.NEWS: + case AddonModForumType.BLOG: this.addDiscussionText = Translate.instant('addon.mod_forum.addanewtopic'); break; - case 'qanda': + case AddonModForumType.QANDA: this.addDiscussionText = Translate.instant('addon.mod_forum.addanewquestion'); break; default: @@ -451,7 +452,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom const cutoffDateReached = AddonModForumHelper.isCutoffDateReached(forum) && !accessInfo.cancanoverridecutoff; this.canAddDiscussion = !!forum.cancreatediscussions && !cutoffDateReached; - this.showQAMessage = forum.type === 'qanda' && !accessInfo.canviewqandawithoutposting; + this.showQAMessage = forum.type === AddonModForumType.QANDA && !accessInfo.canviewqandawithoutposting; return; }), diff --git a/src/addons/mod/forum/constants.ts b/src/addons/mod/forum/constants.ts index 459bf53e2..3bbcc9062 100644 --- a/src/addons/mod/forum/constants.ts +++ b/src/addons/mod/forum/constants.ts @@ -37,5 +37,15 @@ export const enum AddonModForumSortorder { REPLIES_ASC = 6, } +export const enum AddonModForumType { + NEWS = 'news', + SOCIAL = 'social', + GENERAL = 'general', + EACHUSER = 'eachuser', + SINGLE = 'single', + QANDA = 'qanda', + BLOG = 'blog', +} + export const ADDON_MOD_FORUM_ALL_PARTICIPANTS = -1; export const ADDON_MOD_FORUM_ALL_GROUPS = -2; diff --git a/src/addons/mod/forum/pages/discussion/discussion.ts b/src/addons/mod/forum/pages/discussion/discussion.ts index ef8fc7ec9..111b0abfb 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.ts @@ -57,6 +57,7 @@ import { ADDON_MOD_FORUM_MANUAL_SYNCED, ADDON_MOD_FORUM_MARK_READ_EVENT, ADDON_MOD_FORUM_REPLY_DISCUSSION_EVENT, + AddonModForumType, } from '../../constants'; import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; import { CoreToasts } from '@services/toasts'; @@ -512,7 +513,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes // Show Q&A message if user hasn't posted. const currentUserId = CoreSites.getCurrentSiteUserId(); - this.showQAMessage = forum.type === 'qanda' && !accessInfo.canviewqandawithoutposting && + this.showQAMessage = forum.type === AddonModForumType.QANDA && !accessInfo.canviewqandawithoutposting && !posts.some(post => post.author.id === currentUserId); return; @@ -534,7 +535,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes throw new Error('Invalid forum discussion.'); } - if (this.startingPost && this.startingPost.author && this.forum.type == 'single') { + if (this.startingPost && this.startingPost.author && this.forum.type === AddonModForumType.SINGLE) { // Hide author and groups for first post and type single. delete this.startingPost.author.fullname; delete this.startingPost.author.groups; diff --git a/src/addons/mod/forum/services/forum.ts b/src/addons/mod/forum/services/forum.ts index 7f911724f..a7a60d76d 100644 --- a/src/addons/mod/forum/services/forum.ts +++ b/src/addons/mod/forum/services/forum.ts @@ -47,6 +47,7 @@ import { ADDON_MOD_FORUM_PREFERENCE_SORTORDER, ADDON_MOD_FORUM_REPLY_DISCUSSION_EVENT, AddonModForumSortorder, + AddonModForumType, } from '../constants'; declare module '@singletons/events' { @@ -417,7 +418,10 @@ export class AddonModForumProvider { * @param options Other options. * @returns Promise resolved when the forums are retrieved. */ - async getCourseForums(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { + async getCourseForums( + courseId: number, + options: CoreSitesCommonWSOptions = {}, + ): Promise { const site = await CoreSites.getSite(options.siteId); const params: AddonModForumGetForumsByCoursesWSParams = { @@ -483,8 +487,7 @@ export class AddonModForumProvider { */ async getForum(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { const forums = await this.getCourseForums(courseId, options); - - const forum = forums.find(forum => forum.cmid == cmId); + const forum = forums.find(forum => forum.cmid === cmId); if (!forum) { throw new CoreError(Translate.instant('core.course.modulenotfound')); @@ -1385,7 +1388,7 @@ type AddonModForumGetForumsByCoursesWSParams = { export type AddonModForumData = { id: number; // Forum id. course: number; // Course id. - type: string; // The forum type. + type: AddonModForumType; // The forum type. name: string; // Forum name. intro: string; // The forum intro. introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN). @@ -1421,6 +1424,11 @@ export type AddonModForumData = { unreadpostscount?: number; // The number of unread posts for tracked forums. }; +/** + * Data returned by mod_forum_get_forums_by_courses WS. + */ +type AddonModForumGetForumsByCoursesWSResponse = AddonModForumData[]; + /** * Forum discussion. */ @@ -1751,11 +1759,6 @@ export type AddonModForumGetForumDiscussionsPaginatedWSResponse = { warnings?: CoreWSExternalWarning[]; }; -/** - * Data returned by mod_forum_get_forums_by_courses WS. - */ -export type AddonModForumGetForumsByCoursesWSResponse = AddonModForumData[]; - /** * Array options of mod_forum_add_discussion WS. */ diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index f1d37e681..fcb3abbd2 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -860,11 +860,11 @@ export class CoreLoginHelperProvider { const params: Record = {}; if (username) { - params.username = username; + params.username = username.trim(); } if (email) { - params.email = email; + params.email = email.trim(); } return CoreWS.callAjax('core_auth_request_password_reset', params, { siteUrl });