// (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 { Injectable } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { makeSingleton, Translate } from '@singletons/core.singletons'; /* * Service to handle groups. */ @Injectable() export class CoreGroupsProvider { // Group mode constants. static NOGROUPS = 0; static SEPARATEGROUPS = 1; static VISIBLEGROUPS = 2; protected ROOT_CACHE_KEY = 'mmGroups:'; constructor() { } /** * Check if group mode of an activity is enabled. * * @param cmId Course module ID. * @param siteId Site ID. If not defined, current site. * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved with true if the activity has groups, resolved with false otherwise. */ activityHasGroups(cmId: number, siteId?: string, ignoreCache?: boolean): Promise { return this.getActivityGroupMode(cmId, siteId, ignoreCache).then((groupmode) => { return groupmode === CoreGroupsProvider.SEPARATEGROUPS || groupmode === CoreGroupsProvider.VISIBLEGROUPS; }).catch(() => { return false; }); } /** * Get the groups allowed in an activity. * * @param cmId Course module ID. * @param userId User ID. If not defined, use current user. * @param siteId Site ID. If not defined, current site. * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved when the groups are retrieved. */ getActivityAllowedGroups(cmId: number, userId?: number, siteId?: string, ignoreCache?: boolean): Promise { return CoreSites.instance.getSite(siteId).then((site) => { userId = userId || site.getUserId(); const params = { cmid: cmId, userid: userId, }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getActivityAllowedGroupsCacheKey(cmId, userId), updateFrequency: CoreSite.FREQUENCY_RARELY, }; if (ignoreCache) { preSets.getFromCache = false; preSets.emergencyCache = false; } return site.read('core_group_get_activity_allowed_groups', params, preSets).then((response) => { if (!response || !response.groups) { return Promise.reject(null); } return response; }); }); } /** * Get cache key for group mode WS calls. * * @param cmId Course module ID. * @param userId User ID. * @return Cache key. */ protected getActivityAllowedGroupsCacheKey(cmId: number, userId: number): string { return this.ROOT_CACHE_KEY + 'allowedgroups:' + cmId + ':' + userId; } /** * Get the groups allowed in an activity if they are allowed. * * @param cmId Course module ID. * @param userId User ID. If not defined, use current user. * @param siteId Site ID. If not defined, current site. * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved when the groups are retrieved. If not allowed, empty array will be returned. */ getActivityAllowedGroupsIfEnabled(cmId: number, userId?: number, siteId?: string, ignoreCache?: boolean): Promise { siteId = siteId || CoreSites.instance.getCurrentSiteId(); // Get real groupmode, in case it's forced by the course. return this.activityHasGroups(cmId, siteId, ignoreCache).then((hasGroups) => { if (hasGroups) { // Get the groups available for the user. return this.getActivityAllowedGroups(cmId, userId, siteId, ignoreCache); } return { groups: [] }; }); } /** * Helper function to get activity group info (group mode and list of groups). * * @param cmId Course module ID. * @param addAllParts Deprecated. * @param userId User ID. If not defined, use current user. * @param siteId Site ID. If not defined, current site. * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved with the group info. */ getActivityGroupInfo(cmId: number, addAllParts?: boolean, userId?: number, siteId?: string, ignoreCache?: boolean) : Promise { const groupInfo: CoreGroupInfo = { groups: [] }; return this.getActivityGroupMode(cmId, siteId, ignoreCache).then((groupMode) => { groupInfo.separateGroups = groupMode === CoreGroupsProvider.SEPARATEGROUPS; groupInfo.visibleGroups = groupMode === CoreGroupsProvider.VISIBLEGROUPS; if (groupInfo.separateGroups || groupInfo.visibleGroups) { return this.getActivityAllowedGroups(cmId, userId, siteId, ignoreCache); } return { groups: [], canaccessallgroups: false }; }).then((result) => { if (result.groups.length <= 0) { groupInfo.separateGroups = false; groupInfo.visibleGroups = false; groupInfo.defaultGroupId = 0; } else { // The "canaccessallgroups" field was added in 3.4. Add all participants for visible groups in previous versions. if (result.canaccessallgroups || (typeof result.canaccessallgroups == 'undefined' && groupInfo.visibleGroups)) { groupInfo.groups.push({ id: 0, name: Translate.instance.instant('core.allparticipants') }); groupInfo.defaultGroupId = 0; } else { groupInfo.defaultGroupId = result.groups[0].id; } groupInfo.groups = groupInfo.groups.concat(result.groups); } return groupInfo; }); } /** * Get the group mode of an activity. * * @param cmId Course module ID. * @param siteId Site ID. If not defined, current site. * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @return Promise resolved when the group mode is retrieved. */ getActivityGroupMode(cmId: number, siteId?: string, ignoreCache?: boolean): Promise { return CoreSites.instance.getSite(siteId).then((site) => { const params = { cmid: cmId, }; const preSets: CoreSiteWSPreSets = { cacheKey: this.getActivityGroupModeCacheKey(cmId), updateFrequency: CoreSite.FREQUENCY_RARELY, }; if (ignoreCache) { preSets.getFromCache = false; preSets.emergencyCache = false; } return site.read('core_group_get_activity_groupmode', params, preSets).then((response) => { if (!response || typeof response.groupmode == 'undefined') { return Promise.reject(null); } return response.groupmode; }); }); } /** * Get cache key for group mode WS calls. * * @param cmId Course module ID. * @return Cache key. */ protected getActivityGroupModeCacheKey(cmId: number): string { return this.ROOT_CACHE_KEY + 'groupmode:' + cmId; } /** * Get user groups in all the user enrolled courses. * * @param siteId Site to get the groups from. If not defined, use current site. * @return Promise resolved when the groups are retrieved. */ getAllUserGroups(siteId?: string): Promise { return CoreSites.instance.getSite(siteId).then((site) => { siteId = siteId || site.getId(); if (site.isVersionGreaterEqualThan('3.6')) { return this.getUserGroupsInCourse(0, siteId); } // @todo Get courses. }); } /** * Get user groups in all the supplied courses. * * @param courses List of courses or course ids to get the groups from. * @param siteId Site to get the groups from. If not defined, use current site. * @param userId ID of the user. If not defined, use the userId related to siteId. * @return Promise resolved when the groups are retrieved. */ getUserGroups(courses: any[], siteId?: string, userId?: number): Promise { // Get all courses one by one. const promises = courses.map((course) => { const courseId = typeof course == 'object' ? course.id : course; return this.getUserGroupsInCourse(courseId, siteId, userId); }); return Promise.all(promises).then((courseGroups) => { return [].concat(...courseGroups); }); } /** * Get user groups in a course. * * @param courseId ID of the course. 0 to get all enrolled courses groups (Moodle version > 3.6). * @param siteId Site to get the groups from. If not defined, use current site. * @param userId ID of the user. If not defined, use ID related to siteid. * @return Promise resolved when the groups are retrieved. */ getUserGroupsInCourse(courseId: number, siteId?: string, userId?: number): Promise { return CoreSites.instance.getSite(siteId).then((site) => { userId = userId || site.getUserId(); const data = { userid: userId, courseid: courseId, }; const preSets = { cacheKey: this.getUserGroupsInCourseCacheKey(courseId, userId), updateFrequency: CoreSite.FREQUENCY_RARELY, }; return site.read('core_group_get_course_user_groups', data, preSets).then((response) => { if (response && response.groups) { return response.groups; } else { return Promise.reject(null); } }); }); } /** * Get prefix cache key for user groups in course WS calls. * * @return Prefix Cache key. */ protected getUserGroupsInCoursePrefixCacheKey(): string { return this.ROOT_CACHE_KEY + 'courseGroups:'; } /** * Get cache key for user groups in course WS calls. * * @param courseId Course ID. * @param userId User ID. * @return Cache key. */ protected getUserGroupsInCourseCacheKey(courseId: number, userId: number): string { return this.getUserGroupsInCoursePrefixCacheKey() + courseId + ':' + userId; } /** * Invalidates activity allowed groups. * * @param cmId Course module ID. * @param userId User ID. If not defined, use current user. * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the data is invalidated. */ invalidateActivityAllowedGroups(cmId: number, userId?: number, siteId?: string): Promise { return CoreSites.instance.getSite(siteId).then((site) => { userId = userId || site.getUserId(); return site.invalidateWsCacheForKey(this.getActivityAllowedGroupsCacheKey(cmId, userId)); }); } /** * Invalidates activity group mode. * * @param cmId Course module ID. * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the data is invalidated. */ invalidateActivityGroupMode(cmId: number, siteId?: string): Promise { return CoreSites.instance.getSite(siteId).then((site) => { return site.invalidateWsCacheForKey(this.getActivityGroupModeCacheKey(cmId)); }); } /** * Invalidates all activity group info: mode and allowed groups. * * @param cmId Course module ID. * @param userId User ID. If not defined, use current user. * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the data is invalidated. */ invalidateActivityGroupInfo(cmId: number, userId?: number, siteId?: string): Promise { const promises = []; promises.push(this.invalidateActivityAllowedGroups(cmId, userId, siteId)); promises.push(this.invalidateActivityGroupMode(cmId, siteId)); return Promise.all(promises); } /** * Invalidates user groups in all user enrolled courses. * * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the data is invalidated. */ invalidateAllUserGroups(siteId?: string): Promise { return CoreSites.instance.getSite(siteId).then((site) => { if (site.isVersionGreaterEqualThan('3.6')) { return this.invalidateUserGroupsInCourse(0, siteId); } return site.invalidateWsCacheForKeyStartingWith(this.getUserGroupsInCoursePrefixCacheKey()); }); } /** * Invalidates user groups in courses. * * @param courses List of courses or course ids. * @param siteId Site ID. If not defined, current site. * @param userId User ID. If not defined, use current user. * @return Promise resolved when the data is invalidated. */ invalidateUserGroups(courses: any[], siteId?: string, userId?: number): Promise { return CoreSites.instance.getSite(siteId).then((site) => { userId = userId || site.getUserId(); const promises = courses.map((course) => { const courseId = typeof course == 'object' ? course.id : course; return this.invalidateUserGroupsInCourse(courseId, site.id, userId); }); return Promise.all(promises); }); } /** * Invalidates user groups in course. * * @param courseId ID of the course. 0 to get all enrolled courses groups (Moodle version > 3.6). * @param siteId Site ID. If not defined, current site. * @param userId User ID. If not defined, use current user. * @return Promise resolved when the data is invalidated. */ invalidateUserGroupsInCourse(courseId: number, siteId?: string, userId?: number): Promise { return CoreSites.instance.getSite(siteId).then((site) => { userId = userId || site.getUserId(); return site.invalidateWsCacheForKey(this.getUserGroupsInCourseCacheKey(courseId, userId)); }); } /** * Validate a group ID. If the group is not visible by the user, it will return the first group ID. * * @param groupId Group ID to validate. * @param groupInfo Group info. * @return Group ID to use. */ validateGroupId(groupId: number, groupInfo: CoreGroupInfo): number { if (groupId > 0 && groupInfo && groupInfo.groups && groupInfo.groups.length > 0) { // Check if the group is in the list of groups. if (groupInfo.groups.some((group) => groupId == group.id)) { return groupId; } } return groupInfo.defaultGroupId; } } export class CoreGroups extends makeSingleton(CoreGroupsProvider) {} /** * Group info for an activity. */ export type CoreGroupInfo = { /** * List of groups. */ groups?: any[]; /** * Whether it's separate groups. */ separateGroups?: boolean; /** * Whether it's visible groups. */ visibleGroups?: boolean; /** * The group ID to use by default. If all participants is visible, 0 will be used. First group ID otherwise. */ defaultGroupId?: number; };