diff --git a/src/addon/badges/badges.module.ts b/src/addon/badges/badges.module.ts new file mode 100644 index 000000000..b4416dfa3 --- /dev/null +++ b/src/addon/badges/badges.module.ts @@ -0,0 +1,44 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { NgModule } from '@angular/core'; +import { AddonBadgesProvider } from './providers/badges'; +import { AddonBadgesUserHandler } from './providers/user-handler'; +import { AddonBadgesMyBadgesLinkHandler } from './providers/mybadges-link-handler'; +import { AddonBadgesBadgeLinkHandler } from './providers/badge-link-handler'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; +import { CoreUserDelegate } from '@core/user/providers/user-delegate'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + AddonBadgesProvider, + AddonBadgesUserHandler, + AddonBadgesMyBadgesLinkHandler, + AddonBadgesBadgeLinkHandler + ] +}) +export class AddonBadgesModule { + constructor(userDelegate: CoreUserDelegate, userHandler: AddonBadgesUserHandler, + contentLinksDelegate: CoreContentLinksDelegate, myBadgesLinkHandler: AddonBadgesMyBadgesLinkHandler, + badgeLinkHandler: AddonBadgesBadgeLinkHandler) { + + userDelegate.registerHandler(userHandler); + contentLinksDelegate.registerHandler(myBadgesLinkHandler); + contentLinksDelegate.registerHandler(badgeLinkHandler); + } +} diff --git a/src/addon/badges/lang/en.json b/src/addon/badges/lang/en.json new file mode 100644 index 000000000..4443773c4 --- /dev/null +++ b/src/addon/badges/lang/en.json @@ -0,0 +1,13 @@ +{ + "badgedetails": "Badge details", + "badges": "Badges", + "contact": "Contact", + "dateawarded": "Date issued", + "expired": "Expired", + "expirydate": "Expiry date", + "issuancedetails": "Badge expiry", + "issuerdetails": "Issuer details", + "issuername": "Issuer name", + "nobadges": "There are no badges available.", + "recipientdetails": "Recipient details" +} diff --git a/src/addon/badges/pages/issued-badge/issued-badge.html b/src/addon/badges/pages/issued-badge/issued-badge.html new file mode 100644 index 000000000..e28898229 --- /dev/null +++ b/src/addon/badges/pages/issued-badge/issued-badge.html @@ -0,0 +1,87 @@ +<ion-header> + <ion-navbar> + <ion-title>{{badge.name}}</ion-title> + </ion-navbar> +</ion-header> +<ion-content> + <ion-refresher [enabled]="badgeLoaded" (ionRefresh)="refreshEvent($event)"> + <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> + </ion-refresher> + <core-loading [hideUntil]="badgeLoaded"> + + <ion-item-group> + <ion-item text-wrap class="item-avatar-center"> + <img *ngIf="badge.badgeurl" class="avatar" [src]="badge.badgeurl" core-external-content [alt]="badge.name"> + <ion-badge color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire"> + {{ 'addon.badges.expired' | translate }} + </ion-badge> + </ion-item> + </ion-item-group> + + <ion-item-group *ngIf="user.fullname"> + <ion-item-divider color="light"> + <h2>{{ 'addon.badges.recipientdetails' | translate}}</h2> + </ion-item-divider> + <ion-item text-wrap> + <h2>{{ 'core.name' | translate}}</h2> + <p> + <core-format-text clean="true" [text]="user.fullname"></core-format-text> + </p> + </ion-item> + </ion-item-group> + + <ion-item-group> + <ion-item-divider color="light"> + <h2>{{ 'addon.badges.issuerdetails' | translate}}</h2> + </ion-item-divider> + <ion-item text-wrap *ngIf="badge.issuername"> + <h2>{{ 'addon.badges.issuername' | translate}}</h2> + <p> + <core-format-text clean="true" [text]="badge.issuername"></core-format-text> + </p> + </ion-item> + <ion-item text-wrap *ngIf="badge.issuercontact"> + <h2>{{ 'addon.badges.contact' | translate}}</h2> + <p> + <core-format-text clean="true" [text]="badge.issuercontact"></core-format-text> + </p> + </ion-item> + </ion-item-group> + + <ion-item-group> + <ion-item-divider color="light"> + <h2>{{ 'addon.badges.badgedetails' | translate}}</h2> + </ion-item-divider> + <ion-item text-wrap *ngIf="badge.name"> + <h2>{{ 'core.name' | translate}}</h2> + <p>{{badge.name}}</p> + </ion-item> + <ion-item text-wrap *ngIf="badge.description"> + <h2>{{ 'core.description' | translate}}</h2> + <p> + <core-format-text clean="true" [text]="badge.description"></core-format-text> + </p> + </ion-item> + <ion-item text-wrap *ngIf="course.fullname"> + <h2>{{ 'core.course' | translate}}</h2> + <p> + <core-format-text clean="true" [text]="course.fullname"></core-format-text> + </p> + </ion-item> + </ion-item-group> + + <ion-item-group> + <ion-item-divider color="light"> + <h2>{{ 'addon.badges.issuancedetails' | translate}}</h2> + </ion-item-divider> + <ion-item text-wrap *ngIf="badge.dateissued"> + <h2>{{ 'addon.badges.dateawarded' | translate}}</h2> + <p>{{badge.dateissued | coreToLocaleString }}</p> + </ion-item> + <ion-item text-wrap *ngIf="badge.dateexpire"> + <h2>{{ 'addon.badges.expirydate' | translate}}</h2> + <p>{{badge.dateexpire | coreToLocaleString }}</p> + </ion-item> + </ion-item-group> + </core-loading> +</ion-content> diff --git a/src/addon/badges/pages/issued-badge/issued-badge.module.ts b/src/addon/badges/pages/issued-badge/issued-badge.module.ts new file mode 100644 index 000000000..cdc621998 --- /dev/null +++ b/src/addon/badges/pages/issued-badge/issued-badge.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; +import { AddonBadgesIssuedBadgePage } from './issued-badge'; + +@NgModule({ + declarations: [ + AddonBadgesIssuedBadgePage, + ], + imports: [ + CoreComponentsModule, + CoreDirectivesModule, + CorePipesModule, + IonicPageModule.forChild(AddonBadgesIssuedBadgePage), + TranslateModule.forChild() + ], +}) +export class AddonBadgesIssuedBadgePageModule {} diff --git a/src/addon/badges/pages/issued-badge/issued-badge.ts b/src/addon/badges/pages/issued-badge/issued-badge.ts new file mode 100644 index 000000000..7b8f6d5a1 --- /dev/null +++ b/src/addon/badges/pages/issued-badge/issued-badge.ts @@ -0,0 +1,114 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, ViewChild } from '@angular/core'; +import { IonicPage, Content, PopoverController, NavParams } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonBadgesProvider } from '../../providers/badges'; +import { CoreTimeUtilsProvider } from '@providers/utils/time'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUserProvider } from '@core/user/providers/user'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; + +/** + * Page that displays the list of calendar events. + */ +@IonicPage({ segment: 'addon-badges-issued-badge' }) +@Component({ + selector: 'page-addon-badges-issued-badge', + templateUrl: 'issued-badge.html', +}) +export class AddonBadgesIssuedBadgePage { + @ViewChild(Content) content: Content; + + courseId: number; + userId: number; + badgeHash: string; + user: any = {}; + course: any = {}; + badge: any = {}; + + badgeLoaded = false; + currentTime = 0; + + constructor(private translate: TranslateService, private badgesProvider: AddonBadgesProvider, navParams: NavParams, + private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, + private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider, + private coursesProvider: CoreCoursesProvider) { + + this.courseId = navParams.get('courseId') || 0; // Use 0 for site badges. + this.userId = navParams.get('userId') || sitesProvider.getCurrentSite().getUserId(); + this.badgeHash = navParams.get('badgeHash'); + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + + this.fetchIssuedBadge().finally(() => { + this.badgeLoaded = true; + }); + } + + /** + * Fetch the issued badge required for the view. + * + * @return {Promise<any>} Promise resolved when done. + */ + fetchIssuedBadge(): Promise<any> { + const promises = []; + + this.currentTime = this.timeUtils.timestamp(); + let promise = this.userProvider.getProfile(this.userId, this.courseId, true).then((user) => { + this.user = user; + }); + promises.push(promise); + + promise = this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { + badges.forEach((badge) => { + if (this.badgeHash == badge.uniquehash) { + this.badge = badge; + if (badge.courseid) { + return this.coursesProvider.getUserCourse(badge.courseid, true).then((course) => { + this.course = course; + }).catch(() => { + // Maybe an old deleted course. + this.course = null; + }); + } + } + }); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'Error getting badge data.'); + }); + promises.push(promise); + + return Promise.all(promises); + } + + /** + * Refresh the badges. + * + * @param {any} refresher Refresher. + */ + refreshBadges(refresher: any): void { + this.badgesProvider.invalidateUserBadges(this.courseId, this.userId).finally(() => { + this.fetchIssuedBadge().finally(() => { + refresher.complete(); + }); + }); + } +} diff --git a/src/addon/badges/pages/user-badges/user-badges.html b/src/addon/badges/pages/user-badges/user-badges.html new file mode 100644 index 000000000..9768f7901 --- /dev/null +++ b/src/addon/badges/pages/user-badges/user-badges.html @@ -0,0 +1,29 @@ +<ion-header> + <ion-navbar> + <ion-title>{{ 'addon.badges.badges' | translate }}</ion-title> + </ion-navbar> +</ion-header> +<core-split-view> + <ion-content> + <ion-refresher [enabled]="badgesLoaded" (ionRefresh)="refreshBadges($event)"> + <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> + </ion-refresher> + <core-loading [hideUntil]="badgesLoaded"> + <core-empty-box *ngIf="!badges || badges.length == 0" icon="trophy" [message]="'addon.badges.nobadges' | translate"> + </core-empty-box> + + <ion-list *ngIf="badges && badges.length" no-margin> + <a ion-item text-wrap *ngFor="let badge of badges" [title]="badge.name" (click)="loadIssuedBadge(badge.uniquehash)" [class.core-split-item-selected]="badge.uniquehash == badgeHash"> + <ion-avatar item-start> + <img [src]="badge.badgeurl" [alt]="badge.name" item-start core-external-content> + </ion-avatar> + <h2><core-format-text [text]="badge.name"></core-format-text></h2> + <p>{{ badge.dateissued | coreToLocaleString }}</p> + <ion-badge item-end color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire"> + {{ 'addon.badges.expired' | translate }} + </ion-badge> + </a> + </ion-list> + </core-loading> + </ion-content> +</core-split-view> \ No newline at end of file diff --git a/src/addon/badges/pages/user-badges/user-badges.module.ts b/src/addon/badges/pages/user-badges/user-badges.module.ts new file mode 100644 index 000000000..2609cdf00 --- /dev/null +++ b/src/addon/badges/pages/user-badges/user-badges.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; +import { AddonBadgesUserBadgesPage } from './user-badges'; + +@NgModule({ + declarations: [ + AddonBadgesUserBadgesPage, + ], + imports: [ + CoreComponentsModule, + CoreDirectivesModule, + CorePipesModule, + IonicPageModule.forChild(AddonBadgesUserBadgesPage), + TranslateModule.forChild() + ], +}) +export class AddonBadgesUserBadgesPageModule {} diff --git a/src/addon/badges/pages/user-badges/user-badges.ts b/src/addon/badges/pages/user-badges/user-badges.ts new file mode 100644 index 000000000..35029793c --- /dev/null +++ b/src/addon/badges/pages/user-badges/user-badges.ts @@ -0,0 +1,104 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, ViewChild } from '@angular/core'; +import { IonicPage, Content, NavParams, NavController } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonBadgesProvider } from '../../providers/badges'; +import { CoreTimeUtilsProvider } from '@providers/utils/time'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreSplitViewComponent } from '@components/split-view/split-view'; + +/** + * Page that displays the list of calendar events. + */ +@IonicPage({ segment: 'addon-badges-user-badges' }) +@Component({ + selector: 'page-addon-badges-user-badges', + templateUrl: 'user-badges.html', +}) +export class AddonBadgesUserBadgesPage { + @ViewChild(Content) content: Content; + @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; + + courseId: number; + userId: number; + + badgesLoaded = false; + badges = []; + currentTime = 0; + badgeHash: string; + + constructor(private translate: TranslateService, private badgesProvider: AddonBadgesProvider, + navParams: NavParams, private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, + private sitesProvider: CoreSitesProvider) { + + this.courseId = navParams.get('courseId') || 0; // Use 0 for site badges. + this.userId = navParams.get('userId') || sitesProvider.getCurrentSite().getUserId(); + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + + this.fetchBadges().finally(() => { + if (!this.badgeHash && this.splitviewCtrl.isOn() && this.badges.length > 0) { + // Take first and load it. + this.loadIssuedBadge(this.badges[0].uniquehash); + } + this.badgesLoaded = true; + }); + } + + /** + * Fetch all the badges required for the view. + * + * @return {Promise<any>} Promise resolved when done. + */ + fetchBadges(): Promise<any> { + this.currentTime = this.timeUtils.timestamp(); + + return this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { + this.badges = badges; + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'Error getting badges data.'); + }); + } + + /** + * Refresh the badges. + * + * @param {any} refresher Refresher. + */ + refreshBadges(refresher: any): void { + this.badgesProvider.invalidateUserBadges(this.courseId, this.userId).finally(() => { + this.fetchBadges().finally(() => { + refresher.complete(); + }); + }); + } + + /** + * Navigate to a particular badge. + * + * @param {string} badgeHash Badge to load. + */ + loadIssuedBadge(badgeHash: string): void { + this.badgeHash = badgeHash; + const params = {courseId: this.courseId, userId: this.userId, badgeHash: badgeHash}; + this.splitviewCtrl.push('AddonBadgesIssuedBadgePage', params); + } +} diff --git a/src/addon/badges/providers/badge-link-handler.ts b/src/addon/badges/providers/badge-link-handler.ts new file mode 100644 index 000000000..bcaf759e0 --- /dev/null +++ b/src/addon/badges/providers/badge-link-handler.ts @@ -0,0 +1,67 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; +import { AddonBadgesProvider } from './badges'; + +/** + * Handler to treat links to user participants page. + */ +@Injectable() +export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase { + name = 'AddonBadgesBadgeLinkHandler'; + pattern = /\/badges\/badge\.php.*([\?\&]hash=)/; + + constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) { + super(); + } + + /** + * Get the list of actions for a link (url). + * + * @param {string[]} siteIds List of sites the URL belongs to. + * @param {string} url The URL to treat. + * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param {number} [courseId] Course ID related to the URL. Optional but recommended. + * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. + */ + getActions(siteIds: string[], url: string, params: any, courseId?: number): + CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { + + return [{ + action: (siteId, navCtrl?): void => { + // Always use redirect to make it the new history root (to avoid "loops" in history). + this.loginHelper.redirect('AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId); + } + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param {string} siteId The site ID. + * @param {string} url The URL to treat. + * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param {number} [courseId] Course ID related to the URL. Optional but recommended. + * @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> { + + return this.badgesProvider.isPluginEnabled(siteId); + } +} diff --git a/src/addon/badges/providers/badges.ts b/src/addon/badges/providers/badges.ts new file mode 100644 index 000000000..2245ad1fb --- /dev/null +++ b/src/addon/badges/providers/badges.ts @@ -0,0 +1,110 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CoreLoggerProvider } from '@providers/logger'; +import { CoreSitesProvider } from '@providers/sites'; + +/** + * Service to handle badges. + */ +@Injectable() +export class AddonBadgesProvider { + protected logger; + protected ROOT_CACHE_KEY = 'mmaBadges:'; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { + this.logger = logger.getInstance('AddonBadgesProvider'); + } + + /** + * Returns whether or not the badge plugin is enabled for a certain site. + * + * This method is called quite often and thus should only perform a quick + * check, we should not be calling WS from here. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise<boolean>} Promise resolved with true if enabled, false otherwise. + */ + isPluginEnabled(siteId?: string): Promise<boolean> { + + return this.sitesProvider.getSite(siteId).then((site) => { + if (!site.canUseAdvancedFeature('enablebadges')) { + return false; + } else if (!site.wsAvailable('core_course_get_user_navigation_options')) { + return false; + } + + return true; + }); + } + + /** + * Get the cache key for the get badges call. + * + * @param {number} courseId ID of the course to get the badges from. + * @param {number} userId ID of the user to get the badges from. + * @return {string} Cache key. + */ + protected getBadgesCacheKey(courseId: number, userId: number): string { + return this.ROOT_CACHE_KEY + 'badges:' + courseId + ':' + userId; + } + + /** + * Get issued badges for a certain user in a course. + * + * @param {number} courseId ID of the course to get the badges from. + * @param {number} userId ID of the user to get the badges from. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise<any>}Promise to be resolved when the badges are retrieved. + */ + getUserBadges(courseId: number, userId: number, siteId?: string): Promise<any> { + + this.logger.debug('Get badges for course ' + courseId); + + return this.sitesProvider.getSite(siteId).then((site) => { + + const data = { + courseid : courseId, + userid : userId + }, + presets = { + cacheKey: this.getBadgesCacheKey(courseId, userId) + }; + + return site.read('core_badges_get_user_badges', data, presets).then((response) => { + if (response && response.badges) { + return response.badges; + } else { + return Promise.reject(null); + } + }); + }); + } + + /** + * Invalidate get badges WS call. + * + * @param {number} courseId Course ID. + * @param {number} userId ID of the user to get the badges from. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise<any>} Promise resolved when data is invalidated. + */ + invalidateUserBadges(courseId: number, userId: number, siteId?: string): Promise<any> { + + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getBadgesCacheKey(courseId, userId)); + }); + } +} diff --git a/src/addon/badges/providers/mybadges-link-handler.ts b/src/addon/badges/providers/mybadges-link-handler.ts new file mode 100644 index 000000000..59a3025da --- /dev/null +++ b/src/addon/badges/providers/mybadges-link-handler.ts @@ -0,0 +1,68 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; +import { AddonBadgesProvider } from './badges'; + +/** + * Handler to treat links to user participants page. + */ +@Injectable() +export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase { + name = 'AddonBadgesMyBadgesLinkHandler'; + featureName = '$mmUserDelegate_mmaBadges'; + pattern = /\/badges\/mybadges\.php/; + + constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) { + super(); + } + + /** + * Get the list of actions for a link (url). + * + * @param {string[]} siteIds List of sites the URL belongs to. + * @param {string} url The URL to treat. + * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param {number} [courseId] Course ID related to the URL. Optional but recommended. + * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. + */ + getActions(siteIds: string[], url: string, params: any, courseId?: number): + CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { + + return [{ + action: (siteId, navCtrl?): void => { + // Always use redirect to make it the new history root (to avoid "loops" in history). + this.loginHelper.redirect('AddonBadgesUserBadgesPage', {}, siteId); + } + }]; + } + + /** + * Check if the handler is enabled for a certain site (site + user) and a URL. + * If not defined, defaults to true. + * + * @param {string} siteId The site ID. + * @param {string} url The URL to treat. + * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} + * @param {number} [courseId] Course ID related to the URL. Optional but recommended. + * @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> { + + return this.badgesProvider.isPluginEnabled(siteId); + } +} diff --git a/src/addon/badges/providers/user-handler.ts b/src/addon/badges/providers/user-handler.ts new file mode 100644 index 000000000..8e8ea9340 --- /dev/null +++ b/src/addon/badges/providers/user-handler.ts @@ -0,0 +1,75 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; +import { AddonBadgesProvider } from './badges'; + +/** + * Profile badges handler. + */ +@Injectable() +export class AddonBadgesUserHandler implements CoreUserProfileHandler { + name = 'mmaBadges'; + priority = 50; + type = CoreUserDelegate.TYPE_NEW_PAGE; + + constructor(protected badgesProvider: AddonBadgesProvider) { } + + /** + * Check if handler is enabled. + * + * @return {Promise<boolean>} Always enabled. + */ + isEnabled(): Promise<boolean> { + return this.badgesProvider.isPluginEnabled(); + } + + /** + * Check if handler is enabled for this user in this context. + * + * @param {any} user User to check. + * @param {number} courseId Course ID. + * @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. + * @return {boolean} True if enabled, false otherwise. + */ + isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean { + + if (navOptions && typeof navOptions.badges != 'undefined') { + return navOptions.badges; + } + + // If we reach here, it means we are opening the user site profile. + return true; + } + + /** + * Returns the data needed to render the handler. + * + * @return {CoreUserProfileHandlerData} Data needed to render the handler. + */ + getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { + return { + icon: 'trophy', + title: 'addon.badges.badges', + class: '', + action: (event, navCtrl, user, courseId): void => { + event.preventDefault(); + event.stopPropagation(); + navCtrl.push('AddonBadgesUserBadgesPage', {courseId: courseId, userId: user.id }); + } + }; + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1ae090cd9..eaf546c1a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -69,8 +69,9 @@ import { CoreSitePluginsModule } from '@core/siteplugins/siteplugins.module'; import { CoreCompileModule } from '@core/compile/compile.module'; // Addon modules. +import { AddonBadgesModule } from '@addon/badges/badges.module'; import { AddonCalendarModule } from '@addon/calendar/calendar.module'; -import { AddonCompetencyModule } from '../addon/competency/competency.module'; +import { AddonCompetencyModule } from '@addon/competency/competency.module'; import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module'; import { AddonFilesModule } from '@addon/files/files.module'; import { AddonModBookModule } from '@addon/mod/book/book.module'; @@ -150,6 +151,7 @@ export const CORE_PROVIDERS: any[] = [ CoreSettingsModule, CoreSitePluginsModule, CoreCompileModule, + AddonBadgesModule, AddonCalendarModule, AddonCompetencyModule, AddonUserProfileFieldModule, diff --git a/src/core/user/providers/user-handler.ts b/src/core/user/providers/user-handler.ts index 5ddb638b2..7414dd287 100644 --- a/src/core/user/providers/user-handler.ts +++ b/src/core/user/providers/user-handler.ts @@ -41,8 +41,8 @@ export class CoreUserProfileMailHandler implements CoreUserProfileHandler { * * @param {any} user User to check. * @param {number} courseId Course ID. - * @param {any} [navOptions] Course navigation options for current user. See $mmCourses#getUserNavigationOptions. - * @param {any} [admOptions] Course admin options for current user. See $mmCourses#getUserAdministrationOptions. + * @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. * @return {boolean|Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise. */ isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {