commit
d55ec1684f
|
@ -28,6 +28,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ios .core-avoid-header ion-content {
|
||||
top: $navbar-ios-height;
|
||||
height: calc(100% - #{($navbar-ios-height)});
|
||||
}
|
||||
|
||||
// Highlights inside the input element.
|
||||
@if ($core-text-input-ios-show-highlight) {
|
||||
.card-ios, .list-ios {
|
||||
|
|
|
@ -28,6 +28,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.md .core-avoid-header ion-content {
|
||||
top: $navbar-md-height;
|
||||
height: calc(100% - #{($navbar-md-height)});
|
||||
}
|
||||
|
||||
// Highlights inside the input element.
|
||||
@if ($core-text-input-md-show-highlight) {
|
||||
.card-md, .list-md {
|
||||
|
|
|
@ -61,6 +61,7 @@ import { CoreCourseModule } from '../core/course/course.module';
|
|||
import { CoreSiteHomeModule } from '../core/sitehome/sitehome.module';
|
||||
import { CoreContentLinksModule } from '../core/contentlinks/contentlinks.module';
|
||||
import { CoreUserModule } from '../core/user/user.module';
|
||||
import { CoreGradesModule } from '../core/grades/grades.module';
|
||||
|
||||
// Addon modules.
|
||||
import { AddonCalendarModule } from '../addon/calendar/calendar.module';
|
||||
|
@ -102,6 +103,7 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
|||
CoreSiteHomeModule,
|
||||
CoreContentLinksModule,
|
||||
CoreUserModule,
|
||||
CoreGradesModule,
|
||||
AddonCalendarModule,
|
||||
AddonUserProfileFieldModule,
|
||||
AddonFilesModule,
|
||||
|
|
|
@ -30,11 +30,16 @@
|
|||
clear: both;
|
||||
}
|
||||
}
|
||||
.img-responsive {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.opacity-hide { opacity: 0; }
|
||||
.core-big { font-size: 115%; }
|
||||
|
||||
@media only screen and (min-width: 430px) {
|
||||
@include media-breakpoint-up(sm) {
|
||||
.core-center-view .scroll-content {
|
||||
display: flex!important;
|
||||
align-content: center !important;
|
||||
|
@ -46,15 +51,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
@include media-breakpoint-down(md) {
|
||||
.hidden-phone {
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) {
|
||||
@include media-breakpoint-up(md) {
|
||||
.hidden-tablet {
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,3 +27,8 @@
|
|||
@extend .card-content-wp;
|
||||
}
|
||||
}
|
||||
|
||||
.wp .core-avoid-header ion-content {
|
||||
top: $navbar-wp-height;
|
||||
height: calc(100% - #{($navbar-wp-height)});
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 341 B |
Binary file not shown.
After Width: | Height: | Size: 318 B |
Binary file not shown.
After Width: | Height: | Size: 349 B |
|
@ -42,7 +42,7 @@ core-empty-box {
|
|||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 420px) {
|
||||
@include media-breakpoint-down(sm) {
|
||||
.core-empty-box {
|
||||
position: relative;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ion-split-pane (ionChange)="onSplitPaneChanged($event._visible);" [when]="when">
|
||||
<ion-menu [content]="detailNav" type="push">
|
||||
<ion-menu [content]="detailNav" type="push" class="core-avoid-header">
|
||||
<ng-content></ng-content>
|
||||
</ion-menu>
|
||||
<ion-nav [root]="detailPage" #detailNav main></ion-nav>
|
||||
<ion-nav [root]="detailPage" #detailNav main class="core-avoid-header"></ion-nav>
|
||||
</ion-split-pane>
|
|
@ -36,16 +36,7 @@ core-split-view {
|
|||
}
|
||||
}
|
||||
}
|
||||
ion-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ios ion-header + core-split-view ion-menu.split-pane-side ion-content{
|
||||
top: $navbar-ios-height;
|
||||
}
|
||||
|
||||
.md ion-header + core-split-view ion-menu.split-pane-side ion-content{
|
||||
top: $navbar-md-height;
|
||||
}
|
||||
|
||||
.wp ion-header + core-split-view ion-menu.split-pane-side ion-content{
|
||||
top: $navbar-wp-height;
|
||||
}
|
|
@ -143,7 +143,7 @@ export class CoreSplitViewComponent implements OnInit {
|
|||
activateSplitView(): void {
|
||||
const currentView = this.masterNav.getActive(),
|
||||
currentPageName = currentView.component.name;
|
||||
if (this.masterNav.getPrevious().component.name == this.masterPageName) {
|
||||
if (this.masterNav.getPrevious() && this.masterNav.getPrevious().component.name == this.masterPageName) {
|
||||
if (currentPageName != this.masterPageName) {
|
||||
// CurrentView is a 'Detail' page remove it from the 'master' nav stack.
|
||||
this.masterNav.pop();
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
core-tabs {
|
||||
.core-tabs-bar {
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
> a {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
core-tabs {
|
||||
.core-tabs-bar {
|
||||
@include position(null, null, 0, 0);
|
||||
left: 0;
|
||||
position: relative;
|
||||
z-index: $z-index-toolbar;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
@ -11,15 +12,17 @@ core-tabs {
|
|||
|
||||
background: $core-top-tabs-background;
|
||||
color: $core-top-tabs-color !important;
|
||||
border-bottom: 1px solid $core-top-tabs-border;
|
||||
font-size: 1.6rem;
|
||||
border: 0;
|
||||
|
||||
&[aria-selected=true] {
|
||||
color: $core-top-tabs-color-active !important;
|
||||
border-bottom: 2px solid $core-top-tabs-color-active;
|
||||
border: 0 !important;
|
||||
border-bottom: 2px solid $core-top-tabs-color-active !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.core-tabs-content-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -56,3 +59,21 @@ core-tabs {
|
|||
.scroll-content.no-scroll {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.ios core-tabs {
|
||||
.core-tabs-bar {
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
> a {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.md core-tabs {
|
||||
.core-tabs-bar::after {
|
||||
@extend .header-md::after;
|
||||
}
|
||||
}
|
|
@ -1,24 +1,3 @@
|
|||
page-core-course-section {
|
||||
.core-tabs-bar {
|
||||
@include position(null, null, 0, 0);
|
||||
|
||||
z-index: $z-index-toolbar;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background: $core-top-tabs-background;
|
||||
|
||||
> a {
|
||||
@extend .tab-button;
|
||||
|
||||
background: $core-top-tabs-background;
|
||||
color: $core-top-tabs-color !important;
|
||||
border-bottom: 1px solid $core-top-tabs-border;
|
||||
font-size: 1.6rem;
|
||||
|
||||
&[aria-selected=true] {
|
||||
color: $core-top-tabs-color-active !important;
|
||||
border-bottom: 2px solid $core-top-tabs-color-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
core-courses-course-progress {
|
||||
&.core-courseoverview {
|
||||
@media (max-width: 576px) {
|
||||
@include media-breakpoint-down(sm) {
|
||||
ion-card.card {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// (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 { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreGradesCourseComponent } from './course/course';
|
||||
import { CoreComponentsModule } from '../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../directives/directives.module';
|
||||
import { CorePipesModule } from '../../../pipes/pipes.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreGradesCourseComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
CoreGradesCourseComponent
|
||||
],
|
||||
entryComponents: [
|
||||
CoreGradesCourseComponent
|
||||
]
|
||||
})
|
||||
export class CoreGradesComponentsModule {}
|
|
@ -0,0 +1,36 @@
|
|||
<ion-content>
|
||||
<ion-refresher [enabled]="gradesLoaded" (ionRefresh)="refreshGrades($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="gradesLoaded">
|
||||
<core-empty-box *ngIf="!gradesTable" icon="stats" [message]="'core.grades.nogradesreturned' | translate">
|
||||
</core-empty-box>
|
||||
|
||||
<div *ngIf="gradesTable" class="core-grades-container">
|
||||
<table cellspacing="0" cellpadding="0" class="core-grades-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th *ngFor="let column of gradesTable.columns" id="{{column.name}}" [class.hidden-phone]="column.hiddenPhone" [attr.colspan]="column.colspan">
|
||||
{{ 'core.grades.' + column.name | translate }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let row of gradesTable.rows" (click)="row.itemtype != 'category' && gotoGrade(row.id)" [class]="row.rowclass">
|
||||
<td *ngIf="row.itemtype == 'category'" class="core-grades-table-category" [attr.rowspan]="row.rowspan">
|
||||
</td>
|
||||
<th class="core-grades-table-gradeitem" [attr.colspan]="row.colspan" [class.core-split-item-selected]="gradeId == row.id">
|
||||
<ion-icon *ngIf="row.icon" name="{{row.icon}}" item-start></ion-icon>
|
||||
<img *ngIf="row.image" [src]="row.image" item-start/>
|
||||
<span [innerHTML]="row.gradeitem"></span>
|
||||
</th>
|
||||
<ng-container *ngFor="let column of gradesTable.columns">
|
||||
<td *ngIf="column.name != 'gradeitem' && row[column.name] != undefined" [class]="'core-grades-table-' + column.name" [innerHTML]="row[column.name]" [class.hidden-phone]="column.hiddenPhone">
|
||||
</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,79 @@
|
|||
core-grades-course {
|
||||
.core-grades-table {
|
||||
border-collapse: collapse;
|
||||
line-height: 20px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
color: $text-color;
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $list-border-color;
|
||||
}
|
||||
th, td {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-right: 10px;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
thead th {
|
||||
vertical-align: bottom;
|
||||
font-weight: bold;
|
||||
background-color: $white;
|
||||
}
|
||||
tbody th {
|
||||
font-weight: normal;
|
||||
}
|
||||
#gradeitem {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.core-grades-table-gradeitem {
|
||||
padding-left: 5px;
|
||||
font-weight: bold;
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
ion-icon {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
.core-grades-table-feedback {
|
||||
padding-left: 5px;
|
||||
.no-overflow {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dimmed_text,
|
||||
.hidden {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.odd {
|
||||
td, th, th.core-split-item-selected {
|
||||
background-color: $gray-lighter;
|
||||
}
|
||||
}
|
||||
.even {
|
||||
td, th, th.core-split-item-selected {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
td {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.split-pane-side, .split-pane-main {
|
||||
core-grades-course .core-grades-table .hidden-phone {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
// (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, Input, Optional } from '@angular/core';
|
||||
import { Content, NavParams, NavController } from 'ionic-angular';
|
||||
import { CoreGradesProvider } from '../../providers/grades';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreGradesHelperProvider } from '../../providers/helper';
|
||||
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||
import { CoreAppProvider } from '../../../../providers/app';
|
||||
|
||||
/**
|
||||
* Component that displays a course grades.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-grades-course',
|
||||
templateUrl: 'course.html',
|
||||
})
|
||||
export class CoreGradesCourseComponent {
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
@Input() courseId: number;
|
||||
@Input() userId: number;
|
||||
@Input() gradeId?: number;
|
||||
|
||||
gradesLoaded = false;
|
||||
gradesTable: any;
|
||||
|
||||
constructor(private gradesProvider: CoreGradesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams,
|
||||
private gradesHelper: CoreGradesHelperProvider, private sitesProvider: CoreSitesProvider, private navCtrl: NavController,
|
||||
private appProvider: CoreAppProvider, @Optional() private svComponent: CoreSplitViewComponent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.fetchData().then(() => {
|
||||
if (this.gradeId) {
|
||||
// There is the grade to load.
|
||||
this.gotoGrade(this.gradeId);
|
||||
}
|
||||
|
||||
// Add log in Moodle.
|
||||
return this.gradesProvider.logCourseGradesView(this.courseId, this.userId);
|
||||
}).finally(() => {
|
||||
this.gradesLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all the data required for the view.
|
||||
*
|
||||
* @param {boolean} [refresh] Empty events array first.
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
fetchData(refresh: boolean = false): Promise<any> {
|
||||
return this.gradesProvider.getCourseGradesTable(this.courseId, this.userId).then((table) => {
|
||||
this.gradesTable = this.gradesHelper.formatGradesTable(table);
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'Error loading grades');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshGrades(refresher: any): void {
|
||||
this.gradesProvider.invalidateCourseGradesData(this.courseId, this.userId).finally(() => {
|
||||
this.fetchData().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the grade of the selected item.
|
||||
* @param {number} gradeId Grade item ID where to navigate.
|
||||
*/
|
||||
gotoGrade(gradeId: number): void {
|
||||
if (gradeId) {
|
||||
this.gradeId = gradeId;
|
||||
let whereToPush, pageName;
|
||||
|
||||
if (this.svComponent) {
|
||||
if (this.svComponent.getMasterNav().getActive().component.name == 'CoreGradesCourseSplitPage') {
|
||||
// Table is on left side. Push on right.
|
||||
whereToPush = this.svComponent;
|
||||
pageName = 'CoreGradesGradePage';
|
||||
} else {
|
||||
// Table is on right side. Load new split view.
|
||||
whereToPush = this.svComponent.getMasterNav();
|
||||
pageName = 'CoreGradesCourseSplitPage';
|
||||
}
|
||||
} else {
|
||||
if (this.appProvider.isWide()) {
|
||||
// Table is full screen and large. Load here.
|
||||
whereToPush = this.navCtrl;
|
||||
pageName = 'CoreGradesCourseSplitPage';
|
||||
} else {
|
||||
// Table is full screen but on mobile. Load here.
|
||||
whereToPush = this.navCtrl;
|
||||
pageName = 'CoreGradesGradePage';
|
||||
}
|
||||
|
||||
}
|
||||
whereToPush.push(pageName, {courseId: this.courseId, userId: this.userId, gradeId: gradeId});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// (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 { CoreGradesProvider } from './providers/grades';
|
||||
import { CoreGradesHelperProvider } from './providers/helper';
|
||||
import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate';
|
||||
import { CoreGradesMainMenuHandler } from './providers/mainmenu-handler';
|
||||
import { CoreGradesCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { CoreGradesComponentsModule } from './components/components.module';
|
||||
import { CoreCourseOptionsDelegate } from '../course/providers/options-delegate';
|
||||
import { CoreGradesUserLinkHandler } from './providers/user-link-handler';
|
||||
import { CoreGradesOverviewLinkHandler } from './providers/overview-link-handler';
|
||||
import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate';
|
||||
import { CoreGradesUserHandler } from './providers/user-handler';
|
||||
import { CoreUserDelegate } from '../user/providers/user-delegate';
|
||||
import { CoreEventsProvider } from '../../providers/events';
|
||||
import { CoreSitesProvider } from '../../providers/sites';
|
||||
import { CoreUserProvider } from '../user/providers/user';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
CoreGradesComponentsModule
|
||||
],
|
||||
providers: [
|
||||
CoreGradesProvider,
|
||||
CoreGradesHelperProvider,
|
||||
CoreGradesMainMenuHandler,
|
||||
CoreGradesCourseOptionHandler,
|
||||
CoreGradesUserLinkHandler,
|
||||
CoreGradesOverviewLinkHandler,
|
||||
CoreGradesUserHandler
|
||||
]
|
||||
})
|
||||
export class CoreGradesModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, gradesMenuHandler: CoreGradesMainMenuHandler,
|
||||
courseOptionHandler: CoreGradesCourseOptionHandler, courseOptionsDelegate: CoreCourseOptionsDelegate,
|
||||
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreGradesUserLinkHandler,
|
||||
overviewLinkHandler: CoreGradesOverviewLinkHandler, userHandler: CoreGradesUserHandler,
|
||||
userDelegate: CoreUserDelegate, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
|
||||
|
||||
// Register handlers.
|
||||
mainMenuDelegate.registerHandler(gradesMenuHandler);
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
contentLinksDelegate.registerHandler(userLinkHandler);
|
||||
contentLinksDelegate.registerHandler(overviewLinkHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
|
||||
// Clear user profile handler cache.
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, (data) => {
|
||||
userHandler.clearViewGradesCache(data.courseId, data.userId);
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
|
||||
userHandler.clearViewGradesCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"average": "Average",
|
||||
"contributiontocoursetotal": "Contribution to course total",
|
||||
"feedback": "Feedback",
|
||||
"grade": "Grade",
|
||||
"gradeitem": "Grade item",
|
||||
"grades": "Grades",
|
||||
"lettergrade": "Letter grade",
|
||||
"nogradesreturned": "No grades returned",
|
||||
"percentage": "Percentage",
|
||||
"range": "Range",
|
||||
"rank": "Rank",
|
||||
"weight": "Weight"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.grades.grades' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<core-grades-course class="core-avoid-header" [courseId]="courseId" [userId]="userId"></core-grades-course>
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { CoreGradesCoursePage } from './course';
|
||||
import { CoreGradesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreGradesCoursePage
|
||||
],
|
||||
imports: [
|
||||
CoreGradesComponentsModule,
|
||||
IonicPageModule.forChild(CoreGradesCoursePage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreGradesCoursePageModule {}
|
|
@ -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 { Component } from '@angular/core';
|
||||
import { IonicPage, NavParams } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
|
||||
/**
|
||||
* Page that displays a course grades.
|
||||
*/
|
||||
@IonicPage({ segment: 'core-grades-course' })
|
||||
@Component({
|
||||
selector: 'page-core-grades-course',
|
||||
templateUrl: 'course.html',
|
||||
})
|
||||
export class CoreGradesCoursePage {
|
||||
courseId: number;
|
||||
userId: number;
|
||||
|
||||
constructor(navParams: NavParams, sitesProvider: CoreSitesProvider) {
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.userId = navParams.get('userId') || sitesProvider.getCurrentSiteUserId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.grades.grades' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<core-split-view>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="gradesLoaded" (ionRefresh)="refreshGrades($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="gradesLoaded">
|
||||
<core-empty-box *ngIf="grades && grades.length == 0" icon="stats" [message]="'core.grades.nogradesreturned' | translate">
|
||||
</core-empty-box>
|
||||
|
||||
<ion-list *ngIf="grades && grades.length > 0">
|
||||
<a ion-item text-wrap *ngFor="let grade of grades" [title]="grade.courseFullName" (click)="gotoCourseGrades(grade.courseid)" [class.core-split-item-selected]="grade.courseid == courseId">
|
||||
<h2><core-format-text [text]="grade.courseFullName"></core-format-text></h2>
|
||||
<ion-badge item-end color="light">{{grade.grade}}</ion-badge>
|
||||
</a>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</core-split-view>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreGradesCoursesPage } from './courses';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreGradesCoursesPage
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
IonicPageModule.forChild(CoreGradesCoursesPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreGradesCoursesPageModule {}
|
|
@ -0,0 +1,100 @@
|
|||
// (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 } from 'ionic-angular';
|
||||
import { CoreGradesProvider } from '../../providers/grades';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||
import { CoreGradesHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
* Page that displays courses grades (main menu option).
|
||||
*/
|
||||
@IonicPage({ segment: 'core-grades-courses' })
|
||||
@Component({
|
||||
selector: 'page-core-grades-courses',
|
||||
templateUrl: 'courses.html',
|
||||
})
|
||||
export class CoreGradesCoursesPage {
|
||||
@ViewChild(Content) content: Content;
|
||||
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||
|
||||
grades = [];
|
||||
courseId: number;
|
||||
userId: number;
|
||||
gradesLoaded = false;
|
||||
|
||||
constructor(private gradesProvider: CoreGradesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private gradesHelper: CoreGradesHelperProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
if (this.courseId) {
|
||||
// There is the course to load, open the course in a new state.
|
||||
this.gotoCourseGrades(this.courseId);
|
||||
}
|
||||
|
||||
this.fetchData().then(() => {
|
||||
if (!this.courseId && this.splitviewCtrl.isOn() && this.grades.length > 0) {
|
||||
this.gotoCourseGrades(this.grades[0].courseid);
|
||||
}
|
||||
|
||||
// Add log in Moodle.
|
||||
return this.gradesProvider.logCoursesGradesView();
|
||||
}).finally(() => {
|
||||
this.gradesLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all the data required for the view.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
fetchData(): Promise<any> {
|
||||
return this.gradesProvider.getCoursesGrades().then((grades) => {
|
||||
return this.gradesHelper.getGradesCourseData(grades).then((grades) => {
|
||||
this.grades = grades;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'Error loading grades');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshGrades(refresher: any): void {
|
||||
this.gradesProvider.invalidateCoursesGradesData().finally(() => {
|
||||
this.fetchData().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the grades of the selected course.
|
||||
* @param {number} courseId Course Id where to navigate.
|
||||
*/
|
||||
gotoCourseGrades(courseId: number): void {
|
||||
this.courseId = courseId;
|
||||
this.splitviewCtrl.push('CoreGradesCoursePage', {courseId: courseId, userId: this.userId});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.grades.grades' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<core-split-view>
|
||||
<core-grades-course class="core-avoid-header" [courseId]="courseId" [userId]="userId" [gradeId]="gradeId"></core-grades-course>
|
||||
</core-split-view>
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreGradesCourseSplitPage } from './coursesplit';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreGradesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreGradesCourseSplitPage
|
||||
],
|
||||
imports: [
|
||||
CoreGradesComponentsModule,
|
||||
CoreComponentsModule,
|
||||
IonicPageModule.forChild(CoreGradesCourseSplitPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreGradesCourseSplitPageModule {}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 } from '@angular/core';
|
||||
import { IonicPage, NavParams } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
|
||||
/**
|
||||
* Page that displays a course grades.
|
||||
*/
|
||||
@IonicPage({ segment: 'core-grades-course-split' })
|
||||
@Component({
|
||||
selector: 'page-core-grades-course-split',
|
||||
templateUrl: 'coursesplit.html',
|
||||
})
|
||||
export class CoreGradesCourseSplitPage {
|
||||
|
||||
courseId: number;
|
||||
userId: number;
|
||||
gradeId: number;
|
||||
|
||||
constructor(navParams: NavParams, sitesProvider: CoreSitesProvider) {
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.userId = navParams.get('userId') || sitesProvider.getCurrentSiteUserId();
|
||||
this.gradeId = navParams.get('gradeId');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.grades.grade' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="gradeLoaded" (ionRefresh)="refreshGrade($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="gradeLoaded">
|
||||
<core-empty-box *ngIf="!grade" icon="stats" [message]="'core.grades.nogradesreturned' | translate"></core-empty-box>
|
||||
|
||||
<ion-list *ngIf="grade">
|
||||
<a ion-item *ngIf="grade.itemname && grade.link" text-wrap detail-push [href]="grade.link" core-link captureLink="true">
|
||||
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" item-start></ion-icon>
|
||||
<img *ngIf="grade.image" [src]="grade.image" item-start/>
|
||||
<h2><core-format-text [text]="grade.itemname"></core-format-text></h2>
|
||||
</a>
|
||||
|
||||
<ion-item *ngIf="grade.itemname && !grade.link" text-wrap >
|
||||
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" item-start></ion-icon>
|
||||
<img *ngIf="grade.image" [src]="grade.image" item-start/>
|
||||
<h2><core-format-text [text]="grade.itemname"></core-format-text></h2>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.weight">
|
||||
<h2>{{ 'core.grades.weight' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.weight"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.grade">
|
||||
<h2>{{ 'core.grades.grade' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.grade"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.range">
|
||||
<h2>{{ 'core.grades.range' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.range"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.percentage">
|
||||
<h2>{{ 'core.grades.percentage' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.percentage"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.lettergrade">
|
||||
<h2>{{ 'core.grades.lettergrade' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.lettergrade"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.rank">
|
||||
<h2>{{ 'core.grades.rank' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.rank"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.average">
|
||||
<h2>{{ 'core.grades.average' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.average"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.feedback">
|
||||
<h2>{{ 'core.grades.feedback' | translate}}</h2>
|
||||
<p><core-format-text [fullTitle]="'core.grades.feedback' | translate" maxHeight="60" fullOnClick="true" [text]="grade.feedback"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item text-wrap *ngIf="grade.contributiontocoursetotal">
|
||||
<h2>{{ 'core.grades.contributiontocoursetotal' | translate}}</h2>
|
||||
<p><core-format-text [text]="grade.contributiontocoursetotal"></core-format-text></p>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreGradesGradePage } from './grade';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreGradesGradePage
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
IonicPageModule.forChild(CoreGradesGradePage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreGradesGradePageModule {}
|
|
@ -0,0 +1,81 @@
|
|||
// (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 } from 'ionic-angular';
|
||||
import { CoreGradesProvider } from '../../providers/grades';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreGradesHelperProvider } from '../../providers/helper';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
|
||||
/**
|
||||
* Page that displays activity grade.
|
||||
*/
|
||||
@IonicPage({ segment: 'core-grades-grade' })
|
||||
@Component({
|
||||
selector: 'page-core-grades-grade',
|
||||
templateUrl: 'grade.html',
|
||||
})
|
||||
export class CoreGradesGradePage {
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
grade: any;
|
||||
courseId: number;
|
||||
userId: number;
|
||||
gradeId: number;
|
||||
gradeLoaded = false;
|
||||
|
||||
constructor(private gradesProvider: CoreGradesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private gradesHelper: CoreGradesHelperProvider, navParams: NavParams, sitesProvider: CoreSitesProvider) {
|
||||
|
||||
this.courseId = navParams.get('courseId');
|
||||
this.userId = navParams.get('userId') || sitesProvider.getCurrentSiteUserId();
|
||||
this.gradeId = navParams.get('gradeId');
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.fetchData().finally(() => {
|
||||
this.gradeLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all the data required for the view.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
fetchData(): Promise<any> {
|
||||
return this.gradesHelper.getGradeItem(this.courseId, this.gradeId, this.userId).then((grade) => {
|
||||
this.grade = grade;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'Error loading grade item');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshGrade(refresher: any): void {
|
||||
this.gradesProvider.invalidateCourseGradesData(this.courseId, this.userId).finally(() => {
|
||||
this.fetchData().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// (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 { NavController } from 'ionic-angular';
|
||||
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate';
|
||||
import { CoreCourseProvider } from '../../course/providers/course';
|
||||
import { CoreGradesProvider } from './grades';
|
||||
import { CoreCoursesProvider } from '../../courses/providers/courses';
|
||||
import { CoreGradesCourseComponent } from '../components/course/course';
|
||||
|
||||
/**
|
||||
* Course nav handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'CoreGrades';
|
||||
priority = 400;
|
||||
|
||||
constructor(private gradesProvider: CoreGradesProvider, private coursesProvider: CoreCoursesProvider) {}
|
||||
|
||||
/**
|
||||
* Should invalidate the data to determine if the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The 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 {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise<any> {
|
||||
if (navOptions && typeof navOptions.grades != 'undefined') {
|
||||
// No need to invalidate anything.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.coursesProvider.invalidateUserCourses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} accessData Access type and data. Default, guest, ...
|
||||
* @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>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
}
|
||||
|
||||
if (navOptions && typeof navOptions.grades != 'undefined') {
|
||||
return navOptions.grades;
|
||||
}
|
||||
|
||||
return this.gradesProvider.isPluginEnabledForCourse(courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreMainMenuHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(): CoreCourseOptionsHandlerData {
|
||||
return {
|
||||
title: 'core.grades.grades',
|
||||
class: 'core-grades-course-handler',
|
||||
component: CoreGradesCourseComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
// (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 { CoreSite } from '../../../classes/site';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreCoursesProvider } from '../../courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Service to provide grade functionalities.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesProvider {
|
||||
protected ROOT_CACHE_KEY = 'mmGrades:';
|
||||
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider,
|
||||
private coursesProvider: CoreCoursesProvider) {
|
||||
this.logger = logger.getInstance('CoreGradesProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for grade table data WS calls.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} userId ID of the user to get the grades from.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCourseGradesCacheKey(courseId: number, userId: number): string {
|
||||
return this.getCourseGradesPrefixCacheKey(courseId) + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for grade items data WS calls.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} userId ID of the user to get the grades from.
|
||||
* @param {number} [groupId] ID of the group to get the grades from. Default: 0.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCourseGradesItemsCacheKey(courseId: number, userId: number, groupId: number): string {
|
||||
groupId = groupId || 0;
|
||||
|
||||
return this.getCourseGradesPrefixCacheKey(courseId) + userId + ':' + groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prefix cache key for grade table data WS calls.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCourseGradesPrefixCacheKey(courseId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'items:' + courseId + ':';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for courses grade WS calls.
|
||||
*
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCoursesGradesCacheKey(): string {
|
||||
return this.ROOT_CACHE_KEY + 'coursesgrades';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grade items for a certain module. Keep in mind that may have more than one item to include outcomes and scales.
|
||||
* Fallback function only used if 'gradereport_user_get_grade_items' WS is not avalaible Moodle < 3.2.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} [userId] ID of the user to get the grades from. If not defined use site's current user.
|
||||
* @param {number} [groupId] ID of the group to get the grades from. Not used for old gradebook table.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @return {Promise<any>} Promise to be resolved when the grades are retrieved.
|
||||
*/
|
||||
getGradeItems(courseId: number, userId?: number, groupId?: number, siteId?: string, ignoreCache: boolean = false):
|
||||
Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
return this.isGradeItemsAvalaible(siteId).then((enabled) => {
|
||||
if (enabled) {
|
||||
return this.getCourseGradesItems(courseId, userId, groupId, siteId, ignoreCache).catch(() => {
|
||||
// FallBack while solving MDL-57255.
|
||||
return this.getCourseGradesTable(courseId, userId, siteId, ignoreCache);
|
||||
});
|
||||
} else {
|
||||
return this.getCourseGradesTable(courseId, userId, siteId, ignoreCache);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grade items for a certain course.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} [userId] ID of the user to get the grades from.
|
||||
* @param {number} [groupId] ID of the group to get the grades from. Default 0.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @return {Promise<any>} Promise to be resolved when the grades table is retrieved.
|
||||
*/
|
||||
getCourseGradesItems(courseId: number, userId?: number, groupId?: number, siteId?: string,
|
||||
ignoreCache: boolean = false): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
groupId = groupId || 0;
|
||||
|
||||
this.logger.debug(`Get grades for course '${courseId}' and user '${userId}'`);
|
||||
|
||||
const data = {
|
||||
courseid : courseId,
|
||||
userid : userId,
|
||||
groupid : groupId
|
||||
},
|
||||
preSets = {
|
||||
cacheKey: this.getCourseGradesItemsCacheKey(courseId, userId, groupId)
|
||||
};
|
||||
|
||||
if (ignoreCache) {
|
||||
preSets['getFromCache'] = 0;
|
||||
preSets['emergencyCache'] = 0;
|
||||
}
|
||||
|
||||
return site.read('gradereport_user_get_grade_items', data, preSets).then((grades) => {
|
||||
if (grades && grades.usergrades && grades.usergrades[0]) {
|
||||
return grades.usergrades[0].gradeitems;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grades for a certain course.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} [userId] ID of the user to get the grades from.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @return {Promise<any>} Promise to be resolved when the grades table is retrieved.
|
||||
*/
|
||||
getCourseGradesTable(courseId: number, userId?: number, siteId?: string, ignoreCache: boolean = false): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
this.logger.debug(`Get grades for course '${courseId}' and user '${userId}'`);
|
||||
|
||||
const data = {
|
||||
courseid : courseId,
|
||||
userid : userId
|
||||
},
|
||||
preSets = {
|
||||
cacheKey: this.getCourseGradesCacheKey(courseId, userId)
|
||||
};
|
||||
|
||||
if (ignoreCache) {
|
||||
preSets['getFromCache'] = 0;
|
||||
preSets['emergencyCache'] = 0;
|
||||
}
|
||||
|
||||
return site.read('gradereport_user_get_grades_table', data, preSets).then((table) => {
|
||||
if (table && table.tables && table.tables[0]) {
|
||||
return table.tables[0];
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grades for a certain course.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise to be resolved when the grades are retrieved.
|
||||
*/
|
||||
getCoursesGrades(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
this.logger.debug('Get course grades');
|
||||
|
||||
const preSets = {
|
||||
cacheKey: this.getCoursesGradesCacheKey()
|
||||
};
|
||||
|
||||
return site.read('gradereport_overview_get_course_grades', undefined, preSets).then((data) => {
|
||||
if (data && data.grades) {
|
||||
return data.grades;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates grade table data WS calls.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} [userId] User ID.
|
||||
* @param {string} [siteId] Site id (empty for current site).
|
||||
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateCourseGradesData(courseId: number, userId?: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
return site.invalidateWsCacheForKey(this.getCourseGradesCacheKey(courseId, userId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates courses grade data WS calls.
|
||||
*
|
||||
* @param {string} [siteId] Site id (empty for current site).
|
||||
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateCoursesGradesData(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getCoursesGradesCacheKey());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates courses grade items data WS calls.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} userId ID of the user to get the grades from.
|
||||
* @param {number} [groupId] ID of the group to get the grades from. Default: 0.
|
||||
* @param {string} [siteId] Site id (empty for current site).
|
||||
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateCourseGradesItemsData(courseId: number, userId: number, groupId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getCourseGradesItemsCacheKey(courseId, userId, groupId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the plugin is enabled for a certain site.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Resolve with true if plugin is enabled, false otherwise.
|
||||
* @since Moodle 3.2
|
||||
*/
|
||||
isCourseGradesEnabled(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!site.wsAvailable('gradereport_overview_get_course_grades')) {
|
||||
return false;
|
||||
}
|
||||
// Now check that the configurable mygradesurl is pointing to the gradereport_overview plugin.
|
||||
const url = site.getStoredConfig('mygradesurl') || '';
|
||||
|
||||
return url.indexOf('/grade/report/overview/') !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the grade addon is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promisee<boolean>} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isPluginEnabledForCourse(courseId: number, siteId?: string): Promise<boolean> {
|
||||
if (!courseId) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
return this.coursesProvider.getUserCourse(courseId, true, siteId).then((course) => {
|
||||
return !(course && typeof course.showgrades != 'undefined' && course.showgrades == 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not WS Grade Items is avalaible.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} True if ws is avalaible, false otherwise.
|
||||
* @since Moodle 3.2
|
||||
*/
|
||||
isGradeItemsAvalaible(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.wsAvailable('gradereport_user_get_grade_items');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log Course grades view in Moodle.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} userId User ID.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
logCourseGradesView(courseId: number, userId: number): Promise<any> {
|
||||
userId = userId || this.sitesProvider.getCurrentSiteUserId();
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('gradereport_user_view_grade_report', {
|
||||
courseid: courseId,
|
||||
userid: userId
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log Courses grades view in Moodle.
|
||||
*
|
||||
* @param {number} [courseId] Course ID. If not defined, site Home ID.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
logCoursesGradesView(courseId?: number): Promise<any> {
|
||||
if (!courseId) {
|
||||
courseId = this.sitesProvider.getCurrentSiteHomeId();
|
||||
}
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', {
|
||||
courseid: courseId
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,407 @@
|
|||
// (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';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreCoursesProvider } from '../../courses/providers/courses';
|
||||
import { CoreCourseProvider } from '../../course/providers/course';
|
||||
import { CoreGradesProvider } from './grades';
|
||||
import { CoreTextUtilsProvider } from '../../../providers/utils/text';
|
||||
import { CoreUrlUtilsProvider } from '../../../providers/utils/url';
|
||||
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding grades information.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesHelperProvider {
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private coursesProvider: CoreCoursesProvider,
|
||||
private gradesProvider: CoreGradesProvider, private sitesProvider: CoreSitesProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private courseProvider: CoreCourseProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private translate: TranslateService,
|
||||
private urlUtils: CoreUrlUtilsProvider) {
|
||||
this.logger = logger.getInstance('CoreGradesHelperProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a row from the grades table te be rendered in a page.
|
||||
*
|
||||
* @param {any} tableRow JSON object representing row of grades table data.
|
||||
* @return {any} Formatted row object.
|
||||
*/
|
||||
protected formatGradeRow(tableRow: any): any {
|
||||
const row = {};
|
||||
for (const name in tableRow) {
|
||||
if (typeof(tableRow[name].content) != 'undefined') {
|
||||
let content = tableRow[name].content;
|
||||
|
||||
if (name == 'itemname') {
|
||||
this.setRowIcon(row, content);
|
||||
row['link'] = this.getModuleLink(content);
|
||||
row['rowclass'] += tableRow[name].class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||
row['rowclass'] += tableRow[name].class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
||||
|
||||
content = content.replace(/<\/span>/gi, '\n');
|
||||
content = this.textUtils.cleanTags(content);
|
||||
} else {
|
||||
content = this.textUtils.replaceNewLines(content, '<br>');
|
||||
}
|
||||
|
||||
if (content == ' ') {
|
||||
content = '';
|
||||
}
|
||||
|
||||
row[name] = content.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a row from the grades table to be rendered in one table.
|
||||
*
|
||||
* @param {any} tableRow JSON object representing row of grades table data.
|
||||
* @return {any} Formatted row object.
|
||||
*/
|
||||
protected formatGradeRowForTable(tableRow: any): any {
|
||||
const row = {};
|
||||
for (let name in tableRow) {
|
||||
if (typeof(tableRow[name].content) != 'undefined') {
|
||||
let content = tableRow[name].content;
|
||||
|
||||
if (name == 'itemname') {
|
||||
this.setRowIcon(row, content);
|
||||
row['rowclass'] = tableRow[name].class.indexOf('leveleven') < 0 ? 'odd' : 'even';
|
||||
row['rowclass'] += tableRow[name].class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||
row['rowclass'] += tableRow[name].class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
||||
|
||||
content = content.replace(/<\/span>/gi, '\n');
|
||||
content = this.textUtils.cleanTags(content);
|
||||
|
||||
row['id'] = parseInt(tableRow[name].id.split('_')[1], 10);
|
||||
row['colspan'] = tableRow[name].colspan;
|
||||
row['rowspan'] = (tableRow['leader'] && tableRow['leader'].rowspan) || 1;
|
||||
name = 'gradeitem';
|
||||
} else {
|
||||
content = this.textUtils.replaceNewLines(content, '<br>');
|
||||
}
|
||||
|
||||
if (content == ' ') {
|
||||
content = '';
|
||||
}
|
||||
|
||||
row[name] = content.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes suffix formatted to compatibilize data from table and items.
|
||||
*
|
||||
* @param {any} item Grade item to format.
|
||||
* @return {any} Grade item formatted.
|
||||
*/
|
||||
protected formatGradeItem(item: any): any {
|
||||
for (const name in item) {
|
||||
const index = name.indexOf('formatted');
|
||||
if (index > 0) {
|
||||
item[name.substr(0, index)] = item[name];
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the response of gradereport_user_get_grades_table to be rendered.
|
||||
*
|
||||
* @param {any} table JSON object representing a table with data.
|
||||
* @return {any} Formatted HTML table.
|
||||
*/
|
||||
formatGradesTable(table: any): any {
|
||||
const maxDepth = table.maxdepth,
|
||||
formatted = {
|
||||
columns: [],
|
||||
rows: []
|
||||
},
|
||||
// Columns, in order.
|
||||
columns = {
|
||||
gradeitem: true,
|
||||
weight: false,
|
||||
grade: false,
|
||||
range: false,
|
||||
percentage: false,
|
||||
lettergrade: false,
|
||||
rank: false,
|
||||
average: false,
|
||||
feedback: false,
|
||||
contributiontocoursetotal: false
|
||||
};
|
||||
formatted.rows = table.tabledata.map((row: any) => {
|
||||
return this.formatGradeRowForTable(row);
|
||||
}).filter((row: any) => {
|
||||
return typeof row.gradeitem !== 'undefined';
|
||||
});
|
||||
|
||||
// Get a row with some info.
|
||||
let normalRow = formatted.rows.find((e) => {
|
||||
return e.itemtype != 'leader' && (typeof e.grade != 'undefined' || typeof e.percentage != 'undefined');
|
||||
});
|
||||
|
||||
// Decide if grades or percentage is being shown on phones.
|
||||
if (normalRow && typeof normalRow.grade != 'undefined') {
|
||||
columns.grade = true;
|
||||
} else if (normalRow && typeof normalRow.percentage != 'undefined') {
|
||||
columns.percentage = true;
|
||||
} else {
|
||||
normalRow = formatted.rows.find((e) => {
|
||||
return e.itemtype != 'leader';
|
||||
});
|
||||
columns.grade = true;
|
||||
}
|
||||
|
||||
for (const colName in columns) {
|
||||
if (typeof normalRow[colName] != 'undefined') {
|
||||
formatted.columns.push({
|
||||
name: colName,
|
||||
colspan: colName == 'gradeitem' ? maxDepth : 1,
|
||||
hiddenPhone: !columns[colName]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get course data for grades since they only have courseid.
|
||||
*
|
||||
* @param {any} grades Grades to get the data for.
|
||||
* @return {Promise<any>} Promise always resolved. Resolve param is the formatted grades.
|
||||
*/
|
||||
getGradesCourseData(grades: any): Promise<any> {
|
||||
// Using cache for performance reasons.
|
||||
return this.coursesProvider.getUserCourses(true).then((courses) => {
|
||||
const indexedCourses = {};
|
||||
courses.forEach((course) => {
|
||||
indexedCourses[course.id] = course;
|
||||
});
|
||||
|
||||
grades.forEach((grade) => {
|
||||
if (typeof indexedCourses[grade.courseid] != 'undefined') {
|
||||
grade.courseFullName = indexedCourses[grade.courseid].fullname;
|
||||
}
|
||||
});
|
||||
|
||||
return grades;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an specific grade item.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} gradeId Grade ID.
|
||||
* @param {number} [userId] ID of the user to get the grades from. If not defined use site's current user.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @return {Promise<any>} Promise to be resolved when the grades are retrieved.
|
||||
*/
|
||||
getGradeItem(courseId: number, gradeId: number, userId?: number, siteId?: string, ignoreCache: boolean = false): Promise<any> {
|
||||
|
||||
return this.gradesProvider.getCourseGradesTable(courseId, userId, siteId, ignoreCache).then((grades) => {
|
||||
if (grades) {
|
||||
return this.getGradesTableRow(grades, gradeId);
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the grade items for a certain module. Keep in mind that may have more than one item to include outcomes and scales.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the grades from.
|
||||
* @param {number} moduleId Module ID.
|
||||
* @param {number} [userId] ID of the user to get the grades from. If not defined use site's current user.
|
||||
* @param {number} [groupId] ID of the group to get the grades from. Not used for old gradebook table.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {boolean} [ignoreCache=false] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @return {Promise<any>} Promise to be resolved when the grades are retrieved.
|
||||
*/
|
||||
getGradeModuleItems(courseId: number, moduleId: number, userId?: number, groupId?: number, siteId?: string,
|
||||
ignoreCache: boolean = false): Promise<any> {
|
||||
|
||||
return this.gradesProvider.getGradeItems(courseId, userId, groupId, siteId, ignoreCache).then((grades) => {
|
||||
if (grades) {
|
||||
if (typeof grades.tabledata != 'undefined') {
|
||||
// Table format.
|
||||
return this.getModuleGradesTableRows(grades, moduleId);
|
||||
} else {
|
||||
return grades.filter((item) => {
|
||||
return item.cmid == moduleId;
|
||||
}).map((item) => {
|
||||
return this.formatGradeItem(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the link to the module for the selected grade.
|
||||
*
|
||||
* @param {string} text HTML where the link is present.
|
||||
* @return {string | false} URL linking to the module.
|
||||
*/
|
||||
protected getModuleLink(text: string): string | false {
|
||||
const el = this.domUtils.toDom(text)[0],
|
||||
link = el.attributes['href'] ? el.attributes['href'].value : false;
|
||||
|
||||
if (!link || link.indexOf('/mod/') < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a row from the grades table.
|
||||
*
|
||||
* @param {any} table JSON object representing a table with data.
|
||||
* @param {number} gradeId Grade Object identifier.
|
||||
* @return {any} Formatted HTML table.
|
||||
*/
|
||||
getGradesTableRow(table: any, gradeId: number): any {
|
||||
if (table.tabledata) {
|
||||
const selectedRow = table.tabledata.find((row) => {
|
||||
return row.itemname && row.itemname.id && row.itemname.id.substr(0, 3) == 'row' &&
|
||||
parseInt(row.itemname.id.split('_')[1], 10) == gradeId;
|
||||
});
|
||||
|
||||
if (selectedRow) {
|
||||
return this.formatGradeRow(selectedRow);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rows related to a module from the grades table.
|
||||
*
|
||||
* @param {any} table JSON object representing a table with data.
|
||||
* @param {number} moduleId Grade Object identifier.
|
||||
* @return {any} Formatted HTML table.
|
||||
*/
|
||||
getModuleGradesTableRows(table: any, moduleId: number): any {
|
||||
|
||||
if (table.tabledata) {
|
||||
// Find href containing "/mod/xxx/xxx.php".
|
||||
const regex = /href="([^"]*\/mod\/[^"|^\/]*\/[^"|^\.]*\.php[^"]*)/;
|
||||
|
||||
return table.tabledata.filter((row) => {
|
||||
if (row.itemname && row.itemname.content) {
|
||||
const matches = row.itemname.content.match(regex);
|
||||
|
||||
if (matches && matches.length) {
|
||||
const hrefParams = this.urlUtils.extractUrlParams(matches[1]);
|
||||
|
||||
return hrefParams && hrefParams.id == moduleId;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}).map((row) => {
|
||||
return this.formatGradeRow(row);
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the grade items for a certain module.
|
||||
*
|
||||
* @param {number} courseId ID of the course to invalidate the grades.
|
||||
* @param {number} [userId] ID of the user to invalidate. If not defined use site's current user.
|
||||
* @param {number} [groupId] ID of the group to invalidate. Not used for old gradebook table.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise} Promise to be resolved when the grades are invalidated.
|
||||
*/
|
||||
invalidateGradeModuleItems(courseId: number, userId?: number, groupId?: number, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
return this.gradesProvider.isGradeItemsAvalaible(siteId).then((enabled) => {
|
||||
if (enabled) {
|
||||
return this.gradesProvider.invalidateCourseGradesItemsData(courseId, userId, groupId, siteId);
|
||||
} else {
|
||||
return this.gradesProvider.invalidateCourseGradesData(courseId, userId, siteId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the image and sets it to the row.
|
||||
*
|
||||
* @param {any} row Formatted grade row object.
|
||||
* @param {string} text HTML where the image will be rendered.
|
||||
* @return {any} Row object with the image.
|
||||
*/
|
||||
protected setRowIcon(row: any, text: string): any {
|
||||
text = text.replace('%2F', '/').replace('%2f', '/');
|
||||
|
||||
if (text.indexOf('/agg_mean') > -1) {
|
||||
row['itemtype'] = 'agg_mean';
|
||||
row['image'] = 'assets/img/grades/agg_mean.png';
|
||||
} else if (text.indexOf('/agg_sum') > -1) {
|
||||
row['itemtype'] = 'agg_sum';
|
||||
row['image'] = 'assets/img/grades/agg_sum.png';
|
||||
} else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1) {
|
||||
row['itemtype'] = 'outcome';
|
||||
row['image'] = 'assets/img/grades/outcomes.png';
|
||||
} else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1) {
|
||||
row['itemtype'] = 'category';
|
||||
row['icon'] = 'folder';
|
||||
} else if (text.indexOf('/manual_item') > -1 || text.indexOf('fa-square-o') > -1) {
|
||||
row['itemtype'] = 'manual';
|
||||
row['icon'] = 'square-outline';
|
||||
} else if (text.indexOf('/mod/') > -1) {
|
||||
const module = text.match(/mod\/([^\/]*)\//);
|
||||
if (typeof module[1] != 'undefined') {
|
||||
row['itemtype'] = 'mod';
|
||||
row['itemmodule'] = module[1];
|
||||
row['image'] = this.courseProvider.getModuleIconSrc(module[1]);
|
||||
}
|
||||
} else if (text.indexOf('src=') > -1) {
|
||||
const src = text.match(/src="([^"]*)"/);
|
||||
row['image'] = src[1];
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreGradesProvider } from './grades';
|
||||
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesMainMenuHandler implements CoreMainMenuHandler {
|
||||
name = 'CoreGrades';
|
||||
priority = 950;
|
||||
|
||||
constructor(private gradesProvider: CoreGradesProvider) { }
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean | Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.gradesProvider.isCourseGradesEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreMainMenuHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerData {
|
||||
return {
|
||||
icon: 'stats',
|
||||
title: 'core.grades.grades',
|
||||
page: 'CoreGradesCoursesPage',
|
||||
class: 'core-grades-coursesgrades-handler'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// (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 '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper';
|
||||
import { CoreGradesProvider } from './grades';
|
||||
|
||||
/**
|
||||
* Handler to treat links to overview courses grades.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesOverviewLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreGradesOverviewLinkHandler';
|
||||
pattern = /\/grade\/report\/overview\/index.php/;
|
||||
|
||||
constructor(private linkHelper: CoreContentLinksHelperProvider, private gradesProvider: CoreGradesProvider) {
|
||||
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.linkHelper.goInSite(navCtrl, 'CoreGradesCoursesPage', undefined, 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.gradesProvider.isCourseGradesEnabled(siteId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// (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 '../../user/providers/user-delegate';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper';
|
||||
import { CoreGradesProvider } from './grades';
|
||||
|
||||
/**
|
||||
* Profile grades handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesUserHandler implements CoreUserProfileHandler {
|
||||
name = 'mmGrades';
|
||||
priority = 400;
|
||||
type = CoreUserDelegate.TYPE_NEW_PAGE;
|
||||
viewGradesEnabledCache = {};
|
||||
|
||||
constructor(private linkHelper: CoreContentLinksHelperProvider, protected sitesProvider: CoreSitesProvider,
|
||||
private gradesProvider: CoreGradesProvider) { }
|
||||
|
||||
/**
|
||||
* Clear view grades cache.
|
||||
* If a courseId and userId are specified, it will only delete the entry for that user and course.
|
||||
*
|
||||
* @param {number} [courseId] Course ID.
|
||||
* @param {number} [userId] User ID.
|
||||
*/
|
||||
clearViewGradesCache(courseId?: number, userId?: number): void {
|
||||
if (courseId && userId) {
|
||||
delete this.viewGradesEnabledCache[this.getCacheKey(courseId, userId)];
|
||||
} else {
|
||||
this.viewGradesEnabledCache = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cache key to identify a course and a user.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {number} userId User ID.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCacheKey(courseId: number, userId: number): string {
|
||||
return courseId + '#' + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if handler is enabled.
|
||||
*
|
||||
* @return {boolean} Always enabled.
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $mmCourses#getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See $mmCourses#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> {
|
||||
const cacheKey = this.getCacheKey(courseId, user.id),
|
||||
cache = this.viewGradesEnabledCache[cacheKey];
|
||||
if (typeof cache != 'undefined') {
|
||||
return cache;
|
||||
}
|
||||
|
||||
return this.gradesProvider.isPluginEnabledForCourse(courseId).then(() => {
|
||||
return this.gradesProvider.getCourseGradesTable(courseId, user.id).then(() => {
|
||||
this.viewGradesEnabledCache[cacheKey] = true;
|
||||
|
||||
return true;
|
||||
}).catch(() => {
|
||||
this.viewGradesEnabledCache[cacheKey] = false;
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreUserProfileHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData {
|
||||
return {
|
||||
icon: 'stats',
|
||||
title: 'core.grades.grades',
|
||||
class: 'core-grades-user-handler',
|
||||
action: (event, navCtrl, user, courseId): void => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const pageParams = {
|
||||
courseId: courseId,
|
||||
userId: user.id
|
||||
};
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursePage', pageParams);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// (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 '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper';
|
||||
import { CoreGradesProvider } from './grades';
|
||||
|
||||
/**
|
||||
* Handler to treat links to user grades.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreGradesUserLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreGradesUserLinkHandler';
|
||||
pattern = /\/grade\/report\/user\/index.php/;
|
||||
|
||||
constructor(private linkHelper: CoreContentLinksHelperProvider, private gradesProvider: CoreGradesProvider) {
|
||||
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 => {
|
||||
const pageParams = {
|
||||
course: {id: courseId},
|
||||
userId: params.userid ? parseInt(params.userid, 10) : false,
|
||||
};
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursePage', pageParams, 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> {
|
||||
if (!courseId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.gradesProvider.isPluginEnabledForCourse(courseId, siteId);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<ion-tabs *ngIf="loaded" #mainTabs [selectedIndex]="initialTab">
|
||||
<ion-tabs *ngIf="loaded" #mainTabs [selectedIndex]="initialTab" tabsPlacement="bottom" tabsLayout="title-hide">
|
||||
<ion-tab [enabled]="false" [show]="false" [root]="redirectPage" [rootParams]="redirectParams"></ion-tab>
|
||||
<ion-tab *ngFor="let tab of tabs" [root]="tab.page" [tabTitle]="tab.title | translate" [tabIcon]="tab.icon" class="{{tab.class}}"></ion-tab>
|
||||
</ion-tabs>
|
|
@ -17,7 +17,6 @@ import { NavController } from 'ionic-angular';
|
|||
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate';
|
||||
import { CoreCourseProvider } from '../../course/providers/course';
|
||||
import { CoreUserProvider } from './user';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreUserParticipantsComponent } from '../components/participants/participants';
|
||||
|
||||
/**
|
||||
|
@ -25,10 +24,10 @@ import { CoreUserParticipantsComponent } from '../components/participants/partic
|
|||
*/
|
||||
@Injectable()
|
||||
export class CoreUserParticipantsCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'AddonParticipants';
|
||||
name = 'CoreUserParticipants';
|
||||
priority = 600;
|
||||
|
||||
constructor(private userProvider: CoreUserProvider, private loginHelper: CoreLoginHelperProvider) {}
|
||||
constructor(private userProvider: CoreUserProvider) {}
|
||||
|
||||
/**
|
||||
* Should invalidate the data to determine if the handler is enabled for a certain course.
|
||||
|
|
|
@ -23,7 +23,7 @@ import { CoreUserProvider } from './user';
|
|||
*/
|
||||
@Injectable()
|
||||
export class CoreUserParticipantsLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'AddonParticipants';
|
||||
name = 'CoreUserParticipants';
|
||||
featureName = '$mmCoursesDelegate_mmaParticipants';
|
||||
pattern = /\/user\/index\.php/;
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
"download": "Download",
|
||||
"downloading": "Downloading",
|
||||
"edit": "Edit",
|
||||
"emptysplit": "This page will appear blank if the left panel is empty or is loading.",
|
||||
"emptysplit": "This page will appear blank if the side panel is empty or is loading.",
|
||||
"error": "Error",
|
||||
"errorchangecompletion": "An error occurred while changing the completion status. Please try again.",
|
||||
"errordeletefile": "Error deleting the file. Please try again.",
|
||||
|
|
|
@ -191,6 +191,15 @@ export class CoreAppProvider {
|
|||
return this.platform.is('cordova');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current window is wider than a mobile.
|
||||
*
|
||||
* @return {boolean} Whether the app the current window is wider than a mobile.
|
||||
*/
|
||||
isWide(): boolean {
|
||||
return this.platform.width() > 768;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we are online.
|
||||
*
|
||||
|
|
|
@ -664,6 +664,19 @@ export class CoreSitesProvider {
|
|||
return this.currentSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site home ID of the current site.
|
||||
*
|
||||
* @return {number} Current site home ID.
|
||||
*/
|
||||
getCurrentSiteHomeId(): number {
|
||||
if (this.currentSite) {
|
||||
return this.currentSite.getSiteHomeId();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current site ID.
|
||||
*
|
||||
|
@ -678,15 +691,15 @@ export class CoreSitesProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the site home ID of the current site.
|
||||
* Get current site User ID.
|
||||
*
|
||||
* @return {number} Current site home ID.
|
||||
* @return {number} Current site User ID.
|
||||
*/
|
||||
getCurrentSiteHomeId(): number {
|
||||
getCurrentSiteUserId(): number {
|
||||
if (this.currentSite) {
|
||||
return this.currentSite.getSiteHomeId();
|
||||
return this.currentSite.getUserId();
|
||||
} else {
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -896,6 +896,18 @@ export class CoreDomUtilsProvider {
|
|||
(el.tagName.toLowerCase() == 'input' && this.INPUT_SUPPORT_KEYBOARD.indexOf(el.type) != -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts HTML formatted text to DOM element.
|
||||
* @param {string} text HTML text.
|
||||
* @return {HTMLCollection} Same text converted to HTMLCollection.
|
||||
*/
|
||||
toDom(text: string): HTMLCollection {
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = text;
|
||||
|
||||
return element.children;
|
||||
}
|
||||
|
||||
/**
|
||||
* View an image in a new page or modal.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue