MOBILE-3663 ratings: Implement ratings
This commit is contained in:
		
							parent
							
								
									e35e849ee4
								
							
						
					
					
						commit
						44a03b61e7
					
				@ -58,6 +58,7 @@ import { CORE_MAINMENU_SERVICES } from '@features/mainmenu/mainmenu.module';
 | 
			
		||||
import { CORE_PUSHNOTIFICATIONS_SERVICES } from '@features/pushnotifications/pushnotifications.module';
 | 
			
		||||
import { CORE_QUESTION_SERVICES } from '@features/question/question.module';
 | 
			
		||||
// @todo import { CORE_SHAREDFILES_SERVICES } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
import { CORE_RATING_SERVICES } from '@features/rating/rating.module';
 | 
			
		||||
import { CORE_SEARCH_SERVICES } from '@features/search/search.module';
 | 
			
		||||
import { CORE_SETTINGS_SERVICES } from '@features/settings/settings.module';
 | 
			
		||||
import { CORE_SITEHOME_SERVICES } from '@features/sitehome/sitehome.module';
 | 
			
		||||
@ -266,6 +267,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...CORE_LOGIN_SERVICES,
 | 
			
		||||
            ...CORE_QUESTION_SERVICES,
 | 
			
		||||
            ...CORE_PUSHNOTIFICATIONS_SERVICES,
 | 
			
		||||
            ...CORE_RATING_SERVICES,
 | 
			
		||||
            ...CORE_SEARCH_SERVICES,
 | 
			
		||||
            ...CORE_SETTINGS_SERVICES,
 | 
			
		||||
            // @todo ...CORE_SHAREDFILES_SERVICES,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										144
									
								
								src/core/features/rating/components/aggregate/aggregate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/core/features/rating/components/aggregate/aggregate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Component, Input, OnChanges, OnDestroy } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
    CoreRating,
 | 
			
		||||
    CoreRatingInfo,
 | 
			
		||||
    CoreRatingInfoItem,
 | 
			
		||||
    CoreRatingProvider,
 | 
			
		||||
} from '@features/rating/services/rating';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { ModalController } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents, CoreEventSiteUpdatedData } from '@singletons/events';
 | 
			
		||||
