MOBILE-4288 core: Format group names
Group names may contain other filters such as mathjax, so only filtering multilanguage is not enough.
This commit is contained in:
		
							parent
							
								
									f05085798b
								
							
						
					
					
						commit
						e8a73689ef
					
				| @ -86,7 +86,10 @@ | ||||
|             <ion-item class="ion-text-wrap core-groupname" *ngIf="groupName"> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ 'core.group' | translate}}</p> | ||||
|                     <p>{{ groupName }}</p> | ||||
|                     <p> | ||||
|                         <core-format-text [text]="groupName" contextLevel="course" [contextInstanceId]="event.courseid"> | ||||
|                         </core-format-text> | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap" *ngIf="categoryPath"> | ||||
|  | ||||
| @ -30,7 +30,6 @@ import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreCourse } from '@features/course/services/course'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreGroups } from '@services/groups'; | ||||
| import { NgZone, Translate } from '@singletons'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| @ -42,7 +41,6 @@ import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-sourc | ||||
| import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; | ||||
| import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders'; | ||||
| import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a single calendar event. | ||||
| @ -261,7 +259,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|             // If it's a group event, get the name of the group.
 | ||||
|             if (courseId && this.event.groupid) { | ||||
|                 promises.push(this.loadGroupName(this.event, courseId)); | ||||
|                 this.groupName = event.groupname; | ||||
|             } | ||||
| 
 | ||||
