From 138f83d97952d248a20415927b555fcb33eb0c67 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 14 Feb 2018 12:46:40 +0100 Subject: [PATCH 1/6] MOBILE-2325 badges: Show new page in user profile --- src/addon/badges/badges.module.ts | 34 +++++++ src/addon/badges/lang/en.json | 13 +++ src/addon/badges/providers/badges.ts | 110 +++++++++++++++++++++ src/addon/badges/providers/user-handler.ts | 75 ++++++++++++++ src/app/app.module.ts | 4 +- src/core/user/providers/user-handler.ts | 4 +- 6 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/addon/badges/badges.module.ts create mode 100644 src/addon/badges/lang/en.json create mode 100644 src/addon/badges/providers/badges.ts create mode 100644 src/addon/badges/providers/user-handler.ts diff --git a/src/addon/badges/badges.module.ts b/src/addon/badges/badges.module.ts new file mode 100644 index 000000000..51a2f2c31 --- /dev/null +++ b/src/addon/badges/badges.module.ts @@ -0,0 +1,34 @@ +// (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 { CoreUserDelegate } from '../../core/user/providers/user-delegate'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + AddonBadgesProvider, + AddonBadgesUserHandler + ] +}) +export class AddonBadgesModule { + constructor(userDelegate: CoreUserDelegate, userHandler: AddonBadgesUserHandler) { + userDelegate.registerHandler(userHandler); + } +} 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/providers/badges.ts b/src/addon/badges/providers/badges.ts new file mode 100644 index 000000000..b27802e9d --- /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} Promise resolved with true if enabled, false otherwise. + */ + isPluginEnabled(siteId?: string): Promise { + + 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}Promise to be resolved when the badges are retrieved. + */ + getUserBadges(courseId: number, userId: number, siteId?: string): Promise { + + 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} Promise resolved when data is invalidated. + */ + invalidateUserBadges(courseId: number, userId: number, siteId?: string): Promise { + + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getBadgesCacheKey(courseId, userId)); + }); + } +} diff --git a/src/addon/badges/providers/user-handler.ts b/src/addon/badges/providers/user-handler.ts new file mode 100644 index 000000000..12bd8ee95 --- /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} Always enabled. + */ + isEnabled(): Promise { + 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: 'ion-trophy', + title: 'addon.badges.badges', + class: '', + action: (event, navCtrl, user, courseId): void => { + event.preventDefault(); + event.stopPropagation(); + //navCtrl.push(); + } + }; + } +} 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} Promise resolved with true if enabled, resolved with false otherwise. */ isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { From 68ff3026e41318d5ffb0d9c1e716917fb3389474 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 14 Feb 2018 17:21:03 +0100 Subject: [PATCH 2/6] MOBILE-2325 badges: Issued badges for user page --- .../badges/pages/user-badges/user-badges.html | 28 +++++ .../pages/user-badges/user-badges.module.ts | 35 ++++++ .../badges/pages/user-badges/user-badges.ts | 105 ++++++++++++++++++ src/addon/badges/providers/user-handler.ts | 4 +- 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 src/addon/badges/pages/user-badges/user-badges.html create mode 100644 src/addon/badges/pages/user-badges/user-badges.module.ts create mode 100644 src/addon/badges/pages/user-badges/user-badges.ts 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..58c3b5d82 --- /dev/null +++ b/src/addon/badges/pages/user-badges/user-badges.html @@ -0,0 +1,28 @@ + + + {{ 'addon.badges.badges' | translate }} + + + + + + + + + + + + + + {{badge.name}} + + {{ 'addon.badges.expired' | translate }} + +

+

{{ badge.dateissued | coreToLocaleString }}