import { CoreRatingRatingsComponent } from '../ratings/ratings';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays the aggregation of a rating item.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-rating-aggregate',
 | 
			
		||||
    templateUrl: 'core-rating-aggregate.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreRatingAggregateComponent implements OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() ratingInfo!: CoreRatingInfo;
 | 
			
		||||
    @Input() contextLevel!: ContextLevel;
 | 
			
		||||
    @Input() instanceId!: number;
 | 
			
		||||
    @Input() itemId!: number;
 | 
			
		||||
    @Input() aggregateMethod!: number;
 | 
			
		||||
    @Input() scaleId!: number;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
 | 
			
		||||
    item?: CoreRatingInfoItem;
 | 
			
		||||
    showCount = false;
 | 
			
		||||
    disabled = false;
 | 
			
		||||
    labelKey = '';
 | 
			
		||||
 | 
			
		||||
    protected aggregateObserver: CoreEventObserver;
 | 
			
		||||
    protected updateSiteObserver: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.disabled = CoreRating.isRatingDisabledInSite();
 | 
			
		||||
 | 
			
		||||
        // Update visibility if current site info is updated.
 | 
			
		||||
        this.updateSiteObserver = CoreEvents.on<CoreEventSiteUpdatedData>(CoreEvents.SITE_UPDATED, () => {
 | 
			
		||||
            this.disabled = CoreRating.isRatingDisabledInSite();
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        // Update aggrgate when the user adds or edits a rating.
 | 
			
		||||
        this.aggregateObserver =
 | 
			
		||||
            CoreEvents.on(CoreRatingProvider.AGGREGATE_CHANGED_EVENT, (data) => {
 | 
			
		||||
                if (this.item &&
 | 
			
		||||
                    data.contextLevel == this.contextLevel &&
 | 
			
		||||
                    data.instanceId == this.instanceId &&
 | 
			
		||||
                    data.component == this.ratingInfo.component &&
 | 
			
		||||
                    data.ratingArea == this.ratingInfo.ratingarea &&
 | 
			
		||||
                    data.itemId == this.itemId) {
 | 
			
		||||
                    this.item.aggregatestr = data.aggregate;
 | 
			
		||||
                    this.item.count = data.count;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Detect changes on input properties.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnChanges(): void {
 | 
			
		||||
        this.aggregateObserver && this.aggregateObserver.off();
 | 
			
		||||
 | 
			
		||||
        this.item = (this.ratingInfo.ratings || []).find((rating) => rating.itemid == this.itemId);
 | 
			
		||||
        if (!this.item) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (this.aggregateMethod) {
 | 
			
		||||
            case CoreRatingProvider.AGGREGATE_AVERAGE:
 | 
			
		||||
                this.labelKey = 'core.rating.aggregateavg';
 | 
			
		||||
                break;
 | 
			
		||||
            case CoreRatingProvider.AGGREGATE_COUNT:
 | 
			
		||||
                this.labelKey = 'core.rating.aggregatecount';
 | 
			
		||||
                break;
 | 
			
		||||
            case CoreRatingProvider.AGGREGATE_MAXIMUM:
 | 
			
		||||
                this.labelKey = 'core.rating.aggregatemax';
 | 
			
		||||
                break;
 | 
			
		||||
            case CoreRatingProvider.AGGREGATE_MINIMUM:
 | 
			
		||||
                this.labelKey = 'core.rating.aggregatemin';
 | 
			
		||||
                break;
 | 
			
		||||
            case CoreRatingProvider.AGGREGATE_SUM:
 | 
			
		||||
                this.labelKey = 'core.rating.aggregatesum';
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                this.labelKey = '';
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.showCount = (this.aggregateMethod != CoreRatingProvider.AGGREGATE_COUNT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the individual ratings page.
 | 
			
		||||
     */
 | 
			
		||||
    async openRatings(): Promise<void> {
 | 
			
		||||
        if (!this.ratingInfo.canviewall || !this.item!.count || this.disabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const modal = await ModalController.create({
 | 
			
		||||
            component: CoreRatingRatingsComponent,
 | 
			
		||||
            componentProps: {
 | 
			
		||||
                contextLevel: this.contextLevel,
 | 
			
		||||
                instanceId: this.instanceId,
 | 
			
		||||
                ratingComponent: this.ratingInfo.component,
 | 
			
		||||
                ratingArea: this.ratingInfo.ratingarea,
 | 
			
		||||
                itemId: this.itemId,
 | 
			
		||||
                scaleId: this.scaleId,
 | 
			
		||||
                courseId: this.courseId,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await modal.present();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.aggregateObserver.off();
 | 
			
		||||
        this.updateSiteObserver.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
<ion-item *ngIf="item && item!.canviewaggregate && labelKey && !disabled" class="ion-text-wrap"
 | 
			
		||||
    [detail]="ratingInfo.canviewall && item!.count" (click)="openRatings()">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        {{ labelKey | translate }}{{ 'core.labelsep' | translate }} {{ item!.aggregatestr || '-' }}
 | 
			
		||||
        <span *ngIf="showCount && item!.count && item!.count! > 0">({{ item!.count! }})</span>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
							
								
								
									
										39
									
								
								src/core/features/rating/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/core/features/rating/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { CoreRatingAggregateComponent } from './aggregate/aggregate';
 | 
			
		||||
import { CoreRatingRateComponent } from './rate/rate';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreRatingRatingsComponent } from './ratings/ratings';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreRatingAggregateComponent,
 | 
			
		||||
        CoreRatingRateComponent,
 | 
			
		||||
        CoreRatingRatingsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreRatingAggregateComponent,
 | 
			
		||||
        CoreRatingRateComponent,
 | 
			
		||||
        CoreRatingRatingsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    entryComponents: [
 | 
			
		||||
        CoreRatingRatingsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreRatingComponentsModule {}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
<ion-item class="ion-text-wrap" *ngIf="item && (item!.canrate || item!.rating != null) && !disabled">
 | 
			
		||||
    <ion-label>{{ 'core.rating.rating' | translate }}</ion-label>
 | 
			
		||||
    <ion-select class="ion-text-start" [(ngModel)]="rating" (ngModelChange)="userRatingChanged()" interface="action-sheet"
 | 
			
		||||
        [disabled]="!item!.canrate">
 | 
			
		||||
        <ion-select-option *ngFor="let scaleItem of scale!.items" [value]="scaleItem.value">{{ scaleItem.name }}</ion-select-option>
 | 
			
		||||
    </ion-select>
 | 
			
		||||
</ion-item>
 | 
			
		||||
							
								
								
									
										164
									
								
								src/core/features/rating/components/rate/rate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/core/features/rating/components/rate/rate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,164 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Component, EventEmitter, Input, OnChanges, Output, OnDestroy } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
    CoreRatingProvider,
 | 
			
		||||
    CoreRatingInfo,
 | 
			
		||||
    CoreRatingInfoItem,
 | 
			
		||||
    CoreRatingScale,
 | 
			
		||||
    CoreRating,
 | 
			
		||||
} from '@features/rating/services/rating';
 | 
			
		||||
import { CoreRatingOffline } from '@features/rating/services/rating-offline';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents, CoreEventSiteUpdatedData } from '@singletons/events';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays the user rating select.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-rating-rate',
 | 
			
		||||
    templateUrl: 'core-rating-rate.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreRatingRateComponent implements OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() protected ratingInfo!: CoreRatingInfo;
 | 
			
		||||
    @Input() protected contextLevel!: ContextLevel; // Context level: course, module, user, etc.
 | 
			
		||||
    @Input() protected instanceId!: number; // Context instance id.
 | 
			
		||||
    @Input() protected itemId!: number; // Item id. Example: forum post id.
 | 
			
		||||
    @Input() protected itemSetId!: number; // Item set id. Example: forum discussion id.
 | 
			
		||||
    @Input() protected courseId!: number;
 | 
			
		||||
    @Input() protected aggregateMethod!: number;
 | 
			
		||||
    @Input() protected scaleId!: number;
 | 
			
		||||
    @Input() protected userId!: number;
 | 
			
		||||
    @Output() protected onLoading: EventEmitter<boolean>; // Eevent that indicates whether the component is loading data.
 | 
			
		||||
    @Output() protected onUpdate: EventEmitter<void>; // Event emitted when the rating is updated online.
 | 
			
		||||
 | 
			
		||||
    item?: CoreRatingInfoItem;
 | 
			
		||||
    scale?: CoreRatingScale;
 | 
			
		||||
    rating?: number;
 | 
			
		||||
    disabled = false;
 | 
			
		||||
 | 
			
		||||
    protected updateSiteObserver: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
 | 
			
		||||
        this.onLoading = new EventEmitter<boolean>();
 | 
			
		||||
        this.onUpdate = new EventEmitter<void>();
 | 
			
		||||
 | 
			
		||||
        this.disabled = CoreRating.isRatingDisabledInSite();
 | 
			
		||||
 | 
			
		||||
        // Update visibility if current site info is updated.
 | 
			
		||||
        this.updateSiteObserver = CoreEvents.on<CoreEventSiteUpdatedData>(CoreEvents.SITE_UPDATED, () => {
 | 
			
		||||
            this.disabled = CoreRating.isRatingDisabledInSite();
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Detect changes on input properties.
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnChanges(): Promise<void> {
 | 
			
		||||
        this.item = (this.ratingInfo.ratings || []).find((rating) => rating.itemid == this.itemId);
 | 
			
		||||
        this.scale = (this.ratingInfo.scales || []).find((scale) => scale.id == this.scaleId);
 | 
			
		||||
 | 
			
		||||
        if (!this.item || !this.scale || !CoreRating.isAddRatingWSAvailable()) {
 | 
			
		||||
            this.item = undefined;
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set numeric scale items.
 | 
			
		||||
        if (!this.scale.items) {
 | 
			
		||||
            this.scale.items = [];
 | 
			
		||||
            if (this.scale.isnumeric) {
 | 
			
		||||
                for (let n = 0; n <= this.scale.max; n++) {
 | 
			
		||||
                    this.scale.items.push({ name: String(n), value: n });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add "No rating" item to the scale.
 | 
			
		||||
        if (!this.scale.items[0] || this.scale.items[0].value != CoreRatingProvider.UNSET_RATING) {
 | 
			
		||||
            this.scale.items.unshift({
 | 
			
		||||
                name: Translate.instant('core.none'),
 | 
			
		||||
                value: CoreRatingProvider.UNSET_RATING,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.onLoading.emit(true);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const rating = await CoreRatingOffline.getRating(
 | 
			
		||||
                this.contextLevel,
 | 
			
		||||
                this.instanceId,
 | 
			
		||||
                this.ratingInfo.component,
 | 
			
		||||
                this.ratingInfo.ratingarea,
 | 
			
		||||
                this.itemId,
 | 
			
		||||
            );
 | 
			
		||||
            this.rating = rating.rating;
 | 
			
		||||
        } catch {
 | 
			
		||||
            if (this.item && this.item.rating != null) {
 | 
			
		||||
                this.rating = this.item.rating;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.rating = CoreRatingProvider.UNSET_RATING;
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.onLoading.emit(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send or save the user rating when changed.
 | 
			
		||||
     */
 | 
			
		||||
    async userRatingChanged(): Promise<void> {
 | 
			
		||||
        const modal = await CoreDomUtils.showModalLoading('core.sending', true);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const response = await CoreRating.addRating(
 | 
			
		||||
                this.ratingInfo.component,
 | 
			
		||||
                this.ratingInfo.ratingarea,
 | 
			
		||||
                this.contextLevel,
 | 
			
		||||
                this.instanceId,
 | 
			
		||||
                this.itemId,
 | 
			
		||||
                this.itemSetId,
 | 
			
		||||
                this.courseId,
 | 
			
		||||
                this.scaleId,
 | 
			
		||||
                this.rating!,
 | 
			
		||||
                this.userId,
 | 
			
		||||
                this.aggregateMethod,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if (typeof response == 'undefined') {
 | 
			
		||||
                CoreDomUtils.showToast('core.datastoredoffline', true, 3000);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.onUpdate.emit();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
            modal.dismiss();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.updateSiteObserver.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-title>{{ 'core.rating.ratings' | translate }}</ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button  (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon slot="icon-only" name="fas-times"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <ion-list *ngIf="ratings.length > 0">
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngFor="let rating of ratings">
 | 
			
		||||
                <core-user-avatar [user]="rating" [courseId]="courseId" slot="start"></core-user-avatar>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ rating.userfullname }}</h2>
 | 
			
		||||
                    <p>{{ rating.rating }}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-note slot="end" class="ion-padding-left" *ngIf="rating.timemodified">
 | 
			
		||||
                    {{ rating.timemodified | coreDateDayOrTime }}
 | 
			
		||||
                </ion-note>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
        <core-empty-box *ngIf="ratings.length == 0" icon="fas-star-half-alt" [message]="'core.rating.noratings' | translate"></core-empty-box>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										70
									
								
								src/core/features/rating/components/ratings/ratings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/core/features/rating/components/ratings/ratings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreRating, CoreRatingItemRating } from '@features/rating/services/rating';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { ModalController } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modal that displays individual ratings
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    templateUrl: 'ratings-modal.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreRatingRatingsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() protected contextLevel!: ContextLevel;
 | 
			
		||||
    @Input() protected instanceId!: number;
 | 
			
		||||
    @Input() protected ratingComponent!: string;
 | 
			
		||||
    @Input() protected ratingArea!: string;
 | 
			
		||||
    @Input() protected aggregateMethod!: number;
 | 
			
		||||
    @Input() protected itemId!: number;
 | 
			
		||||
    @Input() protected scaleId!: number;
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    ratings: CoreRatingItemRating[] = [];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Modal loaded.
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            this.ratings = await CoreRating.getItemRatings(
 | 
			
		||||
                this.contextLevel,
 | 
			
		||||
                this.instanceId,
 | 
			
		||||
                this.ratingComponent,
 | 
			
		||||
                this.ratingArea,
 | 
			
		||||
                this.itemId,
 | 
			
		||||
                this.scaleId,
 | 
			
		||||
                undefined,
 | 
			
		||||
                this.courseId,
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
     */
 | 
			
		||||
    closeModal(): void {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/core/features/rating/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/core/features/rating/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
    "aggregateavg": "Average of ratings",
 | 
			
		||||
    "aggregatecount": "Count of ratings",
 | 
			
		||||
    "aggregatemax": "Maximum rating",
 | 
			
		||||
    "aggregatemin": "Minimum rating",
 | 
			
		||||
    "aggregatesum": "Sum of ratings",
 | 
			
		||||
    "noratings": "No ratings submitted",
 | 
			
		||||
    "rating": "Rating",
 | 
			
		||||
    "ratings": "Ratings"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/core/features/rating/rating.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/core/features/rating/rating.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { NgModule, Type } from '@angular/core';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
import { RATINGS_SITE_SCHEMA } from './services/database/rating';
 | 
			
		||||
import { CoreRatingProvider } from './services/rating';
 | 
			
		||||
import { CoreRatingOfflineProvider } from './services/rating-offline';
 | 
			
		||||
import { CoreRatingSyncProvider } from './services/rating-sync';
 | 
			
		||||
 | 
			
		||||
export const CORE_RATING_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    CoreRatingProvider,
 | 
			
		||||
    CoreRatingSyncProvider,
 | 
			
		||||
    CoreRatingOfflineProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [RATINGS_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreRatingModule {}
 | 
			
		||||
							
								
								
									
										101
									
								
								src/core/features/rating/services/database/rating.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/core/features/rating/services/database/rating.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for CoreRatingOffline service.
 | 
			
		||||
 */
 | 
			
		||||
export const RATINGS_TABLE = 'rating_ratings';
 | 
			
		||||
export const RATINGS_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'CoreRatingOfflineProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: RATINGS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'component',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'ratingarea',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'contextlevel',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'instanceid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'itemid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'itemsetid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'scaleid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'rating',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'rateduserid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'aggregation',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['component', 'ratingarea', 'contextlevel', 'instanceid', 'itemid'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Primary data to identify a stored rating.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingDBPrimaryData = {
 | 
			
		||||
    component: string;
 | 
			
		||||
    ratingarea: string;
 | 
			
		||||
    contextlevel: ContextLevel;
 | 
			
		||||
    instanceid: number;
 | 
			
		||||
    itemid: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Rating stored.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingDBRecord = CoreRatingDBPrimaryData & {
 | 
			
		||||
    itemsetid: number;
 | 
			
		||||
    courseid?: number;
 | 
			
		||||
    scaleid: number;
 | 
			
		||||
    rating: number;
 | 
			
		||||
    rateduserid: number;
 | 
			
		||||
    aggregation: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										271
									
								
								src/core/features/rating/services/rating-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								src/core/features/rating/services/rating-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,271 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreRatingDBPrimaryData, CoreRatingDBRecord, RATINGS_TABLE } from './database/rating';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure of item sets.
 | 
			
		||||
 */
 | 
			
		||||
export interface CoreRatingItemSet {
 | 
			
		||||
    component: string;
 | 
			
		||||
    ratingArea: string;
 | 
			
		||||
    contextLevel: ContextLevel;
 | 
			
		||||
    instanceId: number;
 | 
			
		||||
    itemSetId: number;
 | 
			
		||||
    courseId?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle offline data for rating.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class CoreRatingOfflineProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an offline rating.
 | 
			
		||||
     *
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the saved rating, rejected if not found.
 | 
			
		||||
     */
 | 
			
		||||
    async getRating(
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingDBRecord> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: CoreRatingDBPrimaryData = {
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            component: component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecord(RATINGS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add an offline rating.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param courseId Course id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param rating Rating value. Use CoreRatingProvider.UNSET_RATING to delete rating.
 | 
			
		||||
     * @param ratedUserId Rated user id.
 | 
			
		||||
     * @param aggregateMethod Aggregate method.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the rating is saved.
 | 
			
		||||
     */
 | 
			
		||||
    async addRating(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        rating: number,
 | 
			
		||||
        ratedUserId: number,
 | 
			
		||||
        aggregateMethod: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const data: CoreRatingDBRecord = {
 | 
			
		||||
            component: component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
            itemsetid: itemSetId,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            scaleid: scaleId,
 | 
			
		||||
            rating: rating,
 | 
			
		||||
            rateduserid: ratedUserId,
 | 
			
		||||
            aggregation: aggregateMethod,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(RATINGS_TABLE, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete offline rating.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the rating is saved.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteRating(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: CoreRatingDBPrimaryData = {
 | 
			
		||||
            component: component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(RATINGS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the list of item sets in a component or instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the list of item set ids.
 | 
			
		||||
     */
 | 
			
		||||
    async getItemSets(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel?: ContextLevel,
 | 
			
		||||
        instanceId?: number,
 | 
			
		||||
        itemSetId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingItemSet[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const fields = 'DISTINCT contextlevel, instanceid, itemsetid, courseid';
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<CoreRatingDBRecord> = {
 | 
			
		||||
            component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (contextLevel && instanceId) {
 | 
			
		||||
            conditions.contextlevel = contextLevel;
 | 
			
		||||
            conditions.instanceid = instanceId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (itemSetId) {
 | 
			
		||||
            conditions.itemsetid = itemSetId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<CoreRatingDBRecord>(RATINGS_TABLE, conditions, undefined, fields);
 | 
			
		||||
 | 
			
		||||
        return records.map((record) => ({
 | 
			
		||||
            component,
 | 
			
		||||
            ratingArea,
 | 
			
		||||
            contextLevel: record.contextlevel,
 | 
			
		||||
            instanceId: record.instanceid,
 | 
			
		||||
            itemSetId: record.itemsetid,
 | 
			
		||||
            courseId: record.courseid,
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get offline ratings of an item set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the list of ratings.
 | 
			
		||||
     */
 | 
			
		||||
    async getRatings(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<CoreRatingDBRecord> = {
 | 
			
		||||
            component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            itemsetid: itemSetId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords(RATINGS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return whether a component, instance or item set has offline ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with a boolean.
 | 
			
		||||
     */
 | 
			
		||||
    async hasRatings(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel?: ContextLevel,
 | 
			
		||||
        instanceId?: number,
 | 
			
		||||
        itemSetId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<CoreRatingDBRecord> = {
 | 
			
		||||
            component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
        };
 | 
			
		||||
        if (contextLevel && instanceId) {
 | 
			
		||||
            conditions.contextlevel = contextLevel;
 | 
			
		||||
            conditions.instanceid = instanceId;
 | 
			
		||||
        }
 | 
			
		||||
        if (itemSetId) {
 | 
			
		||||
            conditions.itemsetid = itemSetId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoreUtils.promiseWorks(site.getDb().recordExists(RATINGS_TABLE, conditions));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const CoreRatingOffline = makeSingleton(CoreRatingOfflineProvider);
 | 
			
		||||
							
								
								
									
										315
									
								
								src/core/features/rating/services/rating-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								src/core/features/rating/services/rating-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,315 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
 | 
			
		||||
import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreRating } from './rating';
 | 
			
		||||
import { CoreRatingItemSet, CoreRatingOffline } from './rating-offline';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to sync ratings.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class CoreRatingSyncProvider extends CoreSyncBaseProvider<CoreRatingSyncItem> {
 | 
			
		||||
 | 
			
		||||
    static readonly SYNCED_EVENT = 'core_rating_synced';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('CoreRatingSyncProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize all the ratings of a certain component, instance or item set.
 | 
			
		||||
     *
 | 
			
		||||
     * This function should be called from the sync provider of activities with ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id.
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    async syncRatings(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel?: ContextLevel,
 | 
			
		||||
        instanceId?: number,
 | 
			
		||||
        itemSetId?: number,
 | 
			
		||||
        force?: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingSyncItem[]> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const itemSets = await CoreRatingOffline.getItemSets(component, ratingArea, contextLevel, instanceId, itemSetId, siteId);
 | 
			
		||||
 | 
			
		||||
        const results: CoreRatingSyncItem[] = [];
 | 
			
		||||
        await Promise.all(itemSets.map(async (itemSet) => {
 | 
			
		||||
            const result = force
 | 
			
		||||
                ? await this.syncItemSet(
 | 
			
		||||
                    component,
 | 
			
		||||
                    ratingArea,
 | 
			
		||||
                    itemSet.contextLevel,
 | 
			
		||||
                    itemSet.instanceId,
 | 
			
		||||
                    itemSet.itemSetId,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                )
 | 
			
		||||
                : await this.syncItemSetIfNeeded(
 | 
			
		||||
                    component,
 | 
			
		||||
                    ratingArea,
 | 
			
		||||
                    itemSet.contextLevel,
 | 
			
		||||
                    itemSet.instanceId,
 | 
			
		||||
                    itemSet.itemSetId,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
            if (result) {
 | 
			
		||||
                if (result.updated) {
 | 
			
		||||
                    // Sync successful, send event.
 | 
			
		||||
                    CoreEvents.trigger(CoreRatingSyncProvider.SYNCED_EVENT, {
 | 
			
		||||
                        ...itemSet,
 | 
			
		||||
                        warnings: result.warnings,
 | 
			
		||||
                    }, siteId);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                results.push(
 | 
			
		||||
                    {
 | 
			
		||||
                        itemSet,
 | 
			
		||||
                        ...result,
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync ratings of an item set only if a certain time has passed since the last time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when ratings are synced or if it doesn't need to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncItemSetIfNeeded(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingSyncItem | undefined> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const syncId = this.getItemSetSyncId(component, ratingArea, contextLevel, instanceId, itemSetId);
 | 
			
		||||
 | 
			
		||||
        const needed = await this.isSyncNeeded(syncId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (needed) {
 | 
			
		||||
            return this.syncItemSet(component, ratingArea, contextLevel, instanceId, itemSetId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize all offline ratings of an item set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected syncItemSet(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingSyncItem> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const syncId = this.getItemSetSyncId(component, ratingArea, contextLevel, instanceId, itemSetId);
 | 
			
		||||
        if (this.isSyncing(syncId, siteId)) {
 | 
			
		||||
            // There's already a sync ongoing for this item set, return the promise.
 | 
			
		||||
            return this.getOngoingSync(syncId, siteId)!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Try to sync ratings of component '${component}' rating area '${ratingArea}'` +
 | 
			
		||||
            ` context level '${contextLevel}' instance ${instanceId} item set ${itemSetId}`);
 | 
			
		||||
 | 
			
		||||
        // Get offline events.
 | 
			
		||||
        const syncPromise = this.performSyncItemSet(component, ratingArea, contextLevel, instanceId, itemSetId, siteId);
 | 
			
		||||
 | 
			
		||||
        return this.addOngoingSync(syncId, syncPromise, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize all offline ratings of an item set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async performSyncItemSet(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<CoreRatingSyncItem> {
 | 
			
		||||
        const result: CoreRatingSyncItem = {
 | 
			
		||||
            updated: [],
 | 
			
		||||
            warnings: [],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const ratings = await CoreRatingOffline.getRatings(component, ratingArea, contextLevel, instanceId, itemSetId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (!ratings.length) {
 | 
			
		||||
            // Nothing to sync.
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // Cannot sync in offline.
 | 
			
		||||
            throw new CoreNetworkError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const promises = ratings.map(async (rating) => {
 | 
			
		||||
            try {
 | 
			
		||||
                await CoreRating.addRatingOnline(
 | 
			
		||||
                    component,
 | 
			
		||||
                    ratingArea,
 | 
			
		||||
                    rating.contextlevel,
 | 
			
		||||
                    rating.instanceid,
 | 
			
		||||
                    rating.itemid,
 | 
			
		||||
                    rating.scaleid,
 | 
			
		||||
                    rating.rating,
 | 
			
		||||
                    rating.rateduserid,
 | 
			
		||||
                    rating.aggregation,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                if (!CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                    // Couldn't connect to server, reject.
 | 
			
		||||
                    throw error;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const warning = CoreTextUtils.getErrorMessageFromError(error);
 | 
			
		||||
 | 
			
		||||
                if (warning) {
 | 
			
		||||
                    result.warnings.push(warning);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result.updated.push(rating.itemid);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                return CoreRatingOffline.deleteRating(
 | 
			
		||||
                    component,
 | 
			
		||||
                    ratingArea,
 | 
			
		||||
                    rating.contextlevel,
 | 
			
		||||
                    rating.instanceid,
 | 
			
		||||
                    rating.itemid,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
            } finally {
 | 
			
		||||
                await CoreRating.invalidateRatingItems(
 | 
			
		||||
                    rating.contextlevel,
 | 
			
		||||
                    rating.instanceid,
 | 
			
		||||
                    component,
 | 
			
		||||
                    ratingArea,
 | 
			
		||||
                    rating.itemid,
 | 
			
		||||
                    rating.scaleid,
 | 
			
		||||
                    undefined,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        // All done, return the result.
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the sync id of an item set.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating Area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @return Sync id.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemSetSyncId(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
    ): string {
 | 
			
		||||
        return `itemSet#${component}#${ratingArea}#${contextLevel}#${instanceId}#${itemSetId}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const CoreRatingSync = makeSingleton(CoreRatingSyncProvider);
 | 
			
		||||
 | 
			
		||||
declare module '@singletons/events' {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Augment CoreEventsData interface with events specific to this service.
 | 
			
		||||
     *
 | 
			
		||||
     * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 | 
			
		||||
     */
 | 
			
		||||
    export interface CoreEventsData {
 | 
			
		||||
        [CoreRatingSyncProvider.SYNCED_EVENT]: CoreRatingSyncEventData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CoreRatingSyncItem = {
 | 
			
		||||
    itemSet?: CoreRatingItemSet;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
    updated: number[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to SYNCED_EVENT event.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingSyncEventData = CoreRatingItemSet & {
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										588
									
								
								src/core/features/rating/services/rating.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								src/core/features/rating/services/rating.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,588 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreSiteWSPreSets, CoreSite } from '@classes/site';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreRatingOffline } from './rating-offline';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'CoreRating:';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle ratings.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class CoreRatingProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly AGGREGATE_NONE = 0; // No ratings.
 | 
			
		||||
    static readonly AGGREGATE_AVERAGE = 1;
 | 
			
		||||
    static readonly AGGREGATE_COUNT = 2;
 | 
			
		||||
    static readonly AGGREGATE_MAXIMUM = 3;
 | 
			
		||||
    static readonly AGGREGATE_MINIMUM = 4;
 | 
			
		||||
    static readonly AGGREGATE_SUM = 5;
 | 
			
		||||
 | 
			
		||||
    static readonly UNSET_RATING = -999;
 | 
			
		||||
 | 
			
		||||
    static readonly AGGREGATE_CHANGED_EVENT = 'core_rating_aggregate_changed';
 | 
			
		||||
    static readonly RATING_SAVED_EVENT = 'core_rating_rating_saved';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether the web serivce to add ratings is available.
 | 
			
		||||
     *
 | 
			
		||||
     * @return If WS is abalaible.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    isAddRatingWSAvailable(): boolean {
 | 
			
		||||
        return CoreSites.wsAvailableInCurrentSite('core_rating_add_rating');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a rating to an item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param itemSetId Item set id. Example: forum discussion id.
 | 
			
		||||
     * @param courseId Course id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param rating Rating value. Use CoreRatingProvider.UNSET_RATING to delete rating.
 | 
			
		||||
     * @param ratedUserId Rated user id.
 | 
			
		||||
     * @param aggregateMethod Aggregate method.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the aggregated rating or void if stored offline.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    async addRating(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        itemSetId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        rating: number,
 | 
			
		||||
        ratedUserId: number,
 | 
			
		||||
        aggregateMethod: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingAddRatingWSResponse | void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // Convenience function to store a rating to be synchronized later.
 | 
			
		||||
        const storeOffline = async (): Promise<void> => {
 | 
			
		||||
            await CoreRatingOffline.addRating(
 | 
			
		||||
                component,
 | 
			
		||||
                ratingArea,
 | 
			
		||||
                contextLevel,
 | 
			
		||||
                instanceId,
 | 
			
		||||
                itemId,
 | 
			
		||||
                itemSetId,
 | 
			
		||||
                courseId,
 | 
			
		||||
                scaleId,
 | 
			
		||||
                rating,
 | 
			
		||||
                ratedUserId,
 | 
			
		||||
                aggregateMethod,
 | 
			
		||||
                siteId,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            CoreEvents.trigger(CoreRatingProvider.RATING_SAVED_EVENT, {
 | 
			
		||||
                component,
 | 
			
		||||
                ratingArea,
 | 
			
		||||
                contextLevel,
 | 
			
		||||
                instanceId,
 | 
			
		||||
                itemSetId,
 | 
			
		||||
                itemId,
 | 
			
		||||
            }, siteId);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // App is offline, store the action.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreRatingOffline.deleteRating(component, ratingArea, contextLevel, instanceId, itemId, siteId);
 | 
			
		||||
            this.addRatingOnline(
 | 
			
		||||
                component,
 | 
			
		||||
                ratingArea,
 | 
			
		||||
                contextLevel,
 | 
			
		||||
                instanceId,
 | 
			
		||||
                itemId,
 | 
			
		||||
                scaleId,
 | 
			
		||||
                rating,
 | 
			
		||||
                ratedUserId,
 | 
			
		||||
                aggregateMethod,
 | 
			
		||||
                siteId,
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error or offline not supported, reject.
 | 
			
		||||
                return Promise.reject(error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Couldn't connect to server, store offline.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a rating to an item. It will fail if offline or cannot connect.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param rating Rating value. Use CoreRatingProvider.UNSET_RATING to delete rating.
 | 
			
		||||
     * @param ratedUserId Rated user id.
 | 
			
		||||
     * @param aggregateMethod Aggregate method.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the aggregated rating.
 | 
			
		||||
     * @since 3.2
 | 
			
		||||
     */
 | 
			
		||||
    async addRatingOnline(
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        rating: number,
 | 
			
		||||
        ratedUserId: number,
 | 
			
		||||
        aggregateMethod: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreRatingAddRatingWSResponse> {
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        const params: CoreRatingAddRatingWSParams = {
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
            scaleid: scaleId,
 | 
			
		||||
            rating,
 | 
			
		||||
            rateduserid: ratedUserId,
 | 
			
		||||
            aggregation: aggregateMethod,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const response = await site.write<CoreRatingAddRatingWSResponse>('core_rating_add_rating', params);
 | 
			
		||||
 | 
			
		||||
        await this.invalidateRatingItems(contextLevel, instanceId, component, ratingArea, itemId, scaleId);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreRatingProvider.AGGREGATE_CHANGED_EVENT, {
 | 
			
		||||
            contextLevel,
 | 
			
		||||
            instanceId,
 | 
			
		||||
            component,
 | 
			
		||||
            ratingArea,
 | 
			
		||||
            itemId,
 | 
			
		||||
            aggregate: response.aggregate,
 | 
			
		||||
            count: response.count,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get item ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param sort Sort field.
 | 
			
		||||
     * @param courseId Course id. Used for fetching user profiles.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
 | 
			
		||||
     * @return Promise resolved with the list of ratings.
 | 
			
		||||
     */
 | 
			
		||||
    async getItemRatings(
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        sort: string = 'timemodified',
 | 
			
		||||
        courseId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
        ignoreCache: boolean = false,
 | 
			
		||||
    ): Promise<CoreRatingItemRating[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const params: CoreRatingGetItemRatingsWSParams = {
 | 
			
		||||
            contextlevel: contextLevel,
 | 
			
		||||
            instanceid: instanceId,
 | 
			
		||||
            component,
 | 
			
		||||
            ratingarea: ratingArea,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
            scaleid: scaleId,
 | 
			
		||||
            sort,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getItemRatingsCacheKey(contextLevel, instanceId, component, ratingArea, itemId, scaleId, sort),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_RARELY,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (ignoreCache) {
 | 
			
		||||
            preSets.getFromCache = false;
 | 
			
		||||
            preSets.emergencyCache = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const response = await site.read<CoreRatingGetItemRatingsWSResponse>('core_rating_get_item_ratings', params, preSets);
 | 
			
		||||
 | 
			
		||||
        if (!site.isVersionGreaterEqualThan([' 3.6.5', '3.7.1', '3.8'])) {
 | 
			
		||||
            // MDL-65042 We need to fetch profiles because the returned profile pictures are incorrect.
 | 
			
		||||
            const promises = response.ratings.map((rating: CoreRatingItemRating) =>
 | 
			
		||||
                CoreUser.getProfile(rating.userid, courseId, true, site.id).then((user) => {
 | 
			
		||||
                    rating.userpictureurl = user.profileimageurl || '';
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }).catch(() => {
 | 
			
		||||
                    // Ignore error.
 | 
			
		||||
                    rating.userpictureurl = '';
 | 
			
		||||
                }));
 | 
			
		||||
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response.ratings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate item ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Context instance id.
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param sort Sort field.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateRatingItems(
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        sort: string = 'timemodified',
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const key = this.getItemRatingsCacheKey(contextLevel, instanceId, component, ratingArea, itemId, scaleId, sort);
 | 
			
		||||
 | 
			
		||||
        await site.invalidateWsCacheForKey(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if rating is disabled in a certain site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param site Site. If not defined, use current site.
 | 
			
		||||
     * @return Whether it's disabled.
 | 
			
		||||
     */
 | 
			
		||||
    isRatingDisabledInSite(site?: CoreSite): boolean {
 | 
			
		||||
        site = site || CoreSites.getCurrentSite();
 | 
			
		||||
 | 
			
		||||
        return !!site?.isFeatureDisabled('NoDelegate_CoreRating');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if rating is disabled in a certain site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site Id. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved with true if disabled, rejected or resolved with false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async isRatingDisabled(siteId?: string): Promise<boolean> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return this.isRatingDisabledInSite(site);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to merge two or more rating infos of the same instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @param ratingInfos Array of rating infos.
 | 
			
		||||
     * @return Merged rating info or undefined.
 | 
			
		||||
     */
 | 
			
		||||
    mergeRatingInfos(ratingInfos: CoreRatingInfo[]): CoreRatingInfo | undefined {
 | 
			
		||||
        let result: CoreRatingInfo | undefined;
 | 
			
		||||
        const scales: Record<number, CoreRatingScale> = {};
 | 
			
		||||
        const ratings: Record<number, CoreRatingInfoItem> = {};
 | 
			
		||||
 | 
			
		||||
        ratingInfos.forEach((ratingInfo) => {
 | 
			
		||||
            if (!ratingInfo) {
 | 
			
		||||
                // Skip null rating infos.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!result) {
 | 
			
		||||
                result = Object.assign({}, ratingInfo);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            (ratingInfo.scales || []).forEach((scale) => {
 | 
			
		||||
                scales[scale.id] = scale;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            (ratingInfo.ratings || []).forEach((rating) => {
 | 
			
		||||
                ratings[rating.itemid] = rating;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (result) {
 | 
			
		||||
            result.scales = CoreUtils.objectToArray(scales);
 | 
			
		||||
            result.ratings = CoreUtils.objectToArray(ratings);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch individual ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * This function should be called from the prefetch handler of activities with ratings.
 | 
			
		||||
     *
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param instanceId Instance id.
 | 
			
		||||
     * @param siteId Site id. If not defined, current site.
 | 
			
		||||
     * @param courseId Course id. Used for prefetching user profiles.
 | 
			
		||||
     * @param ratingInfo Rating info returned by web services.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async prefetchRatings(
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        courseId?: number,
 | 
			
		||||
        ratingInfo?: CoreRatingInfo,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        if (!ratingInfo || !ratingInfo.ratings) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        const promises = ratingInfo.ratings.map((item) => this.getItemRatings(
 | 
			
		||||
            contextLevel,
 | 
			
		||||
            instanceId,
 | 
			
		||||
            ratingInfo.component,
 | 
			
		||||
            ratingInfo.ratingarea,
 | 
			
		||||
            item.itemid,
 | 
			
		||||
            scaleId,
 | 
			
		||||
            undefined,
 | 
			
		||||
            courseId,
 | 
			
		||||
            site.id,
 | 
			
		||||
            true,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        if (!site.isVersionGreaterEqualThan([' 3.6.5', '3.7.1', '3.8'])) {
 | 
			
		||||
            promises.map((promise) => promise.then(async (ratings) => {
 | 
			
		||||
                const userIds = ratings.map((rating: CoreRatingItemRating) => rating.userid);
 | 
			
		||||
 | 
			
		||||
                await CoreUser.prefetchProfiles(userIds, courseId, site.id);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for rating items WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param contextLevel Context level: course, module, user, etc.
 | 
			
		||||
     * @param component Component. Example: "mod_forum".
 | 
			
		||||
     * @param ratingArea Rating area. Example: "post".
 | 
			
		||||
     * @param itemId Item id. Example: forum post id.
 | 
			
		||||
     * @param scaleId Scale id.
 | 
			
		||||
     * @param sort Sort field.
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemRatingsCacheKey(
 | 
			
		||||
        contextLevel: ContextLevel,
 | 
			
		||||
        instanceId: number,
 | 
			
		||||
        component: string,
 | 
			
		||||
        ratingArea: string,
 | 
			
		||||
        itemId: number,
 | 
			
		||||
        scaleId: number,
 | 
			
		||||
        sort: string,
 | 
			
		||||
    ): string {
 | 
			
		||||
        return `${ROOT_CACHE_KEY}${contextLevel}:${instanceId}:${component}:${ratingArea}:${itemId}:${scaleId}:${sort}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const CoreRating = makeSingleton(CoreRatingProvider);
 | 
			
		||||
 | 
			
		||||
declare module '@singletons/events' {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Augment CoreEventsData interface with events specific to this service.
 | 
			
		||||
     *
 | 
			
		||||
     * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 | 
			
		||||
     */
 | 
			
		||||
    export interface CoreEventsData {
 | 
			
		||||
        [CoreRatingProvider.AGGREGATE_CHANGED_EVENT]: CoreRatingAggregateChangedEventData;
 | 
			
		||||
        [CoreRatingProvider.RATING_SAVED_EVENT]: CoreRatingSavedEventData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure of the rating info returned by web services.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingInfo = {
 | 
			
		||||
    contextid: number; // Context id.
 | 
			
		||||
    component: string; // Context name.
 | 
			
		||||
    ratingarea: string; // Rating area name.
 | 
			
		||||
    canviewall?: boolean; // Whether the user can view all the individual ratings.
 | 
			
		||||
    canviewany?: boolean; // Whether the user can view aggregate of ratings of others.
 | 
			
		||||
    scales?: CoreRatingScale[]; // Different scales used information.
 | 
			
		||||
    ratings?: CoreRatingInfoItem[]; // The ratings.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure of scales in the rating info.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingScale = {
 | 
			
		||||
    id: number; // Scale id.
 | 
			
		||||
    courseid?: number; // Course id.
 | 
			
		||||
    name?: string; // Scale name (when a real scale is used).
 | 
			
		||||
    max: number; // Max value for the scale.
 | 
			
		||||
    isnumeric: boolean; // Whether is a numeric scale.
 | 
			
		||||
    items?: { // Scale items. Only returned for not numerical scales.
 | 
			
		||||
        value: number; // Scale value/option id.
 | 
			
		||||
        name: string; // Scale name.
 | 
			
		||||
    }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure of items in the rating info.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingInfoItem = {
 | 
			
		||||
    itemid: number; // Item id.
 | 
			
		||||
    scaleid?: number; // Scale id.
 | 
			
		||||
    scale?: CoreRatingScale; // Added for rendering purposes.
 | 
			
		||||
    userid?: number; // User who rated id.
 | 
			
		||||
    aggregate?: number; // Aggregated ratings grade.
 | 
			
		||||
    aggregatestr?: string; // Aggregated ratings as string.
 | 
			
		||||
    aggregatelabel?: string; // The aggregation label.
 | 
			
		||||
    count?: number; // Ratings count (used when aggregating).
 | 
			
		||||
    rating?: number; // The rating the user gave.
 | 
			
		||||
    canrate?: boolean; // Whether the user can rate the item.
 | 
			
		||||
    canviewaggregate?: boolean; // Whether the user can view the aggregated grade.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure of a rating returned by the item ratings web service.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingItemRating = {
 | 
			
		||||
    id: number; // Rating id.
 | 
			
		||||
    userid: number; // User id.
 | 
			
		||||
    userpictureurl: string; // URL user picture.
 | 
			
		||||
    userfullname: string; // User fullname.
 | 
			
		||||
    rating: string; // Rating on scale.
 | 
			
		||||
    timemodified: number; // Time modified (timestamp).
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_rating_get_item_ratings WS.
 | 
			
		||||
 */
 | 
			
		||||
type CoreRatingGetItemRatingsWSParams = {
 | 
			
		||||
    contextlevel: ContextLevel; // Context level: course, module, user, etc...
 | 
			
		||||
    instanceid: number; // The instance id of item associated with the context level.
 | 
			
		||||
    component: string; // Component.
 | 
			
		||||
    ratingarea: string; // Rating area.
 | 
			
		||||
    itemid: number; // Associated id.
 | 
			
		||||
    scaleid: number; // Scale id.
 | 
			
		||||
    sort: string; // Sort order (firstname, rating or timemodified).
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by core_rating_get_item_ratings WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingGetItemRatingsWSResponse = {
 | 
			
		||||
    ratings: CoreRatingItemRating[];
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_rating_add_rating WS.
 | 
			
		||||
 */
 | 
			
		||||
type CoreRatingAddRatingWSParams = {
 | 
			
		||||
    contextlevel: ContextLevel; // Context level: course, module, user, etc...
 | 
			
		||||
    instanceid: number; // The instance id of item associated with the context level.
 | 
			
		||||
    component: string; // Component.
 | 
			
		||||
    ratingarea: string; // Rating area.
 | 
			
		||||
    itemid: number; // Associated id.
 | 
			
		||||
    scaleid: number; // Scale id.
 | 
			
		||||
    rating: number; // User rating.
 | 
			
		||||
    rateduserid: number; // Rated user id.
 | 
			
		||||
    aggregation?: number; // Agreggation method.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by core_rating_add_rating WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingAddRatingWSResponse = {
 | 
			
		||||
    success: boolean; // Whether the rate was successfully created.
 | 
			
		||||
    aggregate?: string; // New aggregate.
 | 
			
		||||
    count?: number; // Ratings count.
 | 
			
		||||
    itemid?: number; // Rating item id.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data sent by AGGREGATE_CHANGED_EVENT event.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingAggregateChangedEventData = {
 | 
			
		||||
    contextLevel: ContextLevel;
 | 
			
		||||
    instanceId: number;
 | 
			
		||||
    component: string;
 | 
			
		||||
    ratingArea: string;
 | 
			
		||||
    itemId: number;
 | 
			
		||||
    aggregate?: string;
 | 
			
		||||
    count?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data sent by RATING_SAVED_EVENT event.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRatingSavedEventData = {
 | 
			
		||||
    component: string;
 | 
			
		||||
    ratingArea: string;
 | 
			
		||||
    contextLevel: ContextLevel;
 | 
			
		||||
    instanceId: number;
 | 
			
		||||
    itemSetId: number;
 | 
			
		||||
    itemId: number;
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user