|             if (this.event.iscategoryevent && this.event.category) { | ||||
| @ -359,26 +357,6 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
|         return deleted; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load group name. | ||||
|      * | ||||
|      * @param event Event. | ||||
|      * @param courseId Course ID. | ||||
|      * @returns Promise resolved when done. | ||||
|      */ | ||||
|     protected async loadGroupName(event: AddonCalendarEventToDisplay, courseId: number): Promise<void> { | ||||
|         try { | ||||
|             const groups = await CoreGroups.getUserGroupsInCourse(courseId); | ||||
| 
 | ||||
|             const group = groups.find((group) => group.id == event.groupid); | ||||
|             this.groupName = group ? await CoreLang.filterMultilang(group.name) : ''; | ||||
| 
 | ||||
|         } catch { | ||||
|             // Error getting groups, just don't show the group name.
 | ||||
|             this.groupName = ''; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Add a reminder for this event. | ||||
|      */ | ||||
|  | ||||
| @ -33,7 +33,6 @@ import { CoreError } from '@classes/errors/error'; | ||||
| import { AddonMessagesSyncEvents, AddonMessagesSyncProvider } from './messages-sync'; | ||||
| import { CoreWSError } from '@classes/errors/wserror'; | ||||
| import { AddonNotificationsPreferencesNotificationProcessorState } from '@addons/notifications/services/notifications'; | ||||
| import { MultilangString } from '@services/lang'; | ||||
| 
 | ||||
| const ROOT_CACHE_KEY = 'mmaMessages:'; | ||||
| 
 | ||||
| @ -2971,7 +2970,7 @@ export type AddonMessagesConversationMember = { | ||||
|     conversations?: { // Conversations between users.
 | ||||
|         id: number; // Conversations id.
 | ||||
|         type: number; // Conversation type: private or public.
 | ||||
|         name: MultilangString; // Multilang compatible conversation name2.
 | ||||
|         name: string; // Multilang compatible conversation name2.
 | ||||
|         timecreated: number; // The timecreated timestamp for the conversation.
 | ||||
|     }[]; | ||||
| }; | ||||
| @ -3496,7 +3495,7 @@ export type AddonMessagesGetUserContactsWSResponse = { | ||||
|     conversations?: { // Conversations between users.
 | ||||
|         id: number; // Conversations id.
 | ||||
|         type: number; // Conversation type: private or public.
 | ||||
|         name: MultilangString; // Multilang compatible conversation name2.
 | ||||
|         name: string; // Multilang compatible conversation name2.
 | ||||
|         timecreated: number; // The timecreated timestamp for the conversation.
 | ||||
|     }[]; | ||||
| }[]; | ||||
| @ -3536,7 +3535,7 @@ export type AddonMessagesGetContactRequestsWSResponse = { | ||||
|     conversations?: { // Conversations between users.
 | ||||
|         id: number; // Conversations id.
 | ||||
|         type: number; // Conversation type: private or public.
 | ||||
|         name: MultilangString; // Multilang compatible conversation name2.
 | ||||
|         name: string; // Multilang compatible conversation name2.
 | ||||
|         timecreated: number; // The timecreated timestamp for the conversation.
 | ||||
|     }[]; | ||||
| }[]; | ||||
|  | ||||
| @ -21,7 +21,8 @@ | ||||
|     <ng-container *ngIf="assign && canViewAllSubmissions"> | ||||
|         <ion-list class="core-list-align-detail-right"> | ||||
| 
 | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)"></core-group-selector> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" [courseId]="courseId"> | ||||
|             </core-group-selector> | ||||
| 
 | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|  | ||||
| @ -421,8 +421,10 @@ | ||||
| <!-- Template to render some data regarding the submission status. --> | ||||
| <ng-template #submissionStatus> | ||||
|     <ng-container *ngIf="assign && assign!.teamsubmission && lastAttempt"> | ||||
|         <p *ngIf="lastAttempt!.submissiongroup && lastAttempt!.submissiongroupname" class="core-groupname"> | ||||
|             {{lastAttempt!.submissiongroupname}} | ||||
|         <p *ngIf="lastAttempt.submissiongroup && lastAttempt.submissiongroupname" class="core-groupname"> | ||||
|             <core-format-text [text]="lastAttempt.submissiongroupname" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                 [wsNotFiltered]="true"> | ||||
|             </core-format-text> | ||||
|         </p> | ||||
|         <ng-container *ngIf="assign!.preventsubmissionnotingroup && | ||||
|             !lastAttempt!.submissiongroup && | ||||
|  | ||||
| @ -1147,10 +1147,10 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can | ||||
|         if (this.assign.teamsubmission) { | ||||
|             if (lastAttempt.submissiongroup) { | ||||
|                 // Get the name of the group.
 | ||||
|                 promises.push(CoreGroups.getActivityAllowedGroups(this.assign.cmid).then(async (result) => { | ||||
|                 promises.push(CoreGroups.getActivityAllowedGroups(this.assign.cmid).then((result) => { | ||||
|                     const group = result.groups.find((group) => group.id === lastAttempt.submissiongroup); | ||||
|                     if (group) { | ||||
|                         lastAttempt.submissiongroupname = await CoreLang.filterMultilang(group.name); | ||||
|                         lastAttempt.submissiongroupname = group.name; | ||||
|                     } | ||||
| 
 | ||||
|                     return; | ||||
|  | ||||
| @ -21,7 +21,8 @@ | ||||
|         </ion-refresher> | ||||
|         <core-loading [hideUntil]="submissions.loaded"> | ||||
|             <ion-list> | ||||
|                 <core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSubmissions()"> | ||||
|                 <core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSubmissions()" | ||||
|                     [courseId]="courseId"> | ||||
|                 </core-group-selector> | ||||
| 
 | ||||
|                 <!-- List of submissions. --> | ||||
| @ -36,7 +37,8 @@ | ||||
|                             </p> | ||||
|                             <p *ngIf="assign && assign!.teamsubmission"> | ||||
|                                 <span *ngIf="submission.groupname" class="core-groupname"> | ||||
|                                     {{submission.groupname}} | ||||
|                                     <core-format-text [text]="submission.groupname" contextLevel="course" [contextInstanceId]="courseId"> | ||||
|                                     </core-format-text> | ||||
|                                 </span> | ||||
|                                 <span *ngIf="assign!.preventsubmissionnotingroup && !submission.groupname && submission.noGroups | ||||
|                                     && !submission.blindid" class="text-danger"> | ||||
|  | ||||
| @ -35,7 +35,6 @@ import { AddonModAssignSync, AddonModAssignSyncResult } from '../assign-sync'; | ||||
| import { CoreUser } from '@features/user/services/user'; | ||||
| import { CoreGradesHelper } from '@features/grades/services/grades-helper'; | ||||
| import { CoreCourses } from '@features/courses/services/courses'; | ||||
| import { multilangString } from '@services/lang'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to prefetch assigns. | ||||
| @ -356,7 +355,7 @@ export class AddonModAssignPrefetchHandlerService extends CoreCourseActivityPref | ||||
| 
 | ||||
|         // Teacher, prefetch all submissions.
 | ||||
|         if (!groupInfo.groups || groupInfo.groups.length == 0) { | ||||
|             groupInfo.groups = [{ id: 0, name: multilangString() }]; | ||||
|             groupInfo.groups = [{ id: 0, name: '' }]; | ||||
|         } | ||||
| 
 | ||||
|         const promises = groupInfo.groups.map((group) => | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
|     </core-course-module-info> | ||||
| 
 | ||||
|     <core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="groupChanged()" | ||||
|         [multipleGroupsMessage]="'addon.mod_bigbluebuttonbn.view_groups_selection_warning' | translate"></core-group-selector> | ||||
|         [multipleGroupsMessage]="'addon.mod_bigbluebuttonbn.view_groups_selection_warning' | translate" [courseId]="module.course"> | ||||
|     </core-group-selector> | ||||
| 
 | ||||
|     <ng-container *ngIf="meetingInfo && showRoom"> | ||||
|         <ion-item class="ion-text-wrap" *ngIf="meetingInfo.openingtime"> | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
|             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|         </ion-refresher> | ||||
|         <core-loading [hideUntil]="sessions.loaded"> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSessions()"></core-group-selector> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="reloadSessions()" [courseId]="courseId"> | ||||
|             </core-group-selector> | ||||
| 
 | ||||
|             <ion-item> | ||||
|                 <ion-label>{{ 'addon.mod_chat.showincompletesessions' | translate }}</ion-label> | ||||
|  | ||||
| @ -34,15 +34,16 @@ export class AddonModChatSessionsPage implements AfterViewInit, OnDestroy { | ||||
|     @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; | ||||
| 
 | ||||
|     sessions!: CoreListItemsManager<AddonModChatSessionFormatted, AddonModChatSessionsSource>; | ||||
|     courseId?: number; | ||||
| 
 | ||||
|     constructor() { | ||||
|         try { | ||||
|             const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); | ||||
|             this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); | ||||
|             const chatId = CoreNavigator.getRequiredRouteNumberParam('chatId'); | ||||
|             const cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); | ||||
|             const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( | ||||
|                 AddonModChatSessionsSource, | ||||
|                 [courseId, chatId, cmId], | ||||
|                 [this.courseId, chatId, cmId], | ||||
|             ); | ||||
| 
 | ||||
|             this.sessions = new CoreListItemsManager(source, AddonModChatSessionsPage); | ||||
|  | ||||
| @ -25,7 +25,8 @@ | ||||
|         [courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings" (completionChanged)="onCompletionChange()"> | ||||
|     </core-course-module-info> | ||||
| 
 | ||||
|     <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"> | ||||
|     <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)" | ||||
|         [courseId]="database?.course"> | ||||
|     </core-group-selector> | ||||
| 
 | ||||
|     <ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableFrom"> | ||||
|  | ||||
| @ -18,7 +18,8 @@ | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"> | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)" | ||||
|             [courseId]="database?.course"> | ||||
|         </core-group-selector> | ||||
| 
 | ||||
|         <div class="addon-data-contents {{cssClass}}" *ngIf="database"> | ||||
|  | ||||
| @ -25,7 +25,8 @@ | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)"> | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="setGroup(selectedGroup)" | ||||
|             [courseId]="courseId"> | ||||
|         </core-group-selector> | ||||
| 
 | ||||
|         <div class="addon-data-contents addon-data-entry addon-data-entries-{{database.id}}" *ngIf="database && entry"> | ||||
|  | ||||
| @ -12,7 +12,6 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { multilangString } from '@services/lang'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreComments } from '@features/comments/services/comments'; | ||||
| import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; | ||||
| @ -99,7 +98,7 @@ export class AddonModDataPrefetchHandlerService extends CoreCourseActivityPrefet | ||||
| 
 | ||||
|             const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); | ||||
|             if (!groupInfo.groups || groupInfo.groups.length == 0) { | ||||
|                 groupInfo.groups = [{ id: 0, name: multilangString() }]; | ||||
|                 groupInfo.groups = [{ id: 0, name: '' }]; | ||||
|             } | ||||
|             groups = groupInfo.groups || []; | ||||
| 
 | ||||
|  | ||||
| @ -60,7 +60,8 @@ | ||||
| 
 | ||||
| <ng-template #basicInfo> | ||||
|     <ion-list *ngIf="access && access.canviewanalysis && !access.isempty"> | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)"></core-group-selector> | ||||
|         <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" [courseId]="courseId"> | ||||
|         </core-group-selector> | ||||
| 
 | ||||
|         <ion-item class="ion-text-wrap" (click)="openAttempts()" [class.hide-detail]="!(access.canviewreports && completedCount > 0)" | ||||
|             [detail]="true" [button]="access.canviewreports && completedCount > 0"> | ||||
|  | ||||
| @ -15,7 +15,8 @@ | ||||
|         </ion-refresher> | ||||
|         <core-loading [hideUntil]="attempts && attempts.loaded"> | ||||
|             <ion-list class="ion-no-margin"> | ||||
|                 <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="reloadAttempts()"> | ||||
|                 <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="reloadAttempts()" | ||||
|                     [courseId]="courseId"> | ||||
|                 </core-group-selector> | ||||
| 
 | ||||
|                 <ng-container *ngIf="identifiableAttemptsTotal > 0"> | ||||
|  | ||||
| @ -39,6 +39,7 @@ export class AddonModFeedbackAttemptsPage implements AfterViewInit, OnDestroy { | ||||
| 
 | ||||
|     promisedAttempts: CorePromisedValue<AddonModFeedbackAttemptsManager>; | ||||
|     fetchFailed = false; | ||||
|     courseId?: number; | ||||
| 
 | ||||
|     constructor(protected route: ActivatedRoute) { | ||||
|         this.promisedAttempts = new CorePromisedValue(); | ||||
| @ -86,11 +87,11 @@ export class AddonModFeedbackAttemptsPage implements AfterViewInit, OnDestroy { | ||||
|      */ | ||||
|     async ngAfterViewInit(): Promise<void> { | ||||
|         try { | ||||
|             this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); | ||||
|             const cmId = CoreNavigator.getRequiredRouteNumberParam('cmId'); | ||||
|             const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId'); | ||||
|             const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( | ||||
|                 AddonModFeedbackAttemptsSource, | ||||
|                 [courseId, cmId], | ||||
|                 [this.courseId, cmId], | ||||
|             ); | ||||
| 
 | ||||
|             source.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0; | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
|     </ion-refresher> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <ion-list class="ion-no-margin"> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="loadAttempts(selectedGroup)"> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="selectedGroup" (selectedChange)="loadAttempts(selectedGroup)" | ||||
|                 [courseId]="courseId"> | ||||
|             </core-group-selector> | ||||
| 
 | ||||
|             <ion-item-divider> | ||||
|  | ||||
| @ -31,10 +31,10 @@ import { AddonModFeedbackHelper, AddonModFeedbackNonRespondent } from '../../ser | ||||
| export class AddonModFeedbackNonRespondentsPage implements OnInit { | ||||
| 
 | ||||
|     protected cmId!: number; | ||||
|     protected courseId!: number; | ||||
|     protected feedback?: AddonModFeedbackWSFeedback; | ||||
|     protected page = 0; | ||||
| 
 | ||||
|     courseId!: number; | ||||
|     selectedGroup!: number; | ||||
|     groupInfo?: CoreGroupInfo; | ||||
|     users: AddonModFeedbackNonRespondent[] = []; | ||||
|  | ||||
| @ -12,7 +12,6 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { multilangString } from '@services/lang'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; | ||||
| import { CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course'; | ||||
| @ -188,7 +187,7 @@ export class AddonModFeedbackPrefetchHandlerService extends CoreCourseActivityPr | ||||
|         const promises: Promise<unknown>[] = []; | ||||
| 
 | ||||
|         if (!groupInfo.groups || groupInfo.groups.length == 0) { | ||||
|             groupInfo.groups = [{ id: 0, name: multilangString() }]; | ||||
|             groupInfo.groups = [{ id: 0, name: '' }]; | ||||
|         } | ||||
| 
 | ||||
|         groupInfo.groups.forEach((group) => { | ||||
|  | ||||
| @ -25,7 +25,8 @@ | ||||
|             </ion-item> | ||||
|         </core-course-module-info> | ||||
| 
 | ||||
|         <core-group-selector *ngIf="supportsChangeGroup" [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="groupChanged()"> | ||||
|         <core-group-selector *ngIf="supportsChangeGroup" [groupInfo]="groupInfo" [(selected)]="groupId" (selectedChange)="groupChanged()" | ||||
|             [courseId]="courseId"> | ||||
|         </core-group-selector> | ||||
| 
 | ||||
|         <!-- Cut-off date or due date message --> | ||||
| @ -95,7 +96,10 @@ | ||||
|                             <span *ngIf="discussion.userfullname">{{discussion.userfullname}}</span> | ||||
|                             <p *ngIf="discussion.groupname" class="core-groupname"> | ||||
|                                 <ion-icon name="fas-users" [attr.aria-label]="'addon.mod_forum.group' | translate"> | ||||
|                                 </ion-icon> {{ discussion.groupname }} | ||||
|                                 </ion-icon> | ||||
|                                 <core-format-text [text]="discussion.groupname" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                                     [wsNotFiltered]="true"> | ||||
|                                 </core-format-text> | ||||
|                             </p> | ||||
|                             <p *ngIf="isOnlineDiscussion(discussion)"> | ||||
|                                 {{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}} | ||||
|  | ||||
| @ -34,7 +34,10 @@ | ||||
|                             <p *ngIf="post.author && post.author.groups" class="core-groupname"> | ||||
|                                 <ng-container *ngFor="let group of post.author.groups"> | ||||
|                                     <ion-icon name="fas-users" [attr.aria-label]="'addon.mod_forum.group' | translate"> | ||||
|                                     </ion-icon> {{ group.name }} | ||||
|                                     </ion-icon> | ||||
|                                     <core-format-text [text]="group.name" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                                         [wsNotFiltered]="true"> | ||||
|                                     </core-format-text> | ||||
|                                 </ng-container> | ||||
|                             </p> | ||||
|                             <p *ngIf="post.timecreated">{{post.timecreated * 1000 | coreFormatDate: "strftimerecentfull"}}</p> | ||||
|  | ||||
| @ -50,7 +50,10 @@ | ||||
|                     <ion-select [(ngModel)]="newDiscussion.groupId" [disabled]="newDiscussion.postToAllGroups" interface="action-sheet" | ||||
|                         name="groupid" [interfaceOptions]="{header: 'addon.mod_forum.group' | translate}" | ||||
|                         (ionChange)="calculateGroupName()"> | ||||
|                         <ion-select-option *ngFor="let group of groups" [value]="group.id">{{ group.name }}</ion-select-option> | ||||
|                         <ion-select-option *ngFor="let group of groups" [value]="group.id"> | ||||
|                             <core-format-text [text]="group.name" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                                 [wsNotFiltered]="true"></core-format-text> | ||||
|                         </ion-select-option> | ||||
|                     </ion-select> | ||||
|                 </ion-item> | ||||
|                 <ion-item> | ||||
| @ -66,9 +69,13 @@ | ||||
|                     [allowOffline]="true" [courseId]="courseId"> | ||||
|                 </core-attachments> | ||||
|             </div> | ||||
|             <ion-item *ngIf="showGroups && groupName && !newDiscussion.postToAllGroups" class="addon-forum-group-info"> | ||||
|             <ion-item *ngIf="showGroups && postInGroupMessage && !newDiscussion.postToAllGroups" class="addon-forum-group-info"> | ||||
|                 <ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 <ion-label>{{ 'addon.mod_forum.postingroup' | translate:{groupname: groupName} }}</ion-label> | ||||
|                 <ion-label> | ||||
|                     <core-format-text [text]="postInGroupMessage" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                         [wsNotFiltered]="true"> | ||||
|                     </core-format-text> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|             <ion-item class="addon-forum-new-discussion-buttons"> | ||||
|                 <ion-label> | ||||
|  | ||||
| @ -44,7 +44,6 @@ import { AddonModForumDiscussionsSwipeManager } from '../../classes/forum-discus | ||||
| import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; | ||||
| import { AddonModForumDiscussionsSource } from '../../classes/forum-discussions-source'; | ||||
| import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; | ||||
| import { CoreLang, multilangString } from '@services/lang'; | ||||
| 
 | ||||
| type NewDiscussionData = { | ||||
|     subject: string; | ||||
| @ -93,7 +92,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea | ||||
|     advanced = false; // Display all form fields.
 | ||||
|     accessInfo: AddonModForumAccessInformation = {}; | ||||
|     courseId!: number; | ||||
|     groupName?: string; | ||||
|     postInGroupMessage?: string; | ||||
| 
 | ||||
|     discussions?: AddonModForumNewDiscussionDiscussionsSwipeManager; | ||||
| 
 | ||||
| @ -427,7 +426,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea | ||||
|                 groups.unshift({ | ||||
|                     courseid: this.courseId, | ||||
|                     id: AddonModForumProvider.ALL_PARTICIPANTS, | ||||
|                     name: multilangString(Translate.instant('core.allparticipants')), | ||||
|                     name: Translate.instant('core.allparticipants'), | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
| @ -614,11 +613,11 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea | ||||
|      */ | ||||
|     async calculateGroupName(): Promise<void> { | ||||
|         if (this.newDiscussion.groupId <= 0) { | ||||
|             this.groupName = undefined; | ||||
|             this.postInGroupMessage = undefined; | ||||
|         } else { | ||||
|             const groupName = this.groups.find(group => group.id === this.newDiscussion.groupId)?.name; | ||||
| 
 | ||||
|             this.groupName = groupName && await CoreLang.filterMultilang(groupName); | ||||
|             this.postInGroupMessage = groupName && Translate.instant('addon.mod_forum.postingroup', { groupname: groupName }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -56,14 +56,16 @@ | ||||
|         <core-tab *ngIf="canViewReports" [title]="'addon.mod_lesson.reports' | translate" (ionSelect)="reportsSelected()"> | ||||
|             <ng-template> | ||||
|                 <core-loading [hideUntil]="reportLoaded"> | ||||
|                     <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)"> | ||||
|                     <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" | ||||
|                         [courseId]="courseId"> | ||||
|                     </core-group-selector> | ||||
| 
 | ||||
|                     <!-- No lesson retakes. --> | ||||
|                     <core-empty-box *ngIf="!overview && selectedGroupName" icon="fas-chart-bar" | ||||
|                         [message]="'addon.mod_lesson.nolessonattemptsgroup' | translate:{$a: selectedGroupName}"> | ||||
|                     <core-empty-box *ngIf="lesson && !overview && selectedGroupEmptyMessage" icon="fas-chart-bar"> | ||||
|                         <core-format-text [text]="selectedGroupEmptyMessage" contextLevel="course" [contextInstanceId]="courseId" | ||||
|                             [wsNotFiltered]="true"></core-format-text> | ||||
|                     </core-empty-box> | ||||
|                     <core-empty-box *ngIf="!overview && !selectedGroupName" icon="fas-chart-bar" | ||||
|                     <core-empty-box *ngIf="!overview && !selectedGroupEmptyMessage" icon="fas-chart-bar" | ||||
|                         [message]="'addon.mod_lesson.nolessonattempts' | translate"> | ||||
|                     </core-empty-box> | ||||
| 
 | ||||
|  | ||||
| @ -48,7 +48,7 @@ import { | ||||
| import { AddonModLessonModuleHandlerService } from '../../services/handlers/module'; | ||||
| import { CoreTime } from '@singletons/time'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| import { Translate } from '@singletons'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a lesson entry page. | ||||
| @ -79,7 +79,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo | ||||
|     leftDuringTimed?: boolean; // Whether the user has started and left a retake.
 | ||||
|     groupInfo?: CoreGroupInfo; // The group info.
 | ||||
|     reportLoaded?: boolean; // Whether the report data has been loaded.
 | ||||
|     selectedGroupName?: string; // The name of the selected group.
 | ||||
|     selectedGroupEmptyMessage?: string; // The message to show if the selected group is empty.
 | ||||
|     overview?: AttemptsOverview; // Reports overview data.
 | ||||
|     finishedOffline?: boolean; // Whether a retake was finished in offline.
 | ||||
|     avetimeReadable?: string; // Average time in a readable format.
 | ||||
| @ -487,12 +487,15 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo | ||||
|         } | ||||
| 
 | ||||
|         this.group = groupId; | ||||
|         this.selectedGroupName = ''; | ||||
|         this.selectedGroupEmptyMessage = ''; | ||||
| 
 | ||||
|         // Search the name of the group if it isn't all participants.
 | ||||
|         if (groupId && this.groupInfo && this.groupInfo.groups) { | ||||
|             const group = this.groupInfo.groups.find(group => groupId == group.id); | ||||
|             this.selectedGroupName = group ? await CoreLang.filterMultilang(group.name) : ''; | ||||
| 
 | ||||
|             this.selectedGroupEmptyMessage = group | ||||
|                 ? Translate.instant('addon.mod_lesson.nolessonattemptsgroup', { $a: group.name }) | ||||
|                 : ''; | ||||
|         } | ||||
| 
 | ||||
|         // Get the overview of retakes for the group.
 | ||||
|  | ||||
| @ -55,7 +55,6 @@ import { | ||||
| } from '../../services/wiki-sync'; | ||||
| import { AddonModWikiMapModalComponent, AddonModWikiMapModalReturn } from '../map/map'; | ||||
| import { AddonModWikiSubwikiPickerComponent } from '../subwiki-picker/subwiki-picker'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a wiki entry page. | ||||
| @ -829,6 +828,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | ||||
|         const subwiki = await CoreDomUtils.openPopover<AddonModWikiSubwiki>({ | ||||
|             component: AddonModWikiSubwikiPickerComponent, | ||||
|             componentProps: { | ||||
|                 courseId: this.courseId, | ||||
|                 subwikis: this.subwikiData.subwikis, | ||||
|                 currentSubwiki: this.currentSubwiki, | ||||
|             }, | ||||
| @ -902,7 +902,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | ||||
|                 if (subwiki.groupid != 0 && userGroups.length > 0) { | ||||
|                     // Get groupLabel if it has groupId.
 | ||||
|                     const group = userGroups.find(group => group.id == subwiki.groupid); | ||||
|                     groupLabel = group ? await CoreLang.filterMultilang(group.name) : ''; | ||||
|                     groupLabel = group?.name ?? ''; | ||||
|                 } else { | ||||
|                     groupLabel = Translate.instant('addon.mod_wiki.notingroup'); | ||||
|                 } | ||||
|  | ||||
| @ -2,7 +2,9 @@ | ||||
|     <ng-container *ngFor="let group of subwikis"> | ||||
|         <ion-item-divider *ngIf="group.label"> | ||||
|             <ion-label> | ||||
|                 <p class="item-heading">{{ group.label }}</p> | ||||
|                 <core-format-text [text]="group.label" contextLevel="course" [contextInstanceId]="courseId" [wsNotFiltered]="true" | ||||
|                     class="item-heading"> | ||||
|                 </core-format-text> | ||||
|             </ion-label> | ||||
|         </ion-item-divider> | ||||
|         <ion-item class="ion-text-wrap" *ngFor="let subwiki of group.subwikis" (click)="openSubwiki(subwiki)" button | ||||
|  | ||||
| @ -25,6 +25,7 @@ import { AddonModWikiSubwiki, AddonModWikiSubwikiListGrouping } from '../../serv | ||||
| }) | ||||
| export class AddonModWikiSubwikiPickerComponent { | ||||
| 
 | ||||
|     @Input() courseId?: number; | ||||
|     @Input() subwikis: AddonModWikiSubwikiListGrouping[] = []; | ||||
|     @Input() currentSubwiki!: AddonModWikiSubwiki; | ||||
| 
 | ||||
|  | ||||
| @ -181,7 +181,8 @@ | ||||
|                     </h3> | ||||
|                 </ion-label> | ||||
|             </ion-item-divider> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)"></core-group-selector> | ||||
|             <core-group-selector [groupInfo]="groupInfo" [(selected)]="group" (selectedChange)="setGroup(group)" [courseId]="courseId"> | ||||
|             </core-group-selector> | ||||
| 
 | ||||
|             <ng-container *ngFor="let submission of grades"> | ||||
|                 <addon-mod-workshop-submission [submission]="submission" [courseId]="workshop!.course" [module]="module" | ||||
|  | ||||
| @ -12,7 +12,6 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { multilangString } from '@services/lang'; | ||||
| import { AddonModDataSyncResult } from '@addons/mod/data/services/data-sync'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; | ||||
| @ -106,7 +105,7 @@ export class AddonModWorkshopPrefetchHandlerService extends CoreCourseActivityPr | ||||
|             if (access.canviewallsubmissions) { | ||||
|                 const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); | ||||
|                 if (!groupInfo.groups || groupInfo.groups.length == 0) { | ||||
|                     groupInfo.groups = [{ id: 0, name: multilangString() }]; | ||||
|                     groupInfo.groups = [{ id: 0, name: '' }]; | ||||
|                 } | ||||
|                 groups = groupInfo.groups; | ||||
|             } | ||||
|  | ||||
| @ -13,8 +13,9 @@ | ||||
|         </ion-label> | ||||
|         <ion-select [(ngModel)]="selected" (ionChange)="selectedChange.emit(selected)" interface="action-sheet" | ||||
|             [interfaceOptions]="{header: 'core.group' | translate}"> | ||||
|             <ion-select-option *ngFor="let option of options" [value]="option.value"> | ||||
|                 {{ option.text }} | ||||
|             <ion-select-option *ngFor="let group of groupInfo.groups" [value]=" group.id"> | ||||
|                 <core-format-text [text]="group.name" contextLevel="course" [contextInstanceId]="courseId" [wsNotFiltered]="true"> | ||||
|                 </core-format-text> | ||||
|             </ion-select-option> | ||||
|         </ion-select> | ||||
|     </ion-item> | ||||
|  | ||||
| @ -18,12 +18,8 @@ import { | ||||
|     Output, | ||||
|     EventEmitter, | ||||
|     ChangeDetectionStrategy, | ||||
|     OnChanges, | ||||
|     SimpleChanges, | ||||
|     ChangeDetectorRef, | ||||
| } from '@angular/core'; | ||||
| import { CoreGroupInfo } from '@services/groups'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| 
 | ||||
| /** | ||||
|  * Component to display a group selector. | ||||
| @ -33,51 +29,12 @@ import { CoreLang } from '@services/lang'; | ||||
|     templateUrl: 'group-selector.html', | ||||
|     changeDetection: ChangeDetectionStrategy.OnPush, | ||||
| }) | ||||
| export class CoreGroupSelectorComponent implements OnChanges { | ||||
| export class CoreGroupSelectorComponent { | ||||
| 
 | ||||
|     @Input() groupInfo?: CoreGroupInfo; | ||||
|     @Input() multipleGroupsMessage?: string; | ||||
|     @Input() selected!: number; | ||||
|     @Input() courseId?: number; | ||||
|     @Output() selectedChange = new EventEmitter<number>(); | ||||
| 
 | ||||
|     options: GroupOption[] = []; | ||||
| 
 | ||||
|     constructor(protected changeDetectorRef: ChangeDetectorRef) {} | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngOnChanges(changes: SimpleChanges): Promise<void> { | ||||
|         if ('groupInfo' in changes) { | ||||
|             this.options = await this.getOptions(); | ||||
| 
 | ||||
|             this.changeDetectorRef.markForCheck(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get options array. | ||||
|      * | ||||
|      * @returns Options. | ||||
|      */ | ||||
|     protected async getOptions(): Promise<GroupOption[]> { | ||||
|         const options = await Promise.all( | ||||
|             (this.groupInfo?.groups ?? []).map(async group => { | ||||
|                 const text = await CoreLang.filterMultilang(group.name); | ||||
| 
 | ||||
|                 return { value: group.id, text }; | ||||
|             }), | ||||
|         ); | ||||
| 
 | ||||
|         return options; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Group display info. | ||||
|  */ | ||||
| interface GroupOption { | ||||
|     value: number; | ||||
|     text: string; | ||||
| } | ||||
|  | ||||
| @ -414,6 +414,10 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec | ||||
|             this.contextInstanceId = site.getSiteHomeId(); | ||||
|         } | ||||
| 
 | ||||
|         if (this.contextLevel === 'course' && this.contextInstanceId === undefined && this.courseId !== undefined) { | ||||
|             this.contextInstanceId = this.courseId; | ||||
|         } | ||||
| 
 | ||||
|         const filter = this.filter === undefined ? | ||||
|             !!(this.contextLevel && this.contextInstanceId !== undefined) : CoreUtils.isTrueOrOne(this.filter); | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| 
 | ||||
| import { Component, Input, OnInit, Type } from '@angular/core'; | ||||
| import { FormGroup } from '@angular/forms'; | ||||
| import { CoreLang, multilangString } from '@services/lang'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; | ||||
| import { CoreUserProfileField } from '@features/user/services/user'; | ||||
| import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate'; | ||||
| @ -52,7 +52,7 @@ export class CoreUserProfileFieldComponent implements OnInit { | ||||
|         this.componentClass = await CoreUserProfileFieldDelegate.getComponent(this.field, this.signup); | ||||
| 
 | ||||
|         if ('param1' in this.field && this.field.param1) { | ||||
|             this.field.param1 = await CoreLang.filterMultilang(multilangString(this.field.param1)); | ||||
|             this.field.param1 = await CoreLang.filterMultilang(this.field.param1); | ||||
|         } | ||||
| 
 | ||||
|         this.data.field = this.field; | ||||
|  | ||||
| @ -20,7 +20,6 @@ import { CoreError } from '@classes/errors/error'; | ||||
| import { makeSingleton, Translate } from '@singletons'; | ||||
| import { CoreWSExternalWarning } from '@services/ws'; | ||||
| import { CoreCourses } from '@features/courses/services/courses'; | ||||
| import { multilangString, MultilangString } from '@services/lang'; | ||||
| 
 | ||||
| const ROOT_CACHE_KEY = 'mmGroups:'; | ||||
| 
 | ||||
| @ -175,7 +174,7 @@ export class CoreGroupsProvider { | ||||
|             groupInfo.defaultGroupId = 0; | ||||
|         } else { | ||||
|             if (result.canaccessallgroups || groupInfo.visibleGroups) { | ||||
|                 groupInfo.groups.push({ id: 0, name: multilangString(Translate.instant('core.allparticipants')) }); | ||||
|                 groupInfo.groups.push({ id: 0, name: Translate.instant('core.allparticipants') }); | ||||
|                 groupInfo.defaultGroupId = 0; | ||||
|             } else { | ||||
|                 groupInfo.defaultGroupId = result.groups[0].id; | ||||
| @ -443,7 +442,7 @@ export const CoreGroups = makeSingleton(CoreGroupsProvider); | ||||
|  */ | ||||
| export type CoreGroup = { | ||||
|     id: number; // Group ID.
 | ||||
|     name: MultilangString; // Multilang compatible name, course unique'.
 | ||||
|     name: string; // Multilang compatible name, course unique'.
 | ||||
|     description?: string; // Group description text.
 | ||||
|     descriptionformat?: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | ||||
|     idnumber?: string; // Id number.
 | ||||
| @ -527,7 +526,7 @@ type CoreGroupGetCourseUserGroupsWSParams = { | ||||
| export type CoreGroupGetCourseUserGroupsWSResponse = { | ||||
|     groups: { | ||||
|         id: number; // Group record id.
 | ||||
|         name: MultilangString; // Multilang compatible name, course unique.
 | ||||
|         name: string; // Multilang compatible name, course unique.
 | ||||
|         description: string; // Group description text.
 | ||||
|         descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | ||||
|         idnumber: string; // Id number.
 | ||||
|  | ||||
| @ -26,7 +26,6 @@ import { CoreSite } from '../classes/site'; | ||||
| import { CorePlatform } from '@services/platform'; | ||||
| import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang'; | ||||
| import { AddonFilterMultilang2Handler } from '@addons/filter/multilang2/services/handlers/multilang2'; | ||||
| import { Brand } from '@/core/utils/types'; | ||||
| 
 | ||||
| /* | ||||
|  * Service to handle language features, like changing the current language. | ||||
| @ -499,8 +498,8 @@ export class CoreLangProvider { | ||||
|      * @param text Multilang string. | ||||
|      * @returns Filtered string. | ||||
|      */ | ||||
|     async filterMultilang(text: MultilangString): Promise<string> { | ||||
|         return Promise.resolve(text as unknown as string) | ||||
|     async filterMultilang(text: string): Promise<string> { | ||||
|         return Promise.resolve(text) | ||||
|             .then(text => AddonFilterMultilangHandler.filter(text)) | ||||
|             .then(text => AddonFilterMultilang2Handler.filter(text)); | ||||
|     } | ||||
| @ -569,27 +568,11 @@ export class CoreLangProvider { | ||||
| 
 | ||||
| export const CoreLang = makeSingleton(CoreLangProvider); | ||||
| 
 | ||||
| /** | ||||
|  * Make a multilang string. | ||||
|  * | ||||
|  * @param text String. | ||||
|  * @returns Multilang string. | ||||
|  */ | ||||
| export function multilangString(text: string = ''): MultilangString { | ||||
|     return text as unknown as MultilangString; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Language code. E.g. 'au', 'es', etc. | ||||
|  */ | ||||
| export type CoreLangLanguage = string; | ||||
| 
 | ||||
| /** | ||||
|  * Branded type to mark multilang strings, this is useful to avoid rendering | ||||
|  * multilang strings without filtering. | ||||
|  */ | ||||
| export type MultilangString = Brand<unknown, 'multilang'>; | ||||
| 
 | ||||
| /** | ||||
|  * Language object has two leves, first per language and second per string key. | ||||
|  */ | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { mockSingleton } from '@/testing/utils'; | ||||
| import { CoreLang, CoreLangProvider, multilangString } from '@services/lang'; | ||||
| import { CoreLang, CoreLangProvider } from '@services/lang'; | ||||
| 
 | ||||
| describe('Lang', () => { | ||||
| 
 | ||||
| @ -126,7 +126,7 @@ describe('Lang', () => { | ||||
|      * Test multilang filter (normalizing whitespace). | ||||
|      */ | ||||
|     async function expectMultilangFilter(text: string, expected: string): Promise<void> { | ||||
|         const actual = await lang.filterMultilang(multilangString(text)); | ||||
|         const actual = await lang.filterMultilang(text); | ||||
| 
 | ||||
|         expect(actual.replace(/\s+/g, ' ').trim()).toEqual(expected); | ||||
|     } | ||||
|  | ||||
| @ -58,7 +58,7 @@ import { CoreUserSupport } from '@features/user/services/support'; | ||||
| import { CoreErrorInfoComponent } from '@components/error-info/error-info'; | ||||
| import { CorePlatform } from '@services/platform'; | ||||
| import { CoreCancellablePromise } from '@classes/cancellable-promise'; | ||||
| import { CoreLang, multilangString } from '@services/lang'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| 
 | ||||
| /* | ||||
|  * "Utils" service with helper functions for UI, DOM elements and HTML code. | ||||
| @ -1170,7 +1170,7 @@ export class CoreDomUtilsProvider { | ||||
| 
 | ||||
|         if (hasHTMLTags && !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.7')) { | ||||
|             // Treat multilang.
 | ||||
|             options.message = await CoreLang.filterMultilang(multilangString(<string> options.message)); | ||||
|             options.message = await CoreLang.filterMultilang(<string> options.message); | ||||
|         } | ||||
| 
 | ||||
|         const alertId = <string> Md5.hashAsciiStr((options.header || '') + '#' + (options.message || '')); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user