+
+
+ +
+
+
\ 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..e831ae877 --- /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..795f0c304 --- /dev/null +++ b/src/addon/badges/pages/user-badges/user-badges.ts @@ -0,0 +1,105 @@ +// (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 = ''; + + constructor(private translate: TranslateService, private badgesProvider: AddonBadgesProvider, navParams: NavParams, + private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, + sitesProvider: CoreSitesProvider, private navCtrl: NavController) { + + 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(() => { + this.badgesLoaded = true; + }); + } + + /** + * Fetch all the badges required for the view. + * + * @return {Promise} Promise resolved when done. + */ + fetchBadges(): Promise { + this.currentTime = this.timeUtils.timestamp(); + + return this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { + this.badges = badges; + }).catch((message) => { + if (message) { + this.domUtils.showErrorModal(message); + } else { + this.domUtils.showErrorModal('Error getting badges data.'); + } + + return Promise.reject(null); + }); + } + + /** + * 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; + //this.splitviewCtrl.push('', { id: }); + } +} diff --git a/src/addon/badges/providers/user-handler.ts b/src/addon/badges/providers/user-handler.ts index 12bd8ee95..a6257089c 100644 --- a/src/addon/badges/providers/user-handler.ts +++ b/src/addon/badges/providers/user-handler.ts @@ -62,13 +62,13 @@ export class AddonBadgesUserHandler implements CoreUserProfileHandler { */ getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { return { - icon: 'ion-trophy', + icon: 'trophy', title: 'addon.badges.badges', class: '', action: (event, navCtrl, user, courseId): void => { event.preventDefault(); event.stopPropagation(); - //navCtrl.push(); + navCtrl.push('AddonBadgesUserBadgesPage', {courseId: courseId, userId: user.id }); } }; } From 0bf49ac41db3242ad716d0aecd986a61e42cdf53 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 14 Feb 2018 22:26:16 +0100 Subject: [PATCH 3/6] MOBILE-2325 badges: Issued badge page --- .../pages/issued-badge/issued-badge.html | 87 +++++++++++++ .../pages/issued-badge/issued-badge.module.ts | 35 ++++++ .../badges/pages/issued-badge/issued-badge.ts | 114 ++++++++++++++++++ .../badges/pages/user-badges/user-badges.ts | 23 ++-- 4 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 src/addon/badges/pages/issued-badge/issued-badge.html create mode 100644 src/addon/badges/pages/issued-badge/issued-badge.module.ts create mode 100644 src/addon/badges/pages/issued-badge/issued-badge.ts 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..656d54114 --- /dev/null +++ b/src/addon/badges/pages/issued-badge/issued-badge.html @@ -0,0 +1,87 @@ + + + {{badge.name}} + + + + + + + + +
+
+ {{badge.name}} + + {{ 'addon.badges.expired' | translate }} + +
+
+ +
+
+

{{ 'addon.badges.recipientdetails' | translate}}

+
+
+

{{ 'core.name' | translate}}

+

+ +

+
+
+ +
+
+

{{ 'addon.badges.issuerdetails' | translate}}

+
+
+

{{ 'addon.badges.issuername' | translate}}

+

+ +

+
+
+

{{ 'addon.badges.contact' | translate}}

+

+ +

+
+
+ +
+
+

{{ 'addon.badges.badgedetails' | translate}}

+
+
+

{{ 'core.name' | translate}}

+

{{badge.name}}

+
+
+

{{ 'core.description' | translate}}

+

+ +

+
+
+

{{ 'core.course' | translate}}

+

+ +

+
+
+ +
+
+

{{ 'addon.badges.issuancedetails' | translate}}

+
+
+

{{ 'addon.badges.dateawarded' | translate}}

+

{{badge.dateissued | coreToLocaleString }}

+
+
+

{{ 'addon.badges.expirydate' | translate}}

+

{{badge.dateexpire | coreToLocaleString }}

