forked from CIT/Vmeda.Online
		
	MOBILE-3643 forum: Migrate new discussion form
This commit is contained in:
		
							parent
							
								
									fbad712136
								
							
						
					
					
						commit
						d4bc77b386
					
				@ -40,6 +40,14 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
				
			|||||||
import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu';
 | 
					import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu';
 | 
				
			||||||
import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
 | 
					import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Type to use for selecting new discussion form in the discussions manager.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					type NewDiscussionForm = {
 | 
				
			||||||
 | 
					    newDiscussion: true;
 | 
				
			||||||
 | 
					    timeCreated: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Component that displays a forum entry page.
 | 
					 * Component that displays a forum entry page.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -193,17 +201,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
				
			|||||||
                .getForum(this.courseId, this.module.id)
 | 
					                .getForum(this.courseId, this.module.id)
 | 
				
			||||||
                .then(async (forum) => {
 | 
					                .then(async (forum) => {
 | 
				
			||||||
                    this.forum = forum;
 | 
					                    this.forum = forum;
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    this.description = forum.intro || this.description;
 | 
					                    this.description = forum.intro || this.description;
 | 
				
			||||||
 | 
					                    this.availabilityMessage = AddonModForumHelper.instance.getAvailabilityMessage(forum);
 | 
				
			||||||
                    this.descriptionNote = Translate.instant('addon.mod_forum.numdiscussions', {
 | 
					                    this.descriptionNote = Translate.instant('addon.mod_forum.numdiscussions', {
 | 
				
			||||||
                        numdiscussions: forum.numdiscussions,
 | 
					                        numdiscussions: forum.numdiscussions,
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (typeof forum.istracked != 'undefined') {
 | 
					                    if (typeof forum.istracked != 'undefined') {
 | 
				
			||||||
                        this.trackPosts = forum.istracked;
 | 
					                        this.trackPosts = forum.istracked;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    this.availabilityMessage = AddonModForumHelper.instance.getAvailabilityMessage(forum);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    this.dataRetrieved.emit(forum);
 | 
					                    this.dataRetrieved.emit(forum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    switch (forum.type) {
 | 
					                    switch (forum.type) {
 | 
				
			||||||
@ -218,24 +225,20 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
				
			|||||||
                            this.addDiscussionText = Translate.instant('addon.mod_forum.addanewdiscussion');
 | 
					                            this.addDiscussionText = Translate.instant('addon.mod_forum.addanewdiscussion');
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!sync) {
 | 
					                    if (sync) {
 | 
				
			||||||
                        return;
 | 
					                        // Try to synchronize the forum.
 | 
				
			||||||
 | 
					                        const updated = await this.syncActivity(showErrors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (updated) {
 | 
				
			||||||
 | 
					                            // Sync successful, send event.
 | 
				
			||||||
 | 
					                            CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
				
			||||||
 | 
					                                forumId: forum.id,
 | 
				
			||||||
 | 
					                                userId: CoreSites.instance.getCurrentSiteUserId(),
 | 
				
			||||||
 | 
					                                source: 'index',
 | 
				
			||||||
 | 
					                            }, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Try to synchronize the forum.
 | 
					 | 
				
			||||||
                    const updated = await this.syncActivity(showErrors);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (!updated) {
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Sync successful, send event.
 | 
					 | 
				
			||||||
                    CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
					 | 
				
			||||||
                        forumId: forum.id,
 | 
					 | 
				
			||||||
                        userId: CoreSites.instance.getCurrentSiteUserId(),
 | 
					 | 
				
			||||||
                        source: 'index',
 | 
					 | 
				
			||||||
                    }, CoreSites.instance.getCurrentSiteId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    const promises: Promise<void>[] = [];
 | 
					                    const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check if the activity uses groups.
 | 
					                    // Check if the activity uses groups.
 | 
				
			||||||
@ -507,18 +510,10 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
				
			|||||||
     * @param timeCreated Creation time of the offline discussion.
 | 
					     * @param timeCreated Creation time of the offline discussion.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    openNewDiscussion(timeCreated: number = 0): void {
 | 
					    openNewDiscussion(timeCreated: number = 0): void {
 | 
				
			||||||
        alert(`Open new discussion at ${timeCreated} not implemented!`);
 | 
					        this.discussions.select({
 | 
				
			||||||
 | 
					            newDiscussion: true,
 | 
				
			||||||
        // @todo
 | 
					            timeCreated,
 | 
				
			||||||
        // const params = {
 | 
					        });
 | 
				
			||||||
        //     courseId: this.courseId,
 | 
					 | 
				
			||||||
        //     cmId: this.module.id,
 | 
					 | 
				
			||||||
        //     forumId: this.forum.id,
 | 
					 | 
				
			||||||
        //     timeCreated: timeCreated,
 | 
					 | 
				
			||||||
        // };
 | 
					 | 
				
			||||||
        // this.splitviewCtrl.push('AddonModForumNewDiscussionPage', params);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.selectedDiscussion = 0;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -598,7 +593,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModForumDiscussion> {
 | 
					class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModForumDiscussion | NewDiscussionForm> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private discussionsPathPrefix: string;
 | 
					    private discussionsPathPrefix: string;
 | 
				
			||||||
    private component: AddonModForumIndexComponent;
 | 
					    private component: AddonModForumIndexComponent;
 | 
				
			||||||
@ -610,18 +605,21 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModF
 | 
				
			|||||||
        this.discussionsPathPrefix = discussionsPathPrefix;
 | 
					        this.discussionsPathPrefix = discussionsPathPrefix;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getItemQueryParams(discussion: AddonModForumDiscussion): Params {
 | 
					    getItemQueryParams(discussion: AddonModForumDiscussion | NewDiscussionForm): Params {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            discussion,
 | 
					 | 
				
			||||||
            courseId: this.component.courseId,
 | 
					            courseId: this.component.courseId,
 | 
				
			||||||
            cmId: this.component.module!.id,
 | 
					            cmId: this.component.module!.id,
 | 
				
			||||||
            forumId: this.component.forum!.id,
 | 
					            forumId: this.component.forum!.id,
 | 
				
			||||||
            trackPosts: this.component.trackPosts,
 | 
					            ...(
 | 
				
			||||||
 | 
					                this.isNewDiscussionForm(discussion)
 | 
				
			||||||
 | 
					                    ? { timeCreated: discussion.timeCreated }
 | 
				
			||||||
 | 
					                    : { discussion, trackPosts: this.component.trackPosts }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected getItemPath(discussion: AddonModForumDiscussion): string {
 | 
					    protected getItemPath(discussion: AddonModForumDiscussion | NewDiscussionForm): string {
 | 
				
			||||||
        const discussionId = discussion.id;
 | 
					        const discussionId = this.isNewDiscussionForm(discussion) ? 'new' : discussion.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this.discussionsPathPrefix + discussionId;
 | 
					        return this.discussionsPathPrefix + discussionId;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -632,4 +630,8 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModF
 | 
				
			|||||||
        return discussionId ? this.discussionsPathPrefix + discussionId : null;
 | 
					        return discussionId ? this.discussionsPathPrefix + discussionId : null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private isNewDiscussionForm(discussion: AddonModForumDiscussion | NewDiscussionForm): discussion is NewDiscussionForm {
 | 
				
			||||||
 | 
					        return 'newDiscussion' in discussion;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,21 +16,26 @@ import { NgModule } from '@angular/core';
 | 
				
			|||||||
import { RouterModule, Routes } from '@angular/router';
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AddonModForumComponentsModule } from './components/components.module';
 | 
					import { AddonModForumComponentsModule } from './components/components.module';
 | 
				
			||||||
import { AddonModForumIndexPage } from './pages/index';
 | 
					import { AddonModForumIndexPage } from './pages/index';
 | 
				
			||||||
import { conditionalRoutes } from '@/app/app-routing.module';
 | 
					import { conditionalRoutes } from '@/app/app-routing.module';
 | 
				
			||||||
import { CoreScreen } from '@services/screen';
 | 
					import { CoreScreen } from '@services/screen';
 | 
				
			||||||
 | 
					import { AddonModForumNewDiscussionPage } from './pages/new-discussion/new-discussion';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mobileRoutes: Routes = [
 | 
					const mobileRoutes: Routes = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        path: ':courseId/:cmId',
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
        component: AddonModForumIndexPage,
 | 
					        component: AddonModForumIndexPage,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/new',
 | 
				
			||||||
 | 
					        component: AddonModForumNewDiscussionPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        path: ':courseId/:cmId/:discussionId',
 | 
					        path: ':courseId/:cmId/:discussionId',
 | 
				
			||||||
        loadChildren: () => import('./pages/discussion/discussion.module').then(m => m.AddonForumDiscussionPageModule),
 | 
					        loadChildren: () => import('./pages/discussion/discussion.module').then(m => m.AddonForumDiscussionPageModule),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,6 +44,10 @@ const tabletRoutes: Routes = [
 | 
				
			|||||||
        path: ':courseId/:cmId',
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
        component: AddonModForumIndexPage,
 | 
					        component: AddonModForumIndexPage,
 | 
				
			||||||
        children: [
 | 
					        children: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                path: 'new',
 | 
				
			||||||
 | 
					                component: AddonModForumNewDiscussionPage,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                path: ':discussionId',
 | 
					                path: ':discussionId',
 | 
				
			||||||
                loadChildren: () => import('./pages/discussion/discussion.module').then(m => m.AddonForumDiscussionPageModule),
 | 
					                loadChildren: () => import('./pages/discussion/discussion.module').then(m => m.AddonForumDiscussionPageModule),
 | 
				
			||||||
@ -58,9 +67,11 @@ const routes: Routes = [
 | 
				
			|||||||
        RouterModule.forChild(routes),
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
        CoreSharedModule,
 | 
					        CoreSharedModule,
 | 
				
			||||||
        AddonModForumComponentsModule,
 | 
					        AddonModForumComponentsModule,
 | 
				
			||||||
 | 
					        CoreEditorComponentsModule,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    declarations: [
 | 
					    declarations: [
 | 
				
			||||||
        AddonModForumIndexPage,
 | 
					        AddonModForumIndexPage,
 | 
				
			||||||
 | 
					        AddonModForumNewDiscussionPage,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModForumLazyModule {}
 | 
					export class AddonModForumLazyModule {}
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>{{ 'addon.mod_forum.addanewdiscussion' | translate }}</ion-title>
 | 
				
			||||||
 | 
					        <ion-buttons slot="end">
 | 
				
			||||||
 | 
					            <!-- The context menu will be added in here. -->
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <ion-refresher slot="fixed" [disabled]="!groupsLoaded" (ionRefresh)="refreshGroups($event.target)">
 | 
				
			||||||
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <core-loading [hideUntil]="groupsLoaded">
 | 
				
			||||||
 | 
					        <form ion-list *ngIf="showForm" #newDiscFormEl>
 | 
				
			||||||
 | 
					            <ion-item>
 | 
				
			||||||
 | 
					                <ion-label position="stacked">{{ 'addon.mod_forum.subject' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                <ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="newDiscussion.subject" name="subject"></ion-input>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					            <ion-item>
 | 
				
			||||||
 | 
					                <ion-label position="stacked">{{ 'addon.mod_forum.message' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                <core-rich-text-editor item-content name="addon_mod_forum_new_discussion" contextLevel="module" elementId="message"
 | 
				
			||||||
 | 
					                    [control]="messageControl" [placeholder]="'addon.mod_forum.message' | translate" [component]="component"
 | 
				
			||||||
 | 
					                    [componentId]="forum.cmid" [autoSave]="true" [contextInstanceId]="forum.cmid"
 | 
				
			||||||
 | 
					                    (contentChanged)="onMessageChange($event)">
 | 
				
			||||||
 | 
					                </core-rich-text-editor>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					            <ion-item-divider class="ion-text-wrap core-expandable" (click)="toggleAdvanced()">
 | 
				
			||||||
 | 
					                <ion-icon *ngIf="!advanced" name="fa-caret-right" slot="start">
 | 
				
			||||||
 | 
					                </ion-icon>
 | 
				
			||||||
 | 
					                <ion-icon *ngIf="advanced" name="fa-caret-down" slot="start">
 | 
				
			||||||
 | 
					                </ion-icon>
 | 
				
			||||||
 | 
					                <ion-label>{{ 'addon.mod_forum.advanced' | translate }}</ion-label>
 | 
				
			||||||
 | 
					            </ion-item-divider>
 | 
				
			||||||
 | 
					            <ng-container *ngIf="advanced">
 | 
				
			||||||
 | 
					                <ion-item *ngIf="showGroups && groupIds.length > 1 && accessInfo.cancanposttomygroups">
 | 
				
			||||||
 | 
					                    <ion-label>{{ 'addon.mod_forum.posttomygroups' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                    <ion-toggle [(ngModel)]="newDiscussion.postToAllGroups" name="postallgroups"></ion-toggle>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item *ngIf="showGroups">
 | 
				
			||||||
 | 
					                    <ion-label id="addon-mod-forum-groupslabel">{{ 'addon.mod_forum.group' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                    <ion-select [(ngModel)]="newDiscussion.groupId" [disabled]="newDiscussion.postToAllGroups"
 | 
				
			||||||
 | 
					                        aria-labelledby="addon-mod-forum-groupslabel" interface="action-sheet" name="groupid">
 | 
				
			||||||
 | 
					                        <ion-select-option *ngFor="let group of groups" [value]="group.id">{{ group.name }}</ion-select-option>
 | 
				
			||||||
 | 
					                    </ion-select>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item>
 | 
				
			||||||
 | 
					                    <ion-label>{{ 'addon.mod_forum.discussionsubscription' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                    <ion-toggle [(ngModel)]="newDiscussion.subscribe" name="subscribe"></ion-toggle>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item *ngIf="canPin">
 | 
				
			||||||
 | 
					                    <ion-label>{{ 'addon.mod_forum.discussionpinned' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                    <ion-toggle [(ngModel)]="newDiscussion.pin" name="pin"></ion-toggle>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <core-attachments *ngIf="canCreateAttachments && forum && forum.maxattachments > 0"
 | 
				
			||||||
 | 
					                    [files]="newDiscussion.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments"
 | 
				
			||||||
 | 
					                    [component]="component" [componentId]="forum.cmid" [allowOffline]="true">
 | 
				
			||||||
 | 
					                </core-attachments>
 | 
				
			||||||
 | 
					            </ng-container>
 | 
				
			||||||
 | 
					            <ion-item>
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <ion-row>
 | 
				
			||||||
 | 
					                        <ion-col>
 | 
				
			||||||
 | 
					                            <ion-button expand="block" (click)="add()" [disabled]="newDiscussion.subject == '' || newDiscussion.message == null">
 | 
				
			||||||
 | 
					                                {{ 'addon.mod_forum.posttoforum' | translate }}
 | 
				
			||||||
 | 
					                            </ion-button>
 | 
				
			||||||
 | 
					                        </ion-col>
 | 
				
			||||||
 | 
					                        <ion-col *ngIf="hasOffline">
 | 
				
			||||||
 | 
					                            <ion-button expand="block" color="light" (click)="discard()">{{ 'core.discard' | translate }}</ion-button>
 | 
				
			||||||
 | 
					                        </ion-col>
 | 
				
			||||||
 | 
					                    </ion-row>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </core-loading>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										594
									
								
								src/addons/mod/forum/pages/new-discussion/new-discussion.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										594
									
								
								src/addons/mod/forum/pages/new-discussion/new-discussion.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,594 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Component, OnDestroy, ViewChild, ElementRef, OnInit } from '@angular/core';
 | 
				
			||||||
 | 
					import { FileEntry } from '@ionic-native/file/ngx';
 | 
				
			||||||
 | 
					import { FormControl } from '@angular/forms';
 | 
				
			||||||
 | 
					import { CoreEvents, CoreEventObserver } from '@singletons/events';
 | 
				
			||||||
 | 
					import { CoreGroup, CoreGroups, CoreGroupsProvider } from '@services/groups';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModForum,
 | 
				
			||||||
 | 
					    AddonModForumAccessInformation,
 | 
				
			||||||
 | 
					    AddonModForumCanAddDiscussion,
 | 
				
			||||||
 | 
					    AddonModForumData,
 | 
				
			||||||
 | 
					    AddonModForumProvider,
 | 
				
			||||||
 | 
					} from '@addons/mod/forum/services/forum.service';
 | 
				
			||||||
 | 
					import { CoreEditorRichTextEditorComponent } from '@features/editor/components/rich-text-editor/rich-text-editor';
 | 
				
			||||||
 | 
					import { AddonModForumSync, AddonModForumSyncProvider } from '@addons/mod/forum/services/sync.service';
 | 
				
			||||||
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreSync } from '@services/sync';
 | 
				
			||||||
 | 
					import { AddonModForumDiscussionOptions, AddonModForumOffline } from '@addons/mod/forum/services/offline.service';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { AddonModForumHelper } from '@addons/mod/forum/services/helper.service';
 | 
				
			||||||
 | 
					import { IonRefresher } from '@ionic/angular';
 | 
				
			||||||
 | 
					import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
 | 
				
			||||||
 | 
					import { CoreTextUtils } from '@services/utils/text';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NewDiscussionData = {
 | 
				
			||||||
 | 
					    subject: string;
 | 
				
			||||||
 | 
					    message: string | null; // Null means empty or just white space.
 | 
				
			||||||
 | 
					    postToAllGroups: boolean;
 | 
				
			||||||
 | 
					    groupId: number;
 | 
				
			||||||
 | 
					    subscribe: boolean;
 | 
				
			||||||
 | 
					    pin: boolean;
 | 
				
			||||||
 | 
					    files: FileEntry[];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays the new discussion form.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-forum-new-discussion',
 | 
				
			||||||
 | 
					    templateUrl: 'new-discussion.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild('newDiscFormEl') formElement!: ElementRef;
 | 
				
			||||||
 | 
					    @ViewChild(CoreEditorRichTextEditorComponent) messageEditor!: CoreEditorRichTextEditorComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    component = AddonModForumProvider.COMPONENT;
 | 
				
			||||||
 | 
					    messageControl = new FormControl();
 | 
				
			||||||
 | 
					    groupsLoaded = false;
 | 
				
			||||||
 | 
					    showGroups = false;
 | 
				
			||||||
 | 
					    hasOffline = false;
 | 
				
			||||||
 | 
					    canCreateAttachments = true; // Assume we can by default.
 | 
				
			||||||
 | 
					    canPin = false;
 | 
				
			||||||
 | 
					    forum!: AddonModForumData;
 | 
				
			||||||
 | 
					    showForm = false;
 | 
				
			||||||
 | 
					    groups: CoreGroup[] = [];
 | 
				
			||||||
 | 
					    groupIds: number[] = [];
 | 
				
			||||||
 | 
					    newDiscussion: NewDiscussionData = {
 | 
				
			||||||
 | 
					        subject: '',
 | 
				
			||||||
 | 
					        message: null,
 | 
				
			||||||
 | 
					        postToAllGroups: false,
 | 
				
			||||||
 | 
					        groupId: 0,
 | 
				
			||||||
 | 
					        subscribe: true,
 | 
				
			||||||
 | 
					        pin: false,
 | 
				
			||||||
 | 
					        files: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    advanced = false; // Display all form fields.
 | 
				
			||||||
 | 
					    accessInfo: AddonModForumAccessInformation = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected courseId!: number;
 | 
				
			||||||
 | 
					    protected cmId!: number;
 | 
				
			||||||
 | 
					    protected forumId!: number;
 | 
				
			||||||
 | 
					    protected timeCreated!: number;
 | 
				
			||||||
 | 
					    protected syncId!: string;
 | 
				
			||||||
 | 
					    protected syncObserver?: CoreEventObserver;
 | 
				
			||||||
 | 
					    protected isDestroyed = false;
 | 
				
			||||||
 | 
					    protected originalData?: Partial<NewDiscussionData>;
 | 
				
			||||||
 | 
					    protected forceLeave = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Component being initialized.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnInit(): void {
 | 
				
			||||||
 | 
					        this.courseId = CoreNavigator.instance.getRouteNumberParam('courseId')!;
 | 
				
			||||||
 | 
					        this.cmId = CoreNavigator.instance.getRouteNumberParam('cmId')!;
 | 
				
			||||||
 | 
					        this.forumId = CoreNavigator.instance.getRouteNumberParam('forumId')!;
 | 
				
			||||||
 | 
					        this.timeCreated = CoreNavigator.instance.getRouteNumberParam('timeCreated')!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.fetchDiscussionData().finally(() => {
 | 
				
			||||||
 | 
					            this.groupsLoaded = true;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User entered the page that contains the component.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidEnter(): void {
 | 
				
			||||||
 | 
					        if (this.syncObserver) {
 | 
				
			||||||
 | 
					            // Already setup.
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Refresh data if this discussion is synchronized automatically.
 | 
				
			||||||
 | 
					        this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, (data: any) => {
 | 
				
			||||||
 | 
					            if (data.forumId == this.forumId && data.userId == CoreSites.instance.getCurrentSiteUserId()) {
 | 
				
			||||||
 | 
					                CoreDomUtils.instance.showAlertTranslated('core.notice', 'core.contenteditingsynced');
 | 
				
			||||||
 | 
					                this.returnToDiscussions();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Trigger view event, to highlight the current opened discussion in the split view.
 | 
				
			||||||
 | 
					        CoreEvents.trigger(AddonModForumProvider.VIEW_DISCUSSION_EVENT, {
 | 
				
			||||||
 | 
					            forumId: this.forumId,
 | 
				
			||||||
 | 
					            discussion: -this.timeCreated,
 | 
				
			||||||
 | 
					        }, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch if forum uses groups and the groups it uses.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresh Whether we're refreshing data.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchDiscussionData(refresh?: boolean): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const mode = await CoreGroups.instance.getActivityGroupMode(this.cmId);
 | 
				
			||||||
 | 
					            const promises: Promise<unknown>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS) {
 | 
				
			||||||
 | 
					                promises.push(
 | 
				
			||||||
 | 
					                    CoreGroups.instance
 | 
				
			||||||
 | 
					                        .getActivityAllowedGroups(this.cmId)
 | 
				
			||||||
 | 
					                        .then((result) => {
 | 
				
			||||||
 | 
					                            let promise;
 | 
				
			||||||
 | 
					                            if (mode === CoreGroupsProvider.VISIBLEGROUPS) {
 | 
				
			||||||
 | 
					                                // We need to check which of the returned groups the user can post to.
 | 
				
			||||||
 | 
					                                promise = this.validateVisibleGroups(result.groups);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                // WS already filters groups, no need to do it ourselves. Add "All participants" if needed.
 | 
				
			||||||
 | 
					                                promise = this.addAllParticipantsOption(result.groups, true);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // eslint-disable-next-line promise/no-nesting
 | 
				
			||||||
 | 
					                            return promise.then((forumGroups) => {
 | 
				
			||||||
 | 
					                                if (forumGroups.length > 0) {
 | 
				
			||||||
 | 
					                                    this.groups = forumGroups;
 | 
				
			||||||
 | 
					                                    this.groupIds = forumGroups.map((group) => group.id).filter((id) => id > 0);
 | 
				
			||||||
 | 
					                                    // Do not override group id.
 | 
				
			||||||
 | 
					                                    this.newDiscussion.groupId = this.newDiscussion.groupId || forumGroups[0].id;
 | 
				
			||||||
 | 
					                                    this.showGroups = true;
 | 
				
			||||||
 | 
					                                    if (this.groupIds.length <= 1) {
 | 
				
			||||||
 | 
					                                        this.newDiscussion.postToAllGroups = false;
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    return;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    const message = mode === CoreGroupsProvider.SEPARATEGROUPS ?
 | 
				
			||||||
 | 
					                                        'addon.mod_forum.cannotadddiscussionall' : 'addon.mod_forum.cannotadddiscussion';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    throw new Error(Translate.instant(message));
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.showGroups = false;
 | 
				
			||||||
 | 
					                this.newDiscussion.postToAllGroups = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Use the canAddDiscussion WS to check if the user can add attachments and pin discussions.
 | 
				
			||||||
 | 
					                promises.push(
 | 
				
			||||||
 | 
					                    CoreUtils.instance.ignoreErrors(
 | 
				
			||||||
 | 
					                        AddonModForum.instance
 | 
				
			||||||
 | 
					                            .canAddDiscussionToAll(this.forumId, { cmId: this.cmId })
 | 
				
			||||||
 | 
					                            .then((response) => {
 | 
				
			||||||
 | 
					                                this.canPin = !!response.canpindiscussions;
 | 
				
			||||||
 | 
					                                this.canCreateAttachments = !!response.cancreateattachment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                return;
 | 
				
			||||||
 | 
					                            }),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get forum.
 | 
				
			||||||
 | 
					            promises.push(AddonModForum.instance.getForum(this.courseId, this.cmId).then((forum) => this.forum = forum));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get access information.
 | 
				
			||||||
 | 
					            promises.push(
 | 
				
			||||||
 | 
					                AddonModForum.instance
 | 
				
			||||||
 | 
					                    .getAccessInformation(this.forumId, { cmId: this.cmId })
 | 
				
			||||||
 | 
					                    .then((accessInfo) => this.accessInfo = accessInfo),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If editing a discussion, get offline data.
 | 
				
			||||||
 | 
					            if (this.timeCreated && !refresh) {
 | 
				
			||||||
 | 
					                this.syncId = AddonModForumSync.instance.getForumSyncId(this.forumId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await AddonModForumSync.instance.waitForSync(this.syncId).then(() => {
 | 
				
			||||||
 | 
					                    // Do not block if the scope is already destroyed.
 | 
				
			||||||
 | 
					                    if (!this.isDestroyed) {
 | 
				
			||||||
 | 
					                        CoreSync.instance.blockOperation(AddonModForumProvider.COMPONENT, this.syncId);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // eslint-disable-next-line promise/no-nesting
 | 
				
			||||||
 | 
					                    return AddonModForumOffline.instance
 | 
				
			||||||
 | 
					                        .getNewDiscussion(this.forumId, this.timeCreated)
 | 
				
			||||||
 | 
					                        .then(async (discussion) => {
 | 
				
			||||||
 | 
					                            this.hasOffline = true;
 | 
				
			||||||
 | 
					                            discussion.options = discussion.options || {};
 | 
				
			||||||
 | 
					                            if (discussion.groupid == AddonModForumProvider.ALL_GROUPS) {
 | 
				
			||||||
 | 
					                                this.newDiscussion.groupId = this.groups[0].id;
 | 
				
			||||||
 | 
					                                this.newDiscussion.postToAllGroups = true;
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                this.newDiscussion.groupId = discussion.groupid;
 | 
				
			||||||
 | 
					                                this.newDiscussion.postToAllGroups = false;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            this.newDiscussion.subject = discussion.subject;
 | 
				
			||||||
 | 
					                            this.newDiscussion.message = discussion.message;
 | 
				
			||||||
 | 
					                            this.newDiscussion.subscribe = !!discussion.options.discussionsubscribe;
 | 
				
			||||||
 | 
					                            this.newDiscussion.pin = !!discussion.options.discussionpinned;
 | 
				
			||||||
 | 
					                            this.messageControl.setValue(discussion.message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // Treat offline attachments if any.
 | 
				
			||||||
 | 
					                            if (typeof discussion.options.attachmentsid === 'object' && discussion.options.attachmentsid.offline) {
 | 
				
			||||||
 | 
					                                const files = await AddonModForumHelper.instance.getNewDiscussionStoredFiles(
 | 
				
			||||||
 | 
					                                    this.forumId,
 | 
				
			||||||
 | 
					                                    this.timeCreated,
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                this.newDiscussion.files = files;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // Show advanced fields by default if any of them has not the default value.
 | 
				
			||||||
 | 
					                            if (
 | 
				
			||||||
 | 
					                                !this.newDiscussion.subscribe ||
 | 
				
			||||||
 | 
					                                this.newDiscussion.pin ||
 | 
				
			||||||
 | 
					                                this.newDiscussion.files.length ||
 | 
				
			||||||
 | 
					                                this.groups.length > 0 && this.newDiscussion.groupId != this.groups[0].id ||
 | 
				
			||||||
 | 
					                                this.newDiscussion.postToAllGroups
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                this.advanced = true;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!this.originalData) {
 | 
				
			||||||
 | 
					                // Initialize original data.
 | 
				
			||||||
 | 
					                this.originalData = {
 | 
				
			||||||
 | 
					                    subject: this.newDiscussion.subject,
 | 
				
			||||||
 | 
					                    message: this.newDiscussion.message,
 | 
				
			||||||
 | 
					                    files: this.newDiscussion.files.slice(),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.showForm = true;
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_forum.errorgetgroups', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.showForm = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Validate which of the groups returned by getActivityAllowedGroups in visible groups should be shown to post to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param forumGroups Forum groups.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the list of groups.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async validateVisibleGroups(forumGroups: CoreGroup[]): Promise<CoreGroup[]> {
 | 
				
			||||||
 | 
					        let response: AddonModForumCanAddDiscussion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We first check if the user can post to all the groups.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            response = await AddonModForum.instance.canAddDiscussionToAll(this.forumId, { cmId: this.cmId });
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            // The call failed, let's assume he can't.
 | 
				
			||||||
 | 
					            response = {
 | 
				
			||||||
 | 
					                status: false,
 | 
				
			||||||
 | 
					                canpindiscussions: false,
 | 
				
			||||||
 | 
					                cancreateattachment: true,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.canPin = !!response.canpindiscussions;
 | 
				
			||||||
 | 
					        this.canCreateAttachments = !!response.cancreateattachment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The user can post to all groups, add the "All participants" option and return them all.
 | 
				
			||||||
 | 
					        if (response.status) {
 | 
				
			||||||
 | 
					            return this.addAllParticipantsOption(forumGroups, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The user can't post to all groups, let's check which groups he can post to.
 | 
				
			||||||
 | 
					        const promises: Promise<unknown>[] = [];
 | 
				
			||||||
 | 
					        const filtered: CoreGroup[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        forumGroups.forEach((group) => {
 | 
				
			||||||
 | 
					            promises.push(
 | 
				
			||||||
 | 
					                AddonModForum.instance
 | 
				
			||||||
 | 
					                    .canAddDiscussion(this.forumId, group.id, { cmId: this.cmId })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // The call failed, let's return true so the group is shown.
 | 
				
			||||||
 | 
					                    // If the user can't post to it an error will be shown when he tries to add the discussion.
 | 
				
			||||||
 | 
					                    .catch(() =>({ status: true }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    .then((response) => {
 | 
				
			||||||
 | 
					                        if (response.status) {
 | 
				
			||||||
 | 
					                            filtered.push(group);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return filtered;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Filter forum groups, returning only those that are inside user groups.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param forumGroups Forum groups.
 | 
				
			||||||
 | 
					     * @param userGroups User groups.
 | 
				
			||||||
 | 
					     * @return Filtered groups.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected filterGroups(forumGroups: CoreGroup[], userGroups: CoreGroup[]): CoreGroup[] {
 | 
				
			||||||
 | 
					        const userGroupsIds = userGroups.map(group => group.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return forumGroups.filter(forumGroup => userGroupsIds.indexOf(forumGroup.id) > -1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add the "All participants" option to a list of groups if the user can add a discussion to all participants.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groups Groups.
 | 
				
			||||||
 | 
					     * @param check True to check if the user can add a discussion to all participants.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the list of groups.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected addAllParticipantsOption(groups: CoreGroup[], check: boolean): Promise<CoreGroup[]> {
 | 
				
			||||||
 | 
					        if (!AddonModForum.instance.isAllParticipantsFixed()) {
 | 
				
			||||||
 | 
					            // All participants has a bug, don't add it.
 | 
				
			||||||
 | 
					            return Promise.resolve(groups);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (check) {
 | 
				
			||||||
 | 
					            // We need to check if the user can add a discussion to all participants.
 | 
				
			||||||
 | 
					            promise = AddonModForum.instance.canAddDiscussionToAll(this.forumId, { cmId: this.cmId }).then((response) => {
 | 
				
			||||||
 | 
					                this.canPin = !!response.canpindiscussions;
 | 
				
			||||||
 | 
					                this.canCreateAttachments = !!response.cancreateattachment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return response.status;
 | 
				
			||||||
 | 
					            }).catch(() =>
 | 
				
			||||||
 | 
					                // The call failed, let's assume he can't.
 | 
				
			||||||
 | 
					                false);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // No need to check, assume the user can.
 | 
				
			||||||
 | 
					            promise = Promise.resolve(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return promise.then((canAdd) => {
 | 
				
			||||||
 | 
					            if (canAdd) {
 | 
				
			||||||
 | 
					                groups.unshift({
 | 
				
			||||||
 | 
					                    courseid: this.courseId,
 | 
				
			||||||
 | 
					                    id: AddonModForumProvider.ALL_PARTICIPANTS,
 | 
				
			||||||
 | 
					                    name: Translate.instant('core.allparticipants'),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return groups;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Pull to refresh.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresher Refresher.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    refreshGroups(refresher?: IonRefresher): void {
 | 
				
			||||||
 | 
					        const promises = [
 | 
				
			||||||
 | 
					            CoreGroups.instance.invalidateActivityGroupMode(this.cmId),
 | 
				
			||||||
 | 
					            CoreGroups.instance.invalidateActivityAllowedGroups(this.cmId),
 | 
				
			||||||
 | 
					            AddonModForum.instance.invalidateCanAddDiscussion(this.forumId),
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Promise.all(promises).finally(() => {
 | 
				
			||||||
 | 
					            this.fetchDiscussionData(true).finally(() => {
 | 
				
			||||||
 | 
					                refresher?.complete();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience function to update or return to discussions depending on device.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param discussionIds Ids of the new discussions.
 | 
				
			||||||
 | 
					     * @param discTimecreated The time created of the discussion (if offline).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected returnToDiscussions(discussionIds?: number[] | null, discTimecreated?: number): void {
 | 
				
			||||||
 | 
					        const data: any = {
 | 
				
			||||||
 | 
					            forumId: this.forumId,
 | 
				
			||||||
 | 
					            cmId: this.cmId,
 | 
				
			||||||
 | 
					            discussionIds: discussionIds,
 | 
				
			||||||
 | 
					            discTimecreated: discTimecreated,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        CoreEvents.trigger(AddonModForumProvider.NEW_DISCUSSION_EVENT, data, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Delete the local files from the tmp folder.
 | 
				
			||||||
 | 
					        CoreFileUploader.instance.clearTmpFiles(this.newDiscussion.files);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Message changed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param text The new text.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    onMessageChange(text: string): void {
 | 
				
			||||||
 | 
					        this.newDiscussion.message = text;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add a new discussion.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async add(): Promise<void> {
 | 
				
			||||||
 | 
					        const forumName = this.forum.name;
 | 
				
			||||||
 | 
					        const subject = this.newDiscussion.subject;
 | 
				
			||||||
 | 
					        let  message = this.newDiscussion.message || '';
 | 
				
			||||||
 | 
					        const pin = this.newDiscussion.pin;
 | 
				
			||||||
 | 
					        const attachments = this.newDiscussion.files;
 | 
				
			||||||
 | 
					        const discTimecreated = this.timeCreated || Date.now();
 | 
				
			||||||
 | 
					        const options: AddonModForumDiscussionOptions = {
 | 
				
			||||||
 | 
					            discussionsubscribe: !!this.newDiscussion.subscribe,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!subject) {
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.showErrorModal('addon.mod_forum.erroremptysubject', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!message) {
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.showErrorModal('addon.mod_forum.erroremptymessage', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add some HTML to the message if needed.
 | 
				
			||||||
 | 
					        message = CoreTextUtils.instance.formatHtmlLines(message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (pin) {
 | 
				
			||||||
 | 
					            options.discussionpinned = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const groupIds = this.newDiscussion.postToAllGroups ? this.groupIds : [this.newDiscussion.groupId];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const discussionIds = await AddonModForumHelper.instance.addNewDiscussion(
 | 
				
			||||||
 | 
					                this.forumId,
 | 
				
			||||||
 | 
					                forumName,
 | 
				
			||||||
 | 
					                this.courseId,
 | 
				
			||||||
 | 
					                subject,
 | 
				
			||||||
 | 
					                message,
 | 
				
			||||||
 | 
					                attachments,
 | 
				
			||||||
 | 
					                options,
 | 
				
			||||||
 | 
					                groupIds,
 | 
				
			||||||
 | 
					                discTimecreated,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (discussionIds) {
 | 
				
			||||||
 | 
					                // Data sent to server, delete stored files (if any).
 | 
				
			||||||
 | 
					                AddonModForumHelper.instance.deleteNewDiscussionStoredFiles(this.forumId, discTimecreated);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'forum' });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (discussionIds && discussionIds.length < groupIds.length) {
 | 
				
			||||||
 | 
					                // Some discussions could not be created.
 | 
				
			||||||
 | 
					                CoreDomUtils.instance.showErrorModalDefault(null, 'addon.mod_forum.errorposttoallgroups', true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.triggerFormSubmittedEvent(
 | 
				
			||||||
 | 
					                this.formElement,
 | 
				
			||||||
 | 
					                !!discussionIds,
 | 
				
			||||||
 | 
					                CoreSites.instance.getCurrentSiteId(),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.returnToDiscussions(discussionIds, discTimecreated);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_forum.cannotcreatediscussion', true);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            modal.dismiss();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Discard an offline saved discussion.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async discard(): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await CoreDomUtils.instance.showConfirm(Translate.instant('core.areyousure'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const promises: Promise<unknown>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            promises.push(AddonModForumOffline.instance.deleteNewDiscussion(this.forumId, this.timeCreated));
 | 
				
			||||||
 | 
					            promises.push(
 | 
				
			||||||
 | 
					                CoreUtils.instance.ignoreErrors(
 | 
				
			||||||
 | 
					                    AddonModForumHelper.instance.deleteNewDiscussionStoredFiles(this.forumId, this.timeCreated),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.triggerFormCancelledEvent(this.formElement, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.returnToDiscussions();
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            // Cancelled.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show or hide advanced form fields.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    toggleAdvanced(): void {
 | 
				
			||||||
 | 
					        this.advanced = !this.advanced;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if we can leave the page or not.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Resolved if we can leave it, rejected if not.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async ionViewCanLeave(): Promise<void> {
 | 
				
			||||||
 | 
					        if (this.forceLeave) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (AddonModForumHelper.instance.hasPostDataChanged(this.newDiscussion, this.originalData)) {
 | 
				
			||||||
 | 
					            // Show confirmation if some data has been modified.
 | 
				
			||||||
 | 
					            await CoreDomUtils.instance.showConfirm(Translate.instant('core.confirmcanceledit'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Delete the local files from the tmp folder.
 | 
				
			||||||
 | 
					        CoreFileUploader.instance.clearTmpFiles(this.newDiscussion.files);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.formElement) {
 | 
				
			||||||
 | 
					            CoreDomUtils.instance.triggerFormCancelledEvent(this.formElement, CoreSites.instance.getCurrentSiteId());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Runs when the page is about to leave and no longer be the active page.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewWillLeave(): void {
 | 
				
			||||||
 | 
					        this.syncObserver && this.syncObserver.off();
 | 
				
			||||||
 | 
					        delete this.syncObserver;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Page destroyed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnDestroy(): void {
 | 
				
			||||||
 | 
					        if (this.syncId) {
 | 
				
			||||||
 | 
					            CoreSync.instance.unblockOperation(AddonModForumProvider.COMPONENT, this.syncId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.isDestroyed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -13,6 +13,7 @@
 | 
				
			|||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Injectable } from '@angular/core';
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { FileEntry } from '@ionic-native/file/ngx';
 | 
				
			||||||
import { CoreFileEntry, CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
					import { CoreFileEntry, CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
				
			||||||
import { CoreUser } from '@features/user/services/user';
 | 
					import { CoreUser } from '@features/user/services/user';
 | 
				
			||||||
import { CoreApp } from '@services/app';
 | 
					import { CoreApp } from '@services/app';
 | 
				
			||||||
 | 
				
			|||||||
@ -403,7 +403,9 @@ export class AddonModForumOfflineProvider {
 | 
				
			|||||||
export class AddonModForumOffline extends makeSingleton(AddonModForumOfflineProvider) {}
 | 
					export class AddonModForumOffline extends makeSingleton(AddonModForumOfflineProvider) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AddonModForumDiscussionOptions = {
 | 
					export type AddonModForumDiscussionOptions = {
 | 
				
			||||||
    attachmentsid: number | CoreFileUploaderStoreFilesResult;
 | 
					    attachmentsid?: number | CoreFileUploaderStoreFilesResult;
 | 
				
			||||||
 | 
					    discussionsubscribe?: boolean;
 | 
				
			||||||
 | 
					    discussionpinned?: boolean;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AddonModForumReplyOptions = {
 | 
					export type AddonModForumReplyOptions = {
 | 
				
			||||||
 | 
				
			|||||||
@ -743,7 +743,7 @@ export class CoreDomUtilsProvider {
 | 
				
			|||||||
     * @param error Error to check.
 | 
					     * @param error Error to check.
 | 
				
			||||||
     * @return Whether it's a canceled error.
 | 
					     * @return Whether it's a canceled error.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    isCanceledError(error: CoreError | CoreTextErrorObject | string): boolean {
 | 
					    isCanceledError(error: CoreError | CoreTextErrorObject | string | null): boolean {
 | 
				
			||||||
        return error instanceof CoreCanceledError;
 | 
					        return error instanceof CoreCanceledError;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1393,7 +1393,7 @@ export class CoreDomUtilsProvider {
 | 
				
			|||||||
     * @return Promise resolved with the alert modal.
 | 
					     * @return Promise resolved with the alert modal.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async showErrorModalDefault(
 | 
					    async showErrorModalDefault(
 | 
				
			||||||
        error: CoreError | CoreTextErrorObject | string,
 | 
					        error: CoreError | CoreTextErrorObject | string | null,
 | 
				
			||||||
        defaultError: string,
 | 
					        defaultError: string,
 | 
				
			||||||
        needsTranslate?: boolean,
 | 
					        needsTranslate?: boolean,
 | 
				
			||||||
        autocloseTime?: number,
 | 
					        autocloseTime?: number,
 | 
				
			||||||
@ -1409,7 +1409,7 @@ export class CoreDomUtilsProvider {
 | 
				
			|||||||
            errorMessage = CoreTextUtils.instance.getErrorMessageFromError(error);
 | 
					            errorMessage = CoreTextUtils.instance.getErrorMessageFromError(error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this.showErrorModal(typeof errorMessage == 'string' ? error : defaultError, needsTranslate, autocloseTime);
 | 
					        return this.showErrorModal(typeof errorMessage == 'string' ? error! : defaultError, needsTranslate, autocloseTime);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user