forked from CIT/Vmeda.Online
		
	Merge pull request #1887 from albertgasset/MOBILE-2988
MOBILE-2988 forum: Add the ability to lock discussions manually
This commit is contained in:
		
						commit
						561dd1d4f8
					
				| @ -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", | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
|  | ||||
| @ -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.", | ||||
|  | ||||
| @ -13,6 +13,8 @@ | ||||
|         <core-context-menu-item [hidden]="sort == 'flat-oldest'" [priority]="500" [content]="'addon.mod_forum.modeflatoldestfirst' | translate" (action)="changeSort('flat-oldest')" iconAction="arrow-round-down"></core-context-menu-item> | ||||
|         <core-context-menu-item [hidden]="sort == 'flat-newest'" [priority]="450" [content]="'addon.mod_forum.modeflatnewestfirst' | translate" (action)="changeSort('flat-newest')" iconAction="arrow-round-up"></core-context-menu-item> | ||||
|         <core-context-menu-item [hidden]="sort == 'nested'" [priority]="400" [content]="'addon.mod_forum.modenested' | translate" (action)="changeSort('nested')" iconAction="swap"></core-context-menu-item> | ||||
|         <core-context-menu-item [hidden]="!discussion || !discussion.canlock || discussion.locked" [priority]="300" [content]="'addon.mod_forum.notlocked' | translate" (action)="setLockState(true)" iconAction="fa-unlock"></core-context-menu-item> | ||||
|         <core-context-menu-item [hidden]="!discussion || !discussion.canlock || !discussion.locked" [priority]="300" [content]="'addon.mod_forum.locked' | translate" (action)="setLockState(false)" iconAction="fa-lock"></core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| <ion-content> | ||||
| @ -26,7 +28,7 @@ | ||||
|             <ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: discussionStr} }} | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <ion-card class="core-warning-card" *ngIf="locked"> | ||||
|         <ion-card class="core-warning-card" *ngIf="discussion && discussion.locked"> | ||||
|             <ion-icon name="warning"></ion-icon> {{ 'addon.mod_forum.discussionlocked' | translate }} | ||||
|         </ion-card> | ||||
| 
 | ||||
|  | ||||
| @ -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. | ||||
|      */ | ||||
|  | ||||
| @ -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<any>} Promise resvoled when done. | ||||
|      * @since 3.7 | ||||
|      */ | ||||
|     setLockState(forumId: number, discussionId: number, locked: boolean, siteId?: string): Promise<any> { | ||||
|         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. | ||||
|      * | ||||
|  | ||||
| @ -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<any>} Promise resolved with the discussion data. | ||||
|      */ | ||||
|     getDiscussionById(forumId: number, discussionId: number, siteId?: string): Promise<any> { | ||||
|         siteId = siteId || this.sitesProvider.getCurrentSiteId(); | ||||
| 
 | ||||
|         const findDiscussion = (page: number): Promise<any> => { | ||||
|             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. | ||||
|      * | ||||
|  | ||||
| @ -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.", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user