commit
						e4b2e8b3c3
					
				| @ -133,7 +133,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom | ||||
|                     } | ||||
| 
 | ||||
|                     if (typeof data.deleted != 'undefined' && data.deleted) { | ||||
|                         if (data.post.parent == 0 && this.splitviewCtrl && this.splitviewCtrl.isOn()) { | ||||
|                         if (data.post.parentid == 0 && this.splitviewCtrl && this.splitviewCtrl.isOn()) { | ||||
|                             // Discussion deleted, clear details page.
 | ||||
|                             this.splitviewCtrl.emptyDetails(); | ||||
|                         } | ||||
|  | ||||
| @ -47,30 +47,38 @@ export class AddonForumPostOptionsMenuComponent implements OnInit { | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         if (this.forumId) { | ||||
|             if (this.post.id) { | ||||
|                 const site: CoreSite = this.sitesProvider.getCurrentSite(); | ||||
|                 this.url = site.createSiteUrl('/mod/forum/discuss.php', {d: this.post.discussion}, 'p' + this.post.id); | ||||
| 
 | ||||
|                 this.forumProvider.getDiscussionPost(this.forumId, this.post.discussion, this.post.id, true).then((post) => { | ||||
|                     this.canDelete = post.capabilities.delete && this.forumProvider.isDeletePostAvailable(); | ||||
|                     this.canEdit = post.capabilities.edit && this.forumProvider.isUpdatePostAvailable(); | ||||
|                     this.wordCount = post.wordcount; | ||||
|                 }).catch((error) => { | ||||
|                     this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.'); | ||||
|                 }).finally(() => { | ||||
|                     this.loaded = true; | ||||
|                 }); | ||||
|             } else { | ||||
|                 // Offline post, you can edit or discard the post.
 | ||||
|                 this.canEdit = true; | ||||
|                 this.canDelete = true; | ||||
|                 this.loaded = true; | ||||
|             } | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         if (this.post.id) { | ||||
|             const site: CoreSite = this.sitesProvider.getCurrentSite(); | ||||
|             this.url = site.createSiteUrl('/mod/forum/discuss.php', {d: this.post.discussionid}, 'p' + this.post.id); | ||||
|         } else { | ||||
|             // Offline post, you can edit or discard the post.
 | ||||
|             this.canEdit = true; | ||||
|             this.canDelete = true; | ||||
|             this.loaded = true; | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (typeof this.post.capabilities.delete == 'undefined') { | ||||
|             if (this.forumId) { | ||||
|                 try { | ||||
|                     this.post = | ||||
|                         await this.forumProvider.getDiscussionPost(this.forumId, this.post.discussionid, this.post.id, true); | ||||
|                 } catch (error) { | ||||
|                     this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.'); | ||||
|                 } | ||||
|             } else { | ||||
|                 this.loaded = true; | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.canDelete = this.post.capabilities.delete && this.forumProvider.isDeletePostAvailable(); | ||||
|         this.canEdit = this.post.capabilities.edit && this.forumProvider.isUpdatePostAvailable(); | ||||
|         this.wordCount = this.post.haswordcount && this.post.wordcount; | ||||
|         this.loaded = true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -3,11 +3,11 @@ | ||||
|         <ion-item text-wrap> | ||||
|             <div class="addon-mod-forum-post-title" *ngIf="displaySubject"> | ||||
|                 <h2 text-wrap> | ||||
|                     <core-icon name="fa-map-pin" *ngIf="post.parent == 0 && post.pinned"></core-icon> | ||||
|                     <core-icon name="fa-star" class="addon-forum-star" *ngIf="post.parent == 0 && !post.pinned && post.starred"></core-icon> | ||||
|                     <core-icon name="fa-map-pin" *ngIf="discussion && !post.parentid && discussion.pinned"></core-icon> | ||||
|                     <core-icon name="fa-star" class="addon-forum-star" *ngIf="discussion && !post.parentid && !discussion.pinned && discussion.starred"></core-icon> | ||||
|                     <core-format-text [text]="post.subject" contextLevel="module" [contextInstanceId]="forum && forum.cmid" [courseId]="courseId"></core-format-text> | ||||
|                 </h2> | ||||
|                 <ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate"> | ||||
|                 <ion-note float-end padding-left text-end *ngIf="trackPosts && post.unread" [attr.aria-label]="'addon.mod_forum.unread' | translate"> | ||||
|                     <core-icon name="fa-circle" color="primary"></core-icon> | ||||
|                 </ion-note> | ||||
|                 <button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled" [attr.aria-label]="('core.displayoptions' | translate)"> | ||||
| @ -15,15 +15,15 @@ | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="addon-mod-forum-post-info"> | ||||
|                 <ion-avatar *ngIf="post.userfullname" core-user-avatar [user]="post" item-start [courseId]="courseId"></ion-avatar> | ||||
|                 <ion-avatar *ngIf="post.author && post.author.fullname" core-user-avatar [user]="post.author" item-start [courseId]="courseId"></ion-avatar> | ||||
|                 <div class="addon-mod-forum-post-author"> | ||||
|                     <h3 *ngIf="post.userfullname">{{post.userfullname}}</h3> | ||||
|                     <p *ngIf="post.groupname"><ion-icon name="people"></ion-icon> {{ post.groupname }}</p> | ||||
|                     <p *ngIf="post.modified">{{post.modified * 1000 | coreFormatDate: "strftimerecentfull"}}</p> | ||||
|                     <p *ngIf="!post.modified"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p> | ||||
|                     <h3 *ngIf="post.author && post.author.fullname">{{post.author.fullname}}</h3> | ||||
|                     <p *ngIf="post.author && post.author.groups"><ng-container *ngFor="let group of post.author.groups"><ion-icon name="people"></ion-icon> {{ group.name }} </ng-container></p> | ||||
|                     <p *ngIf="post.timecreated">{{post.timecreated * 1000 | coreFormatDate: "strftimerecentfull"}}</p> | ||||
|                     <p *ngIf="!post.timecreated"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p> | ||||
|                 </div> | ||||
|                 <ng-container *ngIf="!displaySubject"> | ||||
|                     <ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate"> | ||||
|                     <ion-note float-end padding-left text-end *ngIf="trackPosts && post.unread" [attr.aria-label]="'addon.mod_forum.unread' | translate"> | ||||
|                         <core-icon name="fa-circle" color="primary"></core-icon> | ||||
|                     </ion-note> | ||||
|                     <button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled" [attr.aria-label]="('core.displayoptions' | translate)"> | ||||
| @ -33,7 +33,7 @@ | ||||
|             </div> | ||||
|         </ion-item> | ||||
|     </ion-card-header> | ||||
|     <ion-card-content [attr.padding-top]="post.parent == 0 || null"> | ||||
|     <ion-card-content [attr.padding-top]="post.parentid == 0 || null"> | ||||
|         <div padding-bottom *ngIf="post.isprivatereply"> | ||||
|             <ion-note color="danger">{{ 'addon.mod_forum.postisprivatereply' | translate }}</ion-note> | ||||
|         </div> | ||||
| @ -47,17 +47,17 @@ | ||||
|             <div item-start>{{ 'core.tag.tags' | translate }}:</div> | ||||
|             <core-tag-list [tags]="post.tags"></core-tag-list> | ||||
|         </ion-item> | ||||
|         <core-rating-rate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [itemSetId]="discussionId" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale" [userId]="post.userid" (onUpdate)="ratingUpdated()"></core-rating-rate> | ||||
|         <core-rating-rate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [itemSetId]="discussionId" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale" [userId]="post.author.id" (onUpdate)="ratingUpdated()"></core-rating-rate> | ||||
|         <core-rating-aggregate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale"></core-rating-aggregate> | ||||
| 
 | ||||
|         <ion-item no-padding text-end *ngIf="post.id && post.canreply && !post.isprivatereply" class="addon-forum-reply-button"> | ||||
|         <ion-item no-padding text-end *ngIf="post.id && post.capabilities.reply && !post.isprivatereply" class="addon-forum-reply-button"> | ||||
|             <button ion-button icon-left clear small (click)="showReplyForm()" [attr.aria-controls]="'addon-forum-reply-edit-form-' + uniqueId" [attr.aria-expanded]="replyData.replyingTo === post.id"> | ||||
|                 <core-icon name="fa-reply"></core-icon> {{ 'addon.mod_forum.reply' | translate }} | ||||
|             </button> | ||||
|         </ion-item> | ||||
|     </div> | ||||
| 
 | ||||
|     <form ion-list [id]="'addon-forum-reply-edit-form-' + uniqueId" *ngIf="(post.id && !replyData.isEditing && replyData.replyingTo == post.id) || (!post.id && replyData.isEditing && replyData.replyingTo == post.parent)" #replyFormEl> | ||||
|     <form ion-list [id]="'addon-forum-reply-edit-form-' + uniqueId" *ngIf="(post.id && !replyData.isEditing && replyData.replyingTo == post.id) || (!post.id && replyData.isEditing && replyData.replyingTo == post.parentid)" #replyFormEl> | ||||
|         <ion-item> | ||||
|             <ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label> | ||||
|             <ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject" name="subject"></ion-input> | ||||
| @ -70,13 +70,15 @@ | ||||
|             <ion-label>{{ 'addon.mod_forum.privatereply' | translate }}</ion-label> | ||||
|             <ion-checkbox item-end [(ngModel)]="replyData.isprivatereply" name="isprivatereply"></ion-checkbox> | ||||
|         </ion-item> | ||||
|         <ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable"> | ||||
|             <core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon> | ||||
|             <core-icon *ngIf="advanced" name="fa-caret-down" item-start></core-icon> | ||||
|             {{ 'addon.mod_forum.advanced' | translate }} | ||||
|         </ion-item-divider> | ||||
|         <ng-container *ngIf="advanced"> | ||||
|             <core-attachments *ngIf="forum.id && forum.maxattachments > 0" [files]="replyData.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"></core-attachments> | ||||
|         <ng-container *ngIf="forum.id && forum.maxattachments > 0"> | ||||
|             <ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable"> | ||||
|                 <core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon> | ||||
|                 <core-icon *ngIf="advanced" name="fa-caret-down" item-start></core-icon> | ||||
|                 {{ 'addon.mod_forum.advanced' | translate }} | ||||
|             </ion-item-divider> | ||||
|             <ng-container *ngIf="advanced"> | ||||
|                 <core-attachments [files]="replyData.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"></core-attachments> | ||||
|             </ng-container> | ||||
|         </ng-container> | ||||
|         <ion-grid> | ||||
|             <ion-row> | ||||
|  | ||||
| @ -43,6 +43,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|     @Input() post: any; // Post.
 | ||||
|     @Input() courseId: number; // Post's course ID.
 | ||||
|     @Input() discussionId: number; // Post's' discussion ID.
 | ||||
|     @Input() discussion?: any; // Post's' discussion, only for starting posts.
 | ||||
|     @Input() component: string; // Component this post belong to.
 | ||||
|     @Input() componentId: number; // Component ID.
 | ||||
|     @Input() replyData: any; // Object with the new post data. Usually shared between posts.
 | ||||
| @ -92,14 +93,14 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.uniqueId = this.post.id ? 'reply' + this.post.id : 'edit' + this.post.parent; | ||||
|         this.uniqueId = this.post.id ? 'reply' + this.post.id : 'edit' + this.post.parentid; | ||||
| 
 | ||||
|         const reTranslated = this.translate.instant('addon.mod_forum.re'); | ||||
|         this.displaySubject = !this.parentSubject || | ||||
|             (this.post.subject != this.parentSubject && this.post.subject != `Re: ${this.parentSubject}` && | ||||
|                 this.post.subject != `${reTranslated} ${this.parentSubject}`); | ||||
|         this.defaultReplySubject = (this.post.subject.startsWith('Re: ') || this.post.subject.startsWith(reTranslated)) | ||||
|             ? this.post.subject : `${reTranslated} ${this.post.subject}`; | ||||
|         this.defaultReplySubject = this.post.replysubject || ((this.post.subject.startsWith('Re: ') || | ||||
|             this.post.subject.startsWith(reTranslated)) ? this.post.subject : `${reTranslated} ${this.post.subject}`); | ||||
| 
 | ||||
|         this.optionsMenuEnabled = !this.post.id || (this.forumProvider.isGetDiscussionPostAvailable() && | ||||
|                     (this.forumProvider.isDeletePostAvailable() || this.forumProvider.isUpdatePostAvailable())); | ||||
| @ -328,7 +329,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|             this.syncId = this.forumSync.getDiscussionSyncId(this.discussionId); | ||||
|             this.syncProvider.blockOperation(AddonModForumProvider.COMPONENT, this.syncId); | ||||
| 
 | ||||
|             this.setReplyFormData(this.post.parent, true, this.post.subject, this.post.message, this.post.attachments, | ||||
|             this.setReplyFormData(this.post.parentid, true, this.post.subject, this.post.message, this.post.attachments, | ||||
|                     this.post.isprivatereply); | ||||
|         }).catch(() => { | ||||
|             // Cancelled.
 | ||||
| @ -460,9 +461,9 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|         this.domUtils.showDeleteConfirm().then(() => { | ||||
|             const promises = []; | ||||
| 
 | ||||
|             promises.push(this.forumOffline.deleteReply(this.post.parent)); | ||||
|             promises.push(this.forumOffline.deleteReply(this.post.parentid)); | ||||
|             if (this.forum.id) { | ||||
|                 promises.push(this.forumHelper.deleteReplyStoredFiles(this.forum.id, this.post.parent).catch(() => { | ||||
|                 promises.push(this.forumHelper.deleteReplyStoredFiles(this.forum.id, this.post.parentid).catch(() => { | ||||
|                     // Ignore errors, maybe there are no files.
 | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <ion-header> | ||||
|     <ion-navbar core-back-button> | ||||
|         <ion-title *ngIf="discussion"><core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId"></core-format-text></ion-title> | ||||
|         <ion-title *ngIf="startingPost"><core-format-text [text]="startingPost.subject" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId"></core-format-text></ion-title> | ||||
|         <ion-buttons end> | ||||
|             <!-- The context menu will be added in here. --> | ||||
|         </ion-buttons> | ||||
| @ -41,14 +41,14 @@ | ||||
|             <core-icon name="fa-lock"></core-icon> {{ 'addon.mod_forum.discussionlocked' | translate }} | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <div *ngIf="discussion" margin-bottom class="highlight"> | ||||
|             <addon-mod-forum-post [post]="discussion" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|         <div *ngIf="startingPost" margin-bottom class="highlight"> | ||||
|             <addon-mod-forum-post [post]="startingPost" [discussion]="discussion" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|         </div> | ||||
| 
 | ||||
|         <ion-card *ngIf="sort != 'nested'"> | ||||
|             <ng-container *ngFor="let post of posts; first as first"> | ||||
|                 <ion-item-divider *ngIf="!first"></ion-item-divider> | ||||
|                 <addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|                 <addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parentid]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|             </ng-container> | ||||
|         </ion-card> | ||||
| 
 | ||||
| @ -60,7 +60,7 @@ | ||||
| 
 | ||||
|         <ng-template #nestedPosts let-post="post"> | ||||
|             <ion-card> | ||||
|                 <addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parent]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|                 <addon-mod-forum-post [post]="post" [courseId]="courseId" [discussionId]="discussionId" [component]="component" [componentId]="cmId" [replyData]="replyData" [originalData]="originalData" [parentSubject]="postSubjects[post.parentid]" [forum]="forum" [accessInfo]="accessInfo" [trackPosts]="trackPosts" [ratingInfo]="ratingInfo" [leavingPage]="leavingPage" (onPostChange)="postListChanged()"></addon-mod-forum-post> | ||||
|             </ion-card> | ||||
|             <div padding-left *ngIf="post.children.length && post.children[0].subject"> | ||||
|                 <ng-container *ngFor="let child of post.children"> | ||||
|  | ||||
| @ -52,6 +52,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|     forum: any = {}; | ||||
|     accessInfo: any = {}; | ||||
|     discussion: any; | ||||
|     startingPost: any; | ||||
|     posts: any[]; | ||||
|     discussionLoaded = false; | ||||
|     postSubjects: { [id: string]: string }; | ||||
| @ -253,7 +254,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|                     } | ||||
| 
 | ||||
|                     if (typeof data.deleted != 'undefined' && data.deleted) { | ||||
|                         if (data.post.parent == 0) { | ||||
|                         if (!data.post.parentid) { | ||||
|                             if (this.svComponent && this.svComponent.isOn()) { | ||||
|                                 this.svComponent.emptyDetails(); | ||||
|                             } else { | ||||
| @ -331,6 +332,8 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|             return this.forumProvider.getDiscussionPosts(this.discussionId, this.cmId).then((response) => { | ||||
|                 onlinePosts = response.posts; | ||||
|                 ratingInfo = response.ratinginfo; | ||||
|                 this.courseId = response.courseid || this.courseId; | ||||
|                 this.forumId = response.forumid || this.forumId; | ||||
|             }).then(() => { | ||||
|                 // Check if there are responses stored in offline.
 | ||||
|                 return this.forumOffline.getDiscussionReplies(this.discussionId).then((replies) => { | ||||
| @ -341,7 +344,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|                     const posts = {}; | ||||
|                     onlinePosts.forEach((post) => { | ||||
|                         posts[post.id] = post; | ||||
|                         hasUnreadPosts = hasUnreadPosts || !post.postread; | ||||
|                         hasUnreadPosts = hasUnreadPosts || !!post.unread; | ||||
|                     }); | ||||
| 
 | ||||
|                     replies.forEach((offlineReply) => { | ||||
| @ -370,18 +373,15 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|         }).then(() => { | ||||
|             let posts = offlineReplies.concat(onlinePosts); | ||||
| 
 | ||||
|             const startingPost = this.forumProvider.extractStartingPost(posts); | ||||
|             if (startingPost) { | ||||
|                 // Update discussion data from first post.
 | ||||
|                 this.discussion = Object.assign(this.discussion || {}, startingPost); | ||||
|             } | ||||
|             this.startingPost = this.forumProvider.extractStartingPost(posts); | ||||
| 
 | ||||
|             // 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'); | ||||
| 
 | ||||
|                 posts = this.utils.formatTree(posts, 'parent', 'id', this.discussion.id); | ||||
|                 const rootId = this.startingPost ? this.startingPost.id : (this.discussion ? this.discussion.id : 0); | ||||
|                 posts = this.utils.formatTree(posts, 'parentid', 'id', rootId); | ||||
|             } else { | ||||
|                 // Set default reply subject.
 | ||||
|                 const direction = this.sort == 'flat-newest' ? 'DESC' : 'ASC'; | ||||
| @ -410,7 +410,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|                     // Just in case the posts were fetched from WS when the cut-off date was not reached but it is now.
 | ||||
|                     if (this.forumHelper.isCutoffDateReached(forum) && !accessInfo.cancanoverridecutoff) { | ||||
|                         posts.forEach((post) => { | ||||
|                             post.canreply = false; | ||||
|                             post.capabilities.reply = false; | ||||
|                         }); | ||||
|                     } | ||||
|                 })); | ||||
| @ -424,24 +424,26 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
|             }).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }).then(() => { | ||||
| 
 | ||||
|                 if (!this.discussion) { | ||||
|                 if (!this.discussion && !this.startingPost) { | ||||
|                     // The discussion object was not passed as parameter and there is no starting post. Should not happen.
 | ||||
|                     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; | ||||
|                 if (this.startingPost.author && this.forum.type == 'single') { | ||||
|                     // Hide author and groups for first post and type single.
 | ||||
|                     this.startingPost.author.fullname = null; | ||||
|                     this.startingPost.author.groups = null; | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|                 this.posts = posts; | ||||
|                 this.ratingInfo = ratingInfo; | ||||
| 
 | ||||
|                 this.postSubjects = this.getAllPosts().reduce((postSubjects, post) => { | ||||
|                     postSubjects[post.id] = post.subject; | ||||
| 
 | ||||
|                     return postSubjects; | ||||
|                 }, { [this.discussion.id]: this.discussion.subject }); | ||||
|                 }, { [this.startingPost.id]: this.startingPost.subject }); | ||||
|             }); | ||||
|         }).then(() => { | ||||
|             if (this.forumProvider.isSetPinStateAvailableForSite()) { | ||||
| @ -746,5 +748,4 @@ export class AddonModForumDiscussionPage implements OnDestroy { | ||||
| 
 | ||||
|         return posts; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -280,7 +280,7 @@ export class AddonModForumProvider { | ||||
|      * @return Starting post or undefined if not found. | ||||
|      */ | ||||
|     extractStartingPost(posts: any[]): any { | ||||
|         const index = posts.findIndex((post) => post.parent == 0); | ||||
|         const index = posts.findIndex((post) => !post.parentid); | ||||
| 
 | ||||
|         return index >= 0 ? posts.splice(index, 1).pop() : undefined; | ||||
|     } | ||||
| @ -305,6 +305,18 @@ export class AddonModForumProvider { | ||||
|         return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_get_discussion_post'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns whether or not getDiscussionPost WS available or not. | ||||
|      * | ||||
|      * @param site Site. If not defined, current site. | ||||
|      * @return If WS is avalaible. | ||||
|      * @since 3.7 | ||||
|      */ | ||||
|     isGetDiscussionPostsAvailable(site?: CoreSite): boolean { | ||||
|         return site ? site.wsAvailable('mod_forum_get_discussion_posts') : | ||||
|             this.sitesProvider.wsAvailableInCurrentSite('mod_forum_get_discussion_posts'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns whether or not deletePost WS available or not. | ||||
|      * | ||||
| @ -496,7 +508,43 @@ export class AddonModForumProvider { | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with forum posts and rating info. | ||||
|      */ | ||||
|     getDiscussionPosts(discussionId: number, cmId: number, siteId?: string): Promise<{posts: any[], ratinginfo?: CoreRatingInfo}> { | ||||
|     getDiscussionPosts(discussionId: number, cmId: number, siteId?: string): Promise<{posts: any[], courseid?: number, | ||||
|             forumid?: number, ratinginfo?: CoreRatingInfo}> { | ||||
| 
 | ||||
|         // Convenience function to translate legacy data to new format.
 | ||||
|         const translateLegacyPostsFormat = (posts: any[]): any[] => { | ||||
|             return posts.map((post) => { | ||||
|                 const newPost = { | ||||
|                     id: post.id , | ||||
|                     discussionid: post.discussion, | ||||
|                     parentid: post.parent, | ||||
|                     hasparent: !!post.parent, | ||||
|                     author: { | ||||
|                         id: post.userid, | ||||
|                         fullname: post.userfullname, | ||||
|                         urls: { profileimage: post.userpictureurl }, | ||||
|                     }, | ||||
|                     timecreated: post.created, | ||||
|                     subject: post.subject, | ||||
|                     message: post.message, | ||||
|                     attachments : post.attachments, | ||||
|                     capabilities: { | ||||
|                         reply: !!post.canreply, | ||||
|                     }, | ||||
| 
 | ||||
|                     unread: !post.postread, | ||||
|                     isprivatereply: !!post.isprivatereply, | ||||
|                     tags: post.tags | ||||
|                 }; | ||||
| 
 | ||||
|                 if (post.groupname) { | ||||
|                     newPost.author['groups'] = [{name: post.groupname}]; | ||||
|                 } | ||||
| 
 | ||||
|                 return newPost; | ||||
|             }); | ||||
|         }; | ||||
| 
 | ||||
|         const params = { | ||||
|             discussionid: discussionId | ||||
|         }; | ||||
| @ -507,8 +555,15 @@ export class AddonModForumProvider { | ||||
|         }; | ||||
| 
 | ||||
|         return this.sitesProvider.getSite(siteId).then((site) => { | ||||
|             return site.read('mod_forum_get_forum_discussion_posts', params, preSets).then((response) => { | ||||
|             const wsName = this.isGetDiscussionPostsAvailable(site) ? 'mod_forum_get_discussion_posts' : | ||||
|                 'mod_forum_get_forum_discussion_posts'; | ||||
| 
 | ||||
|             return site.read(wsName, params, preSets).then((response) => { | ||||
|                 if (response) { | ||||
| 
 | ||||
|                     if (wsName == 'mod_forum_get_forum_discussion_posts') { | ||||
|                         response.posts = translateLegacyPostsFormat(response.posts); | ||||
|                     } | ||||
|                     this.storeUserData(response.posts); | ||||
| 
 | ||||
|                     return response; | ||||
| @ -1053,6 +1108,16 @@ export class AddonModForumProvider { | ||||
|         const users = {}; | ||||
| 
 | ||||
|         list.forEach((entry) => { | ||||
|             if (entry.author) { | ||||
|                 const authorId = parseInt(entry.author.id); | ||||
|                 if (!isNaN(authorId) && !users[authorId]) { | ||||
|                     users[authorId] = { | ||||
|                         id: entry.author.id, | ||||
|                         fullname: entry.author.fullname, | ||||
|                         profileimageurl: entry.author.urls.profileimage | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|             const userId = parseInt(entry.userid); | ||||
|             if (!isNaN(userId) && !users[userId]) { | ||||
|                 users[userId] = { | ||||
|  | ||||
| @ -87,7 +87,7 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { | ||||
|      */ | ||||
|     protected setFields(): void { | ||||
|         const profileUrl = this.profileUrl || (this.user && (this.user.profileimageurl || this.user.userprofileimageurl || | ||||
|             this.user.userpictureurl || this.user.profileimageurlsmall)); | ||||
|             this.user.userpictureurl || this.user.profileimageurlsmall || (this.user.urls && this.user.urls.profileimage)); | ||||
| 
 | ||||
|         if (typeof profileUrl == 'string') { | ||||
|             this.avatarUrl = profileUrl; | ||||
|  | ||||
| @ -228,7 +228,7 @@ export class CoreEditorOfflineProvider { | ||||
| 
 | ||||
|         if (entry) { | ||||
|             if (entry.pageinstance != pageInstance) { | ||||
|                 this.logger.warning(`Discarding draft because of pageinstance. Context '${contextLevel}' '${contextInstanceId}', ` + | ||||
|                 this.logger.warn(`Discarding draft because of pageinstance. Context '${contextLevel}' '${contextInstanceId}', ` + | ||||
|                         `element '${elementId}'`); | ||||
|                 throw null; | ||||
|             } | ||||
|  | ||||
| @ -166,7 +166,7 @@ export class CoreMimetypeUtilsProvider { | ||||
|         if (this.canBeEmbedded(ext)) { | ||||
|             file.embedType = this.getExtensionType(ext); | ||||
| 
 | ||||
|             path = CoreFile.instance.convertFileSrc(path || file.fileurl || (file.toURL && file.toURL())); | ||||
|             path = CoreFile.instance.convertFileSrc(path || file.fileurl || file.url || (file.toURL && file.toURL())); | ||||
| 
 | ||||
|             if (file.embedType == 'image') { | ||||
|                 return '<img src="' + path + '">'; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user