+
+
+
+
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..fc3365801 --- /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..8beceaf63 --- /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} Promise resolved when done. + */ + fetchIssuedBadge(): Promise { + 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.ts b/src/addon/badges/pages/user-badges/user-badges.ts index 795f0c304..0855e5346 100644 --- a/src/addon/badges/pages/user-badges/user-badges.ts +++ b/src/addon/badges/pages/user-badges/user-badges.ts @@ -39,11 +39,11 @@ export class AddonBadgesUserBadgesPage { badgesLoaded = false; badges = []; currentTime = 0; - badgeHash = ''; + badgeHash: string; - constructor(private translate: TranslateService, private badgesProvider: AddonBadgesProvider, navParams: NavParams, - private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - sitesProvider: CoreSitesProvider, private navCtrl: NavController) { + 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(); @@ -55,6 +55,10 @@ export class AddonBadgesUserBadgesPage { 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; }); } @@ -70,13 +74,7 @@ export class AddonBadgesUserBadgesPage { return this.badgesProvider.getUserBadges(this.courseId, this.userId).then((badges) => { this.badges = badges; }).catch((message) => { - if (message) { - this.domUtils.showErrorModal(message); - } else { - this.domUtils.showErrorModal('Error getting badges data.'); - } - - return Promise.reject(null); + this.domUtils.showErrorModalDefault(message, 'Error getting badges data.'); }); } @@ -100,6 +98,7 @@ export class AddonBadgesUserBadgesPage { */ loadIssuedBadge(badgeHash: string): void { this.badgeHash = badgeHash; - //this.splitviewCtrl.push('', { id: }); + const params = {courseId: this.courseId, userId: this.userId, badgeHash: badgeHash}; + this.splitviewCtrl.push('AddonBadgesIssuedBadgePage', params); } } From 21348c78801d4f27f23d8111ab84d38373ac99d2 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Thu, 15 Feb 2018 14:31:12 +0100 Subject: [PATCH 4/6] MOBILE-2325 badges: Add link handlers --- src/addon/badges/badges.module.ts | 14 +++- .../badges/providers/badge-link-handler.ts | 67 ++++++++++++++++++ .../badges/providers/mybadges-link-handler.ts | 68 +++++++++++++++++++ 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/addon/badges/providers/badge-link-handler.ts create mode 100644 src/addon/badges/providers/mybadges-link-handler.ts diff --git a/src/addon/badges/badges.module.ts b/src/addon/badges/badges.module.ts index 51a2f2c31..f7973eec8 100644 --- a/src/addon/badges/badges.module.ts +++ b/src/addon/badges/badges.module.ts @@ -15,6 +15,9 @@ 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({ @@ -24,11 +27,18 @@ import { CoreUserDelegate } from '../../core/user/providers/user-delegate'; ], providers: [ AddonBadgesProvider, - AddonBadgesUserHandler + AddonBadgesUserHandler, + AddonBadgesMyBadgesLinkHandler, + AddonBadgesBadgeLinkHandler ] }) export class AddonBadgesModule { - constructor(userDelegate: CoreUserDelegate, userHandler: AddonBadgesUserHandler) { + 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/providers/badge-link-handler.ts b/src/addon/badges/providers/badge-link-handler.ts new file mode 100644 index 000000000..0ea9fcf1f --- /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} List of (or promise resolved with list of) actions. + */ + getActions(siteIds: string[], url: string, params: any, courseId?: number): + CoreContentLinksAction[] | Promise { + + 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} Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { + + return this.badgesProvider.isPluginEnabled(siteId); + } +} 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..912630288 --- /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} List of (or promise resolved with list of) actions. + */ + getActions(siteIds: string[], url: string, params: any, courseId?: number): + CoreContentLinksAction[] | Promise { + + 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} Whether the handler is enabled for the URL and site. + */ + isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise { + + return this.badgesProvider.isPluginEnabled(siteId); + } +} From 7b159b4a2f7fc416edfd86e14c7aac830f92ba5b Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 16 Mar 2018 11:33:12 +0100 Subject: [PATCH 5/6] MOBILE-2325 badges: Use new aliases in imports --- src/addon/badges/badges.module.ts | 4 ++-- .../badges/pages/issued-badge/issued-badge.module.ts | 6 +++--- src/addon/badges/pages/issued-badge/issued-badge.ts | 10 +++++----- .../badges/pages/user-badges/user-badges.module.ts | 6 +++--- src/addon/badges/pages/user-badges/user-badges.ts | 8 ++++---- src/addon/badges/providers/badge-link-handler.ts | 6 +++--- src/addon/badges/providers/badges.ts | 4 ++-- src/addon/badges/providers/mybadges-link-handler.ts | 6 +++--- src/addon/badges/providers/user-handler.ts | 2 +- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/addon/badges/badges.module.ts b/src/addon/badges/badges.module.ts index f7973eec8..b4416dfa3 100644 --- a/src/addon/badges/badges.module.ts +++ b/src/addon/badges/badges.module.ts @@ -17,8 +17,8 @@ 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'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; +import { CoreUserDelegate } from '@core/user/providers/user-delegate'; @NgModule({ declarations: [ diff --git a/src/addon/badges/pages/issued-badge/issued-badge.module.ts b/src/addon/badges/pages/issued-badge/issued-badge.module.ts index fc3365801..cdc621998 100644 --- a/src/addon/badges/pages/issued-badge/issued-badge.module.ts +++ b/src/addon/badges/pages/issued-badge/issued-badge.module.ts @@ -15,9 +15,9 @@ 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 { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { AddonBadgesIssuedBadgePage } from './issued-badge'; @NgModule({ diff --git a/src/addon/badges/pages/issued-badge/issued-badge.ts b/src/addon/badges/pages/issued-badge/issued-badge.ts index 8beceaf63..7b8f6d5a1 100644 --- a/src/addon/badges/pages/issued-badge/issued-badge.ts +++ b/src/addon/badges/pages/issued-badge/issued-badge.ts @@ -16,11 +16,11 @@ 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'; +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. diff --git a/src/addon/badges/pages/user-badges/user-badges.module.ts b/src/addon/badges/pages/user-badges/user-badges.module.ts index e831ae877..2609cdf00 100644 --- a/src/addon/badges/pages/user-badges/user-badges.module.ts +++ b/src/addon/badges/pages/user-badges/user-badges.module.ts @@ -15,9 +15,9 @@ 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 { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { AddonBadgesUserBadgesPage } from './user-badges'; @NgModule({ diff --git a/src/addon/badges/pages/user-badges/user-badges.ts b/src/addon/badges/pages/user-badges/user-badges.ts index 0855e5346..35029793c 100644 --- a/src/addon/badges/pages/user-badges/user-badges.ts +++ b/src/addon/badges/pages/user-badges/user-badges.ts @@ -16,10 +16,10 @@ 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'; +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. diff --git a/src/addon/badges/providers/badge-link-handler.ts b/src/addon/badges/providers/badge-link-handler.ts index 0ea9fcf1f..bcaf759e0 100644 --- a/src/addon/badges/providers/badge-link-handler.ts +++ b/src/addon/badges/providers/badge-link-handler.ts @@ -13,9 +13,9 @@ // 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 { 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'; /** diff --git a/src/addon/badges/providers/badges.ts b/src/addon/badges/providers/badges.ts index b27802e9d..2245ad1fb 100644 --- a/src/addon/badges/providers/badges.ts +++ b/src/addon/badges/providers/badges.ts @@ -13,8 +13,8 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '../../../providers/logger'; -import { CoreSitesProvider } from '../../../providers/sites'; +import { CoreLoggerProvider } from '@providers/logger'; +import { CoreSitesProvider } from '@providers/sites'; /** * Service to handle badges. diff --git a/src/addon/badges/providers/mybadges-link-handler.ts b/src/addon/badges/providers/mybadges-link-handler.ts index 912630288..59a3025da 100644 --- a/src/addon/badges/providers/mybadges-link-handler.ts +++ b/src/addon/badges/providers/mybadges-link-handler.ts @@ -13,9 +13,9 @@ // 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 { 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'; /** diff --git a/src/addon/badges/providers/user-handler.ts b/src/addon/badges/providers/user-handler.ts index a6257089c..8e8ea9340 100644 --- a/src/addon/badges/providers/user-handler.ts +++ b/src/addon/badges/providers/user-handler.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; import { AddonBadgesProvider } from './badges'; /** From 26e8ce83c46ea0004bb43d9849c4007f8920dddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 26 Feb 2018 10:38:18 +0100 Subject: [PATCH 6/6] MOBILE-2325 badges: Badges styles --- .../pages/issued-badge/issued-badge.html | 106 +++++++++--------- .../badges/pages/user-badges/user-badges.html | 11 +- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/addon/badges/pages/issued-badge/issued-badge.html b/src/addon/badges/pages/issued-badge/issued-badge.html index 656d54114..e28898229 100644 --- a/src/addon/badges/pages/issued-badge/issued-badge.html +++ b/src/addon/badges/pages/issued-badge/issued-badge.html @@ -9,79 +9,79 @@ -
-
- {{badge.name}} - - {{ 'addon.badges.expired' | translate }} - -
-
+ + + + + {{ 'addon.badges.expired' | translate }} + + + -
-
+ +

{{ 'addon.badges.recipientdetails' | translate}}

-
-
-

{{ 'core.name' | translate}}

+ + +

{{ 'core.name' | translate}}

- +

-
-
+ + -
-
+ +

{{ 'addon.badges.issuerdetails' | translate}}

-
-
-

{{ 'addon.badges.issuername' | translate}}

+ + +

{{ 'addon.badges.issuername' | translate}}

- +

-
-
-

{{ 'addon.badges.contact' | translate}}

+ + +

{{ 'addon.badges.contact' | translate}}

- +

-
-
+ + -
-
+ +

{{ 'addon.badges.badgedetails' | translate}}

-
-
-

{{ 'core.name' | translate}}

+ + +

{{ 'core.name' | translate}}

{{badge.name}}

-
-
-

{{ 'core.description' | translate}}

+ + +

{{ 'core.description' | translate}}

- +

-
-
-

{{ 'core.course' | translate}}

+ + +

{{ 'core.course' | translate}}

- +

-
-
+ + -
-
+ +

{{ 'addon.badges.issuancedetails' | translate}}

-
-
-

{{ 'addon.badges.dateawarded' | translate}}

+ + +

{{ 'addon.badges.dateawarded' | translate}}

{{badge.dateissued | coreToLocaleString }}

-
-
-

{{ 'addon.badges.expirydate' | translate}}

+ + +

{{ 'addon.badges.expirydate' | translate}}

{{badge.dateexpire | coreToLocaleString }}

-
-
+ +
diff --git a/src/addon/badges/pages/user-badges/user-badges.html b/src/addon/badges/pages/user-badges/user-badges.html index 58c3b5d82..9768f7901 100644 --- a/src/addon/badges/pages/user-badges/user-badges.html +++ b/src/addon/badges/pages/user-badges/user-badges.html @@ -14,15 +14,16 @@ - {{badge.name}} - - {{ 'addon.badges.expired' | translate }} - + + +

{{ badge.dateissued | coreToLocaleString }}

+ + {{ 'addon.badges.expired' | translate }} +
- \ No newline at end of file