From 6eab6068f4bb3eb21a71f28bce59ee5f3b0c74ff Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Tue, 9 Apr 2019 15:35:24 +0200 Subject: [PATCH] MOBILE-2978 forum: Private reply --- scripts/langindex.json | 2 + .../components/post/addon-mod-forum-post.html | 9 +++- src/addon/mod/forum/components/post/post.ts | 15 +++++- src/addon/mod/forum/lang/en.json | 2 + .../forum/pages/discussion/discussion.html | 6 +-- .../mod/forum/pages/discussion/discussion.ts | 15 +++++- src/addon/mod/forum/providers/forum.ts | 52 +++++++++++++++++++ src/addon/mod/forum/providers/helper.ts | 7 ++- .../mod/forum/providers/prefetch-handler.ts | 11 +++- src/assets/lang/en.json | 3 ++ 10 files changed, 112 insertions(+), 10 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index d86efa26d..37f6829ca 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -496,7 +496,9 @@ "addon.mod_forum.modulenameplural": "forum", "addon.mod_forum.numdiscussions": "local_moodlemobileapp", "addon.mod_forum.numreplies": "local_moodlemobileapp", + "addon.mod_forum.postisprivatereply": "forum", "addon.mod_forum.posttoforum": "forum", + "addon.mod_forum.privatereply": "forum", "addon.mod_forum.re": "forum", "addon.mod_forum.refreshdiscussions": "local_moodlemobileapp", "addon.mod_forum.refreshposts": "local_moodlemobileapp", diff --git a/src/addon/mod/forum/components/post/addon-mod-forum-post.html b/src/addon/mod/forum/components/post/addon-mod-forum-post.html index 6592dabb2..3c827084a 100644 --- a/src/addon/mod/forum/components/post/addon-mod-forum-post.html +++ b/src/addon/mod/forum/components/post/addon-mod-forum-post.html @@ -13,6 +13,9 @@ +
+ {{ 'addon.mod_forum.postisprivatereply' | translate }} +
@@ -25,7 +28,7 @@ - + @@ -45,6 +48,10 @@ + + {{ 'addon.mod_forum.privatereply' | translate }} + + diff --git a/src/addon/mod/forum/components/post/post.ts b/src/addon/mod/forum/components/post/post.ts index d245ddff0..454d80ab5 100644 --- a/src/addon/mod/forum/components/post/post.ts +++ b/src/addon/mod/forum/components/post/post.ts @@ -44,6 +44,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { @Input() originalData: any; // Object with the original post data. Usually shared between posts. @Input() trackPosts: boolean; // True if post is being tracked. @Input() forum: any; // The forum the post belongs to. Required for attachments and offline posts. + @Input() accessInfo: any; // Forum access information. @Input() defaultSubject: string; // Default subject to set to new posts. @Input() ratingInfo?: CoreRatingInfo; // Rating info item. @Output() onPostChange: EventEmitter; // Event emitted when a reply is posted or modified. @@ -95,9 +96,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { * @param {boolean} [isEditing] True it's an offline reply beeing edited, false otherwise. * @param {string} [subject] Subject of the reply. * @param {string} [message] Message of the reply. + * @param {boolean} [isPrivate] True if it's private reply. * @param {any[]} [files] Reply attachments. */ - protected setReplyData(replyingTo?: number, isEditing?: boolean, subject?: string, message?: string, files?: any[]): void { + protected setReplyData(replyingTo?: number, isEditing?: boolean, subject?: string, message?: string, files?: any[], + isPrivate?: boolean): void { // Delete the local files from the tmp folder if any. this.uploaderProvider.clearTmpFiles(this.replyData.files); @@ -106,6 +109,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { this.replyData.subject = subject || this.defaultSubject || ''; this.replyData.message = message || null; this.replyData.files = files || []; + this.replyData.isprivatereply = !!isPrivate; // Update rich text editor. this.messageControl.setValue(this.replyData.message); @@ -114,6 +118,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { this.originalData.subject = this.replyData.subject; this.originalData.message = this.replyData.message; this.originalData.files = this.replyData.files.slice(); + this.originalData.isprivatereply = this.replyData.isprivatereply; } /** @@ -163,7 +168,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { this.syncId = this.forumSync.getDiscussionSyncId(this.discussionId); this.syncProvider.blockOperation(AddonModForumProvider.COMPONENT, this.syncId); - this.setReplyData(this.post.parent, true, this.post.subject, this.post.message, this.post.attachments); + this.setReplyData(this.post.parent, true, this.post.subject, this.post.message, this.post.attachments, + this.post.isprivatereply); }).catch(() => { // Cancelled. }); @@ -206,6 +212,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy { // Add some HTML to the message if needed. message = this.textUtils.formatHtmlLines(message); + // Set private option if checked. + if (this.replyData.isprivatereply) { + options.private = true; + } + // Upload attachments first if any. if (files.length) { promise = this.forumHelper.uploadOrStoreReplyFiles(this.forum.id, replyingTo, files, false).catch((error) => { diff --git a/src/addon/mod/forum/lang/en.json b/src/addon/mod/forum/lang/en.json index 214e828d0..c0ae05283 100644 --- a/src/addon/mod/forum/lang/en.json +++ b/src/addon/mod/forum/lang/en.json @@ -24,7 +24,9 @@ "modulenameplural": "Forums", "numdiscussions": "{{numdiscussions}} discussions", "numreplies": "{{numreplies}} replies", + "postisprivatereply": "This post was made privately and is not visible to all users.", "posttoforum": "Post to forum", + "privatereply": "Reply privately", "re": "Re:", "refreshdiscussions": "Refresh discussions", "refreshposts": "Refresh posts", diff --git a/src/addon/mod/forum/pages/discussion/discussion.html b/src/addon/mod/forum/pages/discussion/discussion.html index 01be9ede2..99adf295a 100644 --- a/src/addon/mod/forum/pages/discussion/discussion.html +++ b/src/addon/mod/forum/pages/discussion/discussion.html @@ -31,13 +31,13 @@ - + - + @@ -49,7 +49,7 @@ - +
diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts index 7cf56cdf0..48c5949ba 100644 --- a/src/addon/mod/forum/pages/discussion/discussion.ts +++ b/src/addon/mod/forum/pages/discussion/discussion.ts @@ -47,6 +47,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { courseId: number; discussionId: number; forum: any; + accessInfo: any; discussion: any; posts: any[]; discussionLoaded = false; @@ -63,11 +64,13 @@ export class AddonModForumDiscussionPage implements OnDestroy { subject: '', message: null, // Null means empty or just white space. files: [], + isprivatereply: false, }; originalData = { subject: null, // Null means original data is not set. message: null, // Null means empty or just white space. files: [], + isprivatereply: false, }; refreshIcon = 'spinner'; syncIcon = 'spinner'; @@ -318,9 +321,14 @@ 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) => { + this.accessInfo = accessInfo; + }); }).catch(() => { // Ignore errors. this.forum = {}; + this.accessInfo = {}; }); }).then(() => { return this.ratingOffline.hasRatings('mod_forum', 'post', 'module', this.cmId, this.discussionId).then((hasRatings) => { @@ -420,7 +428,12 @@ export class AddonModForumDiscussionPage implements OnDestroy { this.refreshIcon = 'spinner'; this.syncIcon = 'spinner'; - return this.forumProvider.invalidateDiscussionPosts(this.discussionId).catch(() => { + const promises = [ + this.forumProvider.invalidateDiscussionPosts(this.discussionId), + this.forumProvider.invalidateAccessInformation(this.forumId) + ]; + + return this.utils.allPromises(promises).catch(() => { // Ignore errors. }).then(() => { return this.fetchPosts(sync, showErrors); diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts index 83f1db720..e4bc9f978 100644 --- a/src/addon/mod/forum/providers/forum.ts +++ b/src/addon/mod/forum/providers/forum.ts @@ -79,6 +79,16 @@ export class AddonModForumProvider { return this.ROOT_CACHE_KEY + 'forum:' + courseId; } + /** + * Get cache key for forum access information WS calls. + * + * @param {number} forumId Forum ID. + * @return {string} Cache key. + */ + protected getAccessInformationCacheKey(forumId: number): string { + return this.ROOT_CACHE_KEY + 'accessInformation:' + forumId; + } + /** * Get cache key for forum discussion posts WS calls. * @@ -365,6 +375,34 @@ export class AddonModForumProvider { }); } + /** + * Get access information for a given forum. + * + * @param {number} forumId Forum ID. + * @param {boolean} [forceCache] True to always get the value from cache. false otherwise. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Object with access information. + * @since 3.7 + */ + getAccessInformation(forumId: number, forceCache?: boolean, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + if (!site.wsAvailable('mod_forum_get_forum_access_information')) { + // Access information not available for 3.6 or older sites. + return Promise.resolve({}); + } + + const params = { + forumid: forumId + }; + const preSets = { + cacheKey: this.getAccessInformationCacheKey(forumId), + omitExpires: forceCache + }; + + return site.read('mod_forum_get_forum_access_information', params, preSets); + }); + } + /** * Get forum discussion posts. * @@ -537,6 +575,7 @@ export class AddonModForumProvider { promises.push(this.invalidateForumData(courseId)); promises.push(this.invalidateDiscussionsList(forum.id)); promises.push(this.invalidateCanAddDiscussion(forum.id)); + promises.push(this.invalidateAccessInformation(forum.id)); response.discussions.forEach((discussion) => { promises.push(this.invalidateDiscussionPosts(discussion.discussion)); @@ -547,6 +586,19 @@ export class AddonModForumProvider { }); } + /** + * Invalidates access information. + * + * @param {number} forumId Forum ID. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateAccessInformation(forumId: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(forumId)); + }); + } + /** * Invalidates forum discussion posts. * diff --git a/src/addon/mod/forum/providers/helper.ts b/src/addon/mod/forum/providers/helper.ts index e039cdf7b..d33c6ef28 100644 --- a/src/addon/mod/forum/providers/helper.ts +++ b/src/addon/mod/forum/providers/helper.ts @@ -54,7 +54,8 @@ export class AddonModForumHelperProvider { postread: false, subject: offlineReply.subject, totalscore: 0, - userid: offlineReply.userid + userid: offlineReply.userid, + isprivatereply: offlineReply.options && offlineReply.options.private }, promises = []; @@ -164,6 +165,10 @@ export class AddonModForumHelperProvider { return true; } + if (post.isprivatereply != original.isprivatereply) { + return true; + } + return this.uploaderProvider.areFileListDifferent(post.files, original.files); } diff --git a/src/addon/mod/forum/providers/prefetch-handler.ts b/src/addon/mod/forum/providers/prefetch-handler.ts index 03e60a6ba..a3f70f913 100644 --- a/src/addon/mod/forum/providers/prefetch-handler.ts +++ b/src/addon/mod/forum/providers/prefetch-handler.ts @@ -183,8 +183,10 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise { // Get the forum data. return this.forumProvider.getForum(courseId, module.id).then((forum) => { + const promises = []; + // Prefetch the posts. - return this.getPostsForPrefetch(forum).then((posts) => { + promises.push(this.getPostsForPrefetch(forum).then((posts) => { const promises = []; // Prefetch user profiles. @@ -201,7 +203,12 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions)); return Promise.all(promises); - }); + })); + + // Prefetch access information. + promises.push(this.forumProvider.getAccessInformation(forum.id)); + + return Promise.all(promises); }); } diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 8fd9e1499..782fa21f1 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -496,7 +496,9 @@ "addon.mod_forum.modulenameplural": "Forums", "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.", "addon.mod_forum.posttoforum": "Post to forum", + "addon.mod_forum.privatereply": "Reply privately", "addon.mod_forum.re": "Re:", "addon.mod_forum.refreshdiscussions": "Refresh discussions", "addon.mod_forum.refreshposts": "Refresh posts", @@ -1342,6 +1344,7 @@ "core.favourites": "Starred", "core.filename": "Filename", "core.filenameexist": "File name already exists: {{$a}}", + "core.filenotfound": "File not found, sorry.", "core.fileuploader.addfiletext": "Add file", "core.fileuploader.audio": "Audio", "core.fileuploader.camera": "Camera",