diff --git a/scripts/langindex.json b/scripts/langindex.json
index a2efd09d1..71ed9574f 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -494,11 +494,13 @@
"addon.mod_forum.errorgetgroups": "local_moodlemobileapp",
"addon.mod_forum.forumnodiscussionsyet": "local_moodlemobileapp",
"addon.mod_forum.group": "local_moodlemobileapp",
+ "addon.mod_forum.locked": "forum",
"addon.mod_forum.message": "forum",
"addon.mod_forum.modeflatnewestfirst": "forum",
"addon.mod_forum.modeflatoldestfirst": "forum",
"addon.mod_forum.modenested": "forum",
"addon.mod_forum.modulenameplural": "forum",
+ "addon.mod_forum.notlocked": "forum",
"addon.mod_forum.numdiscussions": "local_moodlemobileapp",
"addon.mod_forum.numreplies": "local_moodlemobileapp",
"addon.mod_forum.postisprivatereply": "forum",
diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts
index 999974f31..b3c8769e7 100644
--- a/src/addon/mod/forum/components/index/index.ts
+++ b/src/addon/mod/forum/components/index/index.ts
@@ -58,6 +58,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
protected replyObserver: any;
protected newDiscObserver: any;
protected viewDiscObserver: any;
+ protected changeDiscObserver: any;
hasOfflineRatings: boolean;
protected ratingOfflineObserver: any;
@@ -94,6 +95,8 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
this.eventReceived.bind(this, true));
this.replyObserver = this.eventsProvider.on(AddonModForumProvider.REPLY_DISCUSSION_EVENT,
this.eventReceived.bind(this, false));
+ this.changeDiscObserver = this.eventsProvider.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT,
+ this.eventReceived.bind(this, false));
// Select the current opened discussion.
this.viewDiscObserver = this.eventsProvider.on(AddonModForumProvider.VIEW_DISCUSSION_EVENT, (data) => {
@@ -446,9 +449,8 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
courseId: this.courseId,
cmId: this.module.id,
forumId: this.forum.id,
- discussionId: discussion.discussion,
+ discussion: discussion,
trackPosts: this.trackPosts,
- locked: discussion.locked
};
this.splitviewCtrl.push('AddonModForumDiscussionPage', params);
}
@@ -480,6 +482,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
this.newDiscObserver && this.newDiscObserver.off();
this.replyObserver && this.replyObserver.off();
this.viewDiscObserver && this.viewDiscObserver.off();
+ this.changeDiscObserver && this.changeDiscObserver.off();
this.ratingOfflineObserver && this.ratingOfflineObserver.off();
this.ratingSyncObserver && this.ratingSyncObserver.off();
}
diff --git a/src/addon/mod/forum/lang/en.json b/src/addon/mod/forum/lang/en.json
index 42fb302a5..ea720c2ff 100644
--- a/src/addon/mod/forum/lang/en.json
+++ b/src/addon/mod/forum/lang/en.json
@@ -18,11 +18,13 @@
"errorgetgroups": "Error getting group settings.",
"forumnodiscussionsyet": "There are no discussions yet in this forum.",
"group": "Group",
+ "locked": "Locked",
"message": "Message",
"modeflatnewestfirst": "Display replies flat, with newest first",
"modeflatoldestfirst": "Display replies flat, with oldest first",
"modenested": "Display replies in nested form",
"modulenameplural": "Forums",
+ "notlocked": "Lock",
"numdiscussions": "{{numdiscussions}} discussions",
"numreplies": "{{numreplies}} replies",
"postisprivatereply": "This post was made privately and is not visible to all users.",
diff --git a/src/addon/mod/forum/pages/discussion/discussion.html b/src/addon/mod/forum/pages/discussion/discussion.html
index 99adf295a..434ac76e2 100644
--- a/src/addon/mod/forum/pages/discussion/discussion.html
+++ b/src/addon/mod/forum/pages/discussion/discussion.html
@@ -13,6 +13,8 @@
+
+
@@ -26,7 +28,7 @@
{{ 'core.hasdatatosync' | translate:{$a: discussionStr} }}
-
+
{{ 'addon.mod_forum.discussionlocked' | translate }}
diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts
index 48c5949ba..8b3a81a42 100644
--- a/src/addon/mod/forum/pages/discussion/discussion.ts
+++ b/src/addon/mod/forum/pages/discussion/discussion.ts
@@ -54,7 +54,6 @@ export class AddonModForumDiscussionPage implements OnDestroy {
defaultSubject: string;
isOnline: boolean;
isSplitViewOn: boolean;
- locked: boolean;
postHasOffline: boolean;
sort: SortType = 'flat-oldest';
trackPosts: boolean;
@@ -108,9 +107,9 @@ export class AddonModForumDiscussionPage implements OnDestroy {
this.courseId = navParams.get('courseId');
this.cmId = navParams.get('cmId');
this.forumId = navParams.get('forumId');
- this.discussionId = navParams.get('discussionId');
+ this.discussion = navParams.get('discussion');
+ this.discussionId = this.discussion ? this.discussion.discussion : navParams.get('discussionId');
this.trackPosts = navParams.get('trackPosts');
- this.locked = navParams.get('locked');
this.postId = navParams.get('postId');
this.isOnline = this.appProvider.isOnline();
@@ -222,7 +221,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
}
/**
- * Convenience function to get forum discussions.
+ * Convenience function to get the posts.
*
* @param {boolean} [sync] Whether to try to synchronize the discussion.
* @param {boolean} [showErrors] Whether to show errors in a modal.
@@ -243,11 +242,12 @@ export class AddonModForumDiscussionPage implements OnDestroy {
let onlinePosts = [];
const offlineReplies = [];
let hasUnreadPosts = false;
+ let ratingInfo;
return syncPromise.then(() => {
return this.forumProvider.getDiscussionPosts(this.discussionId).then((response) => {
onlinePosts = response.posts;
- this.ratingInfo = response.ratinginfo;
+ ratingInfo = response.ratinginfo;
}).then(() => {
// Check if there are responses stored in offline.
return this.forumOffline.getDiscussionReplies(this.discussionId).then((replies) => {
@@ -285,34 +285,23 @@ export class AddonModForumDiscussionPage implements OnDestroy {
});
});
}).then(() => {
- const posts = offlineReplies.concat(onlinePosts);
- this.discussion = this.forumProvider.extractStartingPost(posts);
-
- if (!this.discussion) {
- return Promise.reject('Invalid forum discussion');
- }
+ let posts = offlineReplies.concat(onlinePosts);
// If sort type is nested, normal sorting is disabled and nested posts will be displayed.
if (this.sort == 'nested') {
// Sort first by creation date to make format tree work.
this.forumProvider.sortDiscussionPosts(posts, 'ASC');
- this.posts = this.utils.formatTree(posts, 'parent', 'id', this.discussion.id);
+ posts = this.utils.formatTree(posts, 'parent', 'id', this.discussion.id);
} else {
// Set default reply subject.
const direction = this.sort == 'flat-newest' ? 'DESC' : 'ASC';
this.forumProvider.sortDiscussionPosts(posts, direction);
- this.posts = posts;
}
this.defaultSubject = this.translate.instant('addon.mod_forum.re') + ' ' + this.discussion.subject;
this.replyData.subject = this.defaultSubject;
// Now try to get the forum.
return this.fetchForum().then((forum) => {
- if (this.discussion.userfullname && this.discussion.parent == 0 && forum.type == 'single') {
- // Hide author for first post and type single.
- this.discussion.userfullname = null;
- }
-
// "forum.istracked" is more reliable than "trackPosts".
if (typeof forum.istracked != 'undefined') {
this.trackPosts = forum.istracked;
@@ -321,14 +310,44 @@ export class AddonModForumDiscussionPage implements OnDestroy {
this.forumId = forum.id;
this.cmId = forum.cmid;
this.forum = forum;
- }).then(() => {
- return this.forumProvider.getAccessInformation(this.forum.id).then((accessInfo) => {
+
+ const promises = [];
+
+ promises.push(this.forumProvider.getAccessInformation(this.forum.id).then((accessInfo) => {
this.accessInfo = accessInfo;
- });
+ }));
+
+ // Fetch the discussion if not passed as parameter.
+ if (!this.discussion) {
+ promises.push(this.forumHelper.getDiscussionById(forum.id, this.discussionId).then((discussion) => {
+ this.discussion = discussion;
+ }).catch(() => {
+ // Ignore errors.
+ }));
+ }
+
+ return Promise.all(promises);
}).catch(() => {
// Ignore errors.
this.forum = {};
this.accessInfo = {};
+ }).then(() => {
+ const startingPost = this.forumProvider.extractStartingPost(posts);
+ if (startingPost) {
+ // Update discussion data from first post.
+ this.discussion = Object.assign(this.discussion || {}, startingPost);
+ } else if (!this.discussion) {
+ // The discussion object was not passed as parameter and there is no starting post.
+ return Promise.reject('Invalid forum discussion.');
+ }
+
+ if (this.discussion.userfullname && this.discussion.parent == 0 && this.forum.type == 'single') {
+ // Hide author for first post and type single.
+ this.discussion.userfullname = null;
+ }
+
+ this.posts = posts;
+ this.ratingInfo = ratingInfo;
});
}).then(() => {
return this.ratingOffline.hasRatings('mod_forum', 'post', 'module', this.cmId, this.discussionId).then((hasRatings) => {
@@ -454,6 +473,31 @@ export class AddonModForumDiscussionPage implements OnDestroy {
return this.fetchPosts();
}
+ /**
+ * Lock or unlock the discussion.
+ *
+ * @param {boolean} locked True to lock the discussion, false to unlock.
+ */
+ setLockState(locked: boolean): void {
+ const modal = this.domUtils.showModalLoading('core.sending', true);
+
+ this.forumProvider.setLockState(this.forumId, this.discussionId, locked).then((response) => {
+ this.discussion.locked = response.locked;
+
+ const data = {
+ forumId: this.forumId,
+ discussionId: this.discussionId,
+ cmId: this.cmId,
+ locked: this.discussion.locked
+ };
+ this.eventsProvider.trigger(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data, this.sitesProvider.getCurrentSiteId());
+ }).catch((error) => {
+ this.domUtils.showErrorModal(error);
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+
/**
* New post added.
*/
diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts
index e4bc9f978..520ed72c5 100644
--- a/src/addon/mod/forum/providers/forum.ts
+++ b/src/addon/mod/forum/providers/forum.ts
@@ -34,6 +34,7 @@ export class AddonModForumProvider {
static NEW_DISCUSSION_EVENT = 'addon_mod_forum_new_discussion';
static REPLY_DISCUSSION_EVENT = 'addon_mod_forum_reply_discussion';
static VIEW_DISCUSSION_EVENT = 'addon_mod_forum_view_discussion';
+ static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_lock_discussion';
static MARK_READ_EVENT = 'addon_mod_forum_mark_read';
protected ROOT_CACHE_KEY = 'mmaModForum:';
@@ -761,6 +762,28 @@ export class AddonModForumProvider {
});
}
+ /**
+ * Lock or unlock a discussion.
+ *
+ * @param {number} forumId Forum id.
+ * @param {number} discussionId DIscussion id.
+ * @param {boolean} locked True to lock, false to unlock.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resvoled when done.
+ * @since 3.7
+ */
+ setLockState(forumId: number, discussionId: number, locked: boolean, siteId?: string): Promise {
+ return this.sitesProvider.getSite(siteId).then((site) => {
+ const params = {
+ forumid: forumId,
+ discussionid: discussionId,
+ targetstate: locked ? 0 : 1
+ };
+
+ return site.write('mod_forum_set_lock_state', params);
+ });
+ }
+
/**
* Store the users data from a discussions/posts list.
*
diff --git a/src/addon/mod/forum/providers/helper.ts b/src/addon/mod/forum/providers/helper.ts
index d33c6ef28..eeeb4a0f7 100644
--- a/src/addon/mod/forum/providers/helper.ts
+++ b/src/addon/mod/forum/providers/helper.ts
@@ -15,6 +15,7 @@
import { Injectable } from '@angular/core';
import { CoreFileProvider } from '@providers/file';
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
+import { CoreSitesProvider } from '@providers/sites';
import { CoreUserProvider } from '@core/user/providers/user';
import { AddonModForumProvider } from './forum';
import { AddonModForumOfflineProvider } from './offline';
@@ -25,8 +26,10 @@ import { AddonModForumOfflineProvider } from './offline';
@Injectable()
export class AddonModForumHelperProvider {
constructor(private fileProvider: CoreFileProvider,
+ private sitesProvider: CoreSitesProvider,
private uploaderProvider: CoreFileUploaderProvider,
private userProvider: CoreUserProvider,
+ private forumProvider: AddonModForumProvider,
private forumOffline: AddonModForumOfflineProvider) {}
/**
@@ -119,6 +122,38 @@ export class AddonModForumHelperProvider {
});
}
+ /**
+ * Get a forum discussion by id.
+ *
+ * This function is inefficient because it needs to fetch all discussion pages in the worst case.
+ *
+ * @param {number} forumId Forum ID.
+ * @param {number} discussionId Discussion ID.
+ * @param {string} [siteId] Site ID. If not defined, current site.
+ * @return {Promise} Promise resolved with the discussion data.
+ */
+ getDiscussionById(forumId: number, discussionId: number, siteId?: string): Promise {
+ siteId = siteId || this.sitesProvider.getCurrentSiteId();
+
+ const findDiscussion = (page: number): Promise => {
+ return this.forumProvider.getDiscussions(forumId, page, false, siteId).then((response) => {
+ if (response.discussions && response.discussions.length > 0) {
+ const discussion = response.discussions.find((discussion) => discussion.id == discussionId);
+ if (discussion) {
+ return discussion;
+ }
+ if (response.canLoadMore) {
+ return findDiscussion(page + 1);
+ }
+ }
+
+ return Promise.reject(null);
+ });
+ };
+
+ return findDiscussion(0);
+ }
+
/**
* Get a list of stored attachment files for a new discussion. See AddonModForumHelper#storeNewDiscussionFiles.
*
diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json
index 66ca0a25a..c4340c998 100644
--- a/src/assets/lang/en.json
+++ b/src/assets/lang/en.json
@@ -494,11 +494,13 @@
"addon.mod_forum.errorgetgroups": "Error getting group settings.",
"addon.mod_forum.forumnodiscussionsyet": "There are no discussions yet in this forum.",
"addon.mod_forum.group": "Group",
+ "addon.mod_forum.locked": "Locked",
"addon.mod_forum.message": "Message",
"addon.mod_forum.modeflatnewestfirst": "Display replies flat, with newest first",
"addon.mod_forum.modeflatoldestfirst": "Display replies flat, with oldest first",
"addon.mod_forum.modenested": "Display replies in nested form",
"addon.mod_forum.modulenameplural": "Forums",
+ "addon.mod_forum.notlocked": "Lock",
"addon.mod_forum.numdiscussions": "{{numdiscussions}} discussions",
"addon.mod_forum.numreplies": "{{numreplies}} replies",
"addon.mod_forum.postisprivatereply": "This post was made privately and is not visible to all users.",