commit
6b8bfbb27b
|
@ -40,7 +40,10 @@
|
|||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"optimization": {
|
||||
"scripts": false,
|
||||
"styles": true
|
||||
},
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
|
@ -52,8 +55,8 @@
|
|||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "50mb",
|
||||
"maximumError": "100mb"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { AddonBadgesMyBadgesLinkHandler } from './services/handlers/mybadges-link';
|
||||
|
@ -23,6 +23,11 @@ import { AddonBadgesUserHandler } from './services/handlers/user';
|
|||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||
import { AddonBadgesPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonBadgesProvider } from './services/badges';
|
||||
|
||||
export const ADDON_BADGES_SERVICES: Type<unknown>[] = [
|
||||
AddonBadgesProvider,
|
||||
];
|
||||
|
||||
const mainMenuRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -101,7 +101,7 @@ export class AddonBlockActivityModulesComponent extends CoreBlockBaseComponent i
|
|||
if (modName === 'resources') {
|
||||
icon = CoreCourse.getModuleIconSrc('page', modIcons['page']);
|
||||
} else {
|
||||
icon = CoreCourseModuleDelegate.getModuleIconSrc(modName, modIcons[modName]);
|
||||
icon = CoreCourseModuleDelegate.getModuleIconSrc(modName, modIcons[modName]) || '';
|
||||
}
|
||||
|
||||
this.entries.push({
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
|
||||
|
||||
|
@ -27,8 +27,18 @@ import { CORE_SITE_SCHEMAS } from '@services/sites';
|
|||
import { CALENDAR_SITE_SCHEMA } from './services/database/calendar';
|
||||
import { CALENDAR_OFFLINE_SITE_SCHEMA } from './services/database/calendar-offline';
|
||||
import { AddonCalendarComponentsModule } from './components/components.module';
|
||||
import { AddonCalendar } from './services/calendar';
|
||||
import { AddonCalendar, AddonCalendarProvider } from './services/calendar';
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { AddonCalendarOfflineProvider } from './services/calendar-offline';
|
||||
import { AddonCalendarHelperProvider } from './services/calendar-helper';
|
||||
import { AddonCalendarSyncProvider } from './services/calendar-sync';
|
||||
|
||||
export const ADDON_CALENDAR_SERVICES: Type<unknown>[] = [
|
||||
AddonCalendarProvider,
|
||||
AddonCalendarOfflineProvider,
|
||||
AddonCalendarHelperProvider,
|
||||
AddonCalendarSyncProvider,
|
||||
];
|
||||
|
||||
const mainMenuChildrenRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
<form [formGroup]="form" *ngIf="!error" #editEventForm>
|
||||
<!-- Event name. -->
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label position="stacked"><h2 [core-mark-required]="true">
|
||||
<ion-label position="stacked">
|
||||
<h2 [core-mark-required]="true">
|
||||
{{ 'addon.calendar.eventname' | translate }}
|
||||
</h2>
|
||||
</ion-label>
|
||||
|
|
|
@ -43,6 +43,7 @@ import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
|||
import { AddonCalendarOfflineEventDBRecord } from '../../services/database/calendar-offline';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
|
||||
/**
|
||||
* Page that displays a form to create/edit an event.
|
||||
|
@ -52,7 +53,7 @@ import { CoreNavigator } from '@services/navigator';
|
|||
templateUrl: 'edit-event.html',
|
||||
styleUrls: ['edit-event.scss'],
|
||||
})
|
||||
export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||
export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||
|
||||
@ViewChild(CoreEditorRichTextEditorComponent) descriptionEditor!: CoreEditorRichTextEditorComponent;
|
||||
@ViewChild('editEventForm') formElement!: ElementRef;
|
||||
|
@ -605,15 +606,17 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
/**
|
||||
* Check if we can leave the page or not.
|
||||
*
|
||||
* @return Resolved if we can leave it, rejected if not.
|
||||
* @return Resolved with true if we can leave it, rejected if not.
|
||||
*/
|
||||
async ionViewCanLeave(): Promise<void> {
|
||||
async canLeave(): Promise<boolean> {
|
||||
if (AddonCalendarHelper.hasEventDataChanged(this.form.value, this.originalData)) {
|
||||
// Show confirmation if some data has been modified.
|
||||
await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit'));
|
||||
}
|
||||
|
||||
CoreDomUtils.triggerFormCancelledEvent(this.formElement, this.currentSite.getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,9 +12,14 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
|
||||
import { AddonMessageOutputAirnotifierModule } from './airnotifier/airnotifier.module';
|
||||
import { AddonMessageOutputDelegateService } from './services/messageoutput-delegate';
|
||||
|
||||
export const ADDON_MESSAGEOUTPUT_SERVICES: Type<unknown>[] = [
|
||||
AddonMessageOutputDelegateService,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
|
||||
|
@ -33,9 +33,17 @@ import { AddonMessagesPushClickHandler } from './services/handlers/push-click';
|
|||
import { CoreUserDelegate } from '@features/user/services/user-delegate';
|
||||
import { AddonMessagesSendMessageUserHandler } from './services/handlers/user-send-message';
|
||||
import { Network, NgZone } from '@singletons';
|
||||
import { AddonMessagesSync } from './services/messages-sync';
|
||||
import { AddonMessagesSync, AddonMessagesSyncProvider } from './services/messages-sync';
|
||||
import { AddonMessagesSyncCronHandler } from './services/handlers/sync-cron';
|
||||
import { CoreSitePreferencesRoutingModule } from '@features/settings/pages/site/site-routing';
|
||||
import { AddonMessagesProvider } from './services/messages';
|
||||
import { AddonMessagesOfflineProvider } from './services/messages-offline';
|
||||
|
||||
export const ADDON_MESSAGES_SERVICES: Type<unknown>[] = [
|
||||
AddonMessagesProvider,
|
||||
AddonMessagesOfflineProvider,
|
||||
AddonMessagesSyncProvider,
|
||||
];
|
||||
|
||||
const mainMenuChildrenRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -23,15 +23,30 @@ import { CoreCronDelegate } from '@services/cron';
|
|||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { AddonModAssignComponentsModule } from './components/components.module';
|
||||
import { AddonModAssignFeedbackModule } from './feedback/feedback.module';
|
||||
import { AddonModAssignProvider } from './services/assign';
|
||||
import { AddonModAssignHelperProvider } from './services/assign-helper';
|
||||
import { AddonModAssignOfflineProvider } from './services/assign-offline';
|
||||
import { AddonModAssignSyncProvider } from './services/assign-sync';
|
||||
import { OFFLINE_SITE_SCHEMA } from './services/database/assign';
|
||||
import { AddonModAssignFeedbackDelegateService } from './services/feedback-delegate';
|
||||
import { AddonModAssignIndexLinkHandler } from './services/handlers/index-link';
|
||||
import { AddonModAssignListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModAssignModuleHandler, AddonModAssignModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModAssignPrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModAssignPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonModAssignSyncCronHandler } from './services/handlers/sync-cron';
|
||||
import { AddonModAssignSubmissionDelegateService } from './services/submission-delegate';
|
||||
import { AddonModAssignSubmissionModule } from './submission/submission.module';
|
||||
|
||||
export const ADDON_MOD_ASSIGN_SERVICES: Type<unknown>[] = [
|
||||
AddonModAssignProvider,
|
||||
AddonModAssignOfflineProvider,
|
||||
AddonModAssignSyncProvider,
|
||||
AddonModAssignHelperProvider,
|
||||
AddonModAssignFeedbackDelegateService,
|
||||
AddonModAssignSubmissionDelegateService,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: AddonModAssignModuleHandlerService.PAGE_NAME,
|
||||
|
|
|
@ -56,6 +56,7 @@ import { CoreGroups } from '@services/groups';
|
|||
import { CoreSync } from '@services/sync';
|
||||
import { AddonModAssignSubmissionPluginComponent } from '../submission-plugin/submission-plugin';
|
||||
import { AddonModAssignModuleHandlerService } from '../../services/handlers/module';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
|
||||
/**
|
||||
* Component that displays an assignment submission.
|
||||
|
@ -65,7 +66,7 @@ import { AddonModAssignModuleHandlerService } from '../../services/handlers/modu
|
|||
templateUrl: 'addon-mod-assign-submission.html',
|
||||
styleUrls: ['submission.scss'],
|
||||
})
|
||||
export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||
export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, CanLeave {
|
||||
|
||||
@ViewChild(CoreTabsComponent) tabs!: CoreTabsComponent;
|
||||
@ViewChildren(AddonModAssignSubmissionPluginComponent) submissionComponents!:
|
||||
|
@ -252,21 +253,20 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
/**
|
||||
* Check if the user can leave the view. If there are changes to be saved, it will ask for confirm.
|
||||
*
|
||||
* @return Promise resolved if can leave the view, rejected otherwise.
|
||||
* @return Promise resolved with true if can leave the view, rejected otherwise.
|
||||
*/
|
||||
async canLeave(): Promise<void> {
|
||||
async canLeave(): Promise<boolean> {
|
||||
// Check if there is data to save.
|
||||
const modified = await this.hasDataToSave();
|
||||
|
||||
if (modified) {
|
||||
// Modified, confirm user wants to go back.
|
||||
try {
|
||||
await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit'));
|
||||
|
||||
await this.discardDrafts();
|
||||
} catch {
|
||||
// Cancelled by the user.
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/co
|
|||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
|
||||
import { CoreSync } from '@services/sync';
|
||||
|
@ -44,7 +45,7 @@ import { AddonModAssignSync } from '../../services/assign-sync';
|
|||
selector: 'page-addon-mod-assign-edit',
|
||||
templateUrl: 'edit.html',
|
||||
})
|
||||
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||
export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave {
|
||||
|
||||
@ViewChild('editSubmissionForm') formElement?: ElementRef;
|
||||
|
||||
|
@ -92,9 +93,9 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @return Resolved if we can leave it, rejected if not.
|
||||
*/
|
||||
async ionViewCanLeave(): Promise<void> {
|
||||
async canLeave(): Promise<boolean> {
|
||||
if (this.forceLeave) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if data has changed.
|
||||
|
@ -107,6 +108,8 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
|||
AddonModAssignHelper.clearSubmissionPluginTmpData(this.assign!, this.userSubmission, this.getInputData());
|
||||
|
||||
CoreDomUtils.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreScreen } from '@services/screen';
|
||||
|
@ -29,7 +30,7 @@ import { AddonModAssign, AddonModAssignAssign } from '../../services/assign';
|
|||
selector: 'page-addon-mod-assign-submission-review',
|
||||
templateUrl: 'submission-review.html',
|
||||
})
|
||||
export class AddonModAssignSubmissionReviewPage implements OnInit {
|
||||
export class AddonModAssignSubmissionReviewPage implements OnInit, CanLeave {
|
||||
|
||||
@ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent;
|
||||
|
||||
|
@ -71,7 +72,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit {
|
|||
*
|
||||
* @return Resolved if we can leave it, rejected if not.
|
||||
*/
|
||||
ionViewCanLeave(): boolean | Promise<void> {
|
||||
async canLeave(): Promise<boolean> {
|
||||
if (!this.submissionComponent || this.forceLeave) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -25,6 +25,11 @@ import { AddonModBookIndexLinkHandler } from './services/handlers/index-link';
|
|||
import { AddonModBookListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModBookPrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModBookTagAreaHandler } from './services/handlers/tag-area';
|
||||
import { AddonModBookProvider } from './services/book';
|
||||
|
||||
export const ADDON_MOD_BOOK_SERVICES: Type<unknown>[] = [
|
||||
AddonModBookProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -20,12 +20,19 @@ import { CoreCourseModulePrefetchDelegate } from '@features/course/services/modu
|
|||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { CorePluginFileDelegate } from '@services/plugin-file-delegate';
|
||||
import { AddonModFolderComponentsModule } from './components/components.module';
|
||||
import { AddonModFolderProvider } from './services/folder';
|
||||
import { AddonModFolderHelperProvider } from './services/folder-helper';
|
||||
import { AddonModFolderIndexLinkHandler } from './services/handlers/index-link';
|
||||
import { AddonModFolderListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModFolderModuleHandler, AddonModFolderModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModFolderPluginFileHandler } from './services/handlers/pluginfile';
|
||||
import { AddonModFolderPrefetchHandler } from './services/handlers/prefetch';
|
||||
|
||||
export const ADDON_MOD_FOLDER_SERVICES: Type<unknown>[] = [
|
||||
AddonModFolderProvider,
|
||||
AddonModFolderHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: AddonModFolderModuleHandlerService.PAGE_NAME,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { conditionalRoutes } from '@/app/app-routing.module';
|
||||
|
@ -38,6 +38,17 @@ import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate';
|
|||
import { AddonModForumTagAreaHandler } from './services/handlers/tag-area';
|
||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||
import { AddonModForumPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonModForumProvider } from './services/forum';
|
||||
import { AddonModForumOfflineProvider } from './services/offline';
|
||||
import { AddonModForumHelperProvider } from './services/helper';
|
||||
import { AddonModForumSyncProvider } from './services/sync';
|
||||
|
||||
export const ADDON_MOD_FORUM_SERVICES: Type<unknown>[] = [
|
||||
AddonModForumProvider,
|
||||
AddonModForumOfflineProvider,
|
||||
AddonModForumHelperProvider,
|
||||
AddonModForumSyncProvider,
|
||||
];
|
||||
|
||||
const mainMenuRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -25,6 +25,11 @@ import { AddonModImscpListLinkHandler } from './services/handlers/list-link';
|
|||
import { AddonModImscpModuleHandler, AddonModImscpModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModImscpPluginFileHandler } from './services/handlers/pluginfile';
|
||||
import { AddonModImscpPrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModImscpProvider } from './services/imscp';
|
||||
|
||||
export const ADDON_MOD_IMSCP_SERVICES: Type<unknown>[] = [
|
||||
AddonModImscpProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -32,6 +32,17 @@ import { AddonModLessonPrefetchHandler } from './services/handlers/prefetch';
|
|||
import { AddonModLessonPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonModLessonReportLinkHandler } from './services/handlers/report-link';
|
||||
import { AddonModLessonSyncCronHandler } from './services/handlers/sync-cron';
|
||||
import { AddonModLessonProvider } from './services/lesson';
|
||||
import { AddonModLessonHelperProvider } from './services/lesson-helper';
|
||||
import { AddonModLessonOfflineProvider } from './services/lesson-offline';
|
||||
import { AddonModLessonSyncProvider } from './services/lesson-sync';
|
||||
|
||||
export const ADDON_MOD_LESSON_SERVICES: Type<unknown>[] = [
|
||||
AddonModLessonProvider,
|
||||
AddonModLessonOfflineProvider,
|
||||
AddonModLessonSyncProvider,
|
||||
AddonModLessonHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -25,6 +25,13 @@ import { AddonModPageListLinkHandler } from './services/handlers/list-link';
|
|||
import { AddonModPageModuleHandler, AddonModPageModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModPagePluginFileHandler } from './services/handlers/pluginfile';
|
||||
import { AddonModPagePrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModPageProvider } from './services/page';
|
||||
import { AddonModPageHelperProvider } from './services/page-helper';
|
||||
|
||||
export const ADDON_MOD_PAGE_SERVICES: Type<unknown>[] = [
|
||||
AddonModPageProvider,
|
||||
AddonModPageHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@ import {
|
|||
} from '../../services/quiz';
|
||||
import { AddonModQuizAttempt, AddonModQuizHelper } from '../../services/quiz-helper';
|
||||
import { AddonModQuizSync } from '../../services/quiz-sync';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
|
||||
/**
|
||||
* Page that allows attempting a quiz.
|
||||
|
@ -53,7 +54,7 @@ import { AddonModQuizSync } from '../../services/quiz-sync';
|
|||
templateUrl: 'player.html',
|
||||
styleUrls: ['player.scss'],
|
||||
})
|
||||
export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
|
||||
export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||
|
||||
@ViewChild(IonContent) content?: IonContent;
|
||||
@ViewChildren(CoreQuestionComponent) questionComponents?: QueryList<CoreQuestionComponent>;
|
||||
|
@ -144,9 +145,9 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @return Resolved if we can leave it, rejected if not.
|
||||
*/
|
||||
async ionViewCanLeave(): Promise<void> {
|
||||
async canLeave(): Promise<boolean> {
|
||||
if (this.forceLeave || this.quizAborted || !this.questions.length || this.showSummary) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save answers.
|
||||
|
@ -164,6 +165,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
|
|||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
|
||||
|
@ -24,6 +24,7 @@ import { CoreCronDelegate } from '@services/cron';
|
|||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { AddonModQuizAccessRulesModule } from './accessrules/accessrules.module';
|
||||
import { AddonModQuizComponentsModule } from './components/components.module';
|
||||
import { AddonModQuizAccessRuleDelegateService } from './services/access-rules-delegate';
|
||||
import { SITE_SCHEMA } from './services/database/quiz';
|
||||
import { AddonModQuizGradeLinkHandler } from './services/handlers/grade-link';
|
||||
import { AddonModQuizIndexLinkHandler } from './services/handlers/index-link';
|
||||
|
@ -33,6 +34,18 @@ import { AddonModQuizPrefetchHandler } from './services/handlers/prefetch';
|
|||
import { AddonModQuizPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonModQuizReviewLinkHandler } from './services/handlers/review-link';
|
||||
import { AddonModQuizSyncCronHandler } from './services/handlers/sync-cron';
|
||||
import { AddonModQuizProvider } from './services/quiz';
|
||||
import { AddonModQuizHelperProvider } from './services/quiz-helper';
|
||||
import { AddonModQuizOfflineProvider } from './services/quiz-offline';
|
||||
import { AddonModQuizSyncProvider } from './services/quiz-sync';
|
||||
|
||||
export const ADDON_MOD_QUIZ_SERVICES: Type<unknown>[] = [
|
||||
AddonModQuizAccessRuleDelegateService,
|
||||
AddonModQuizProvider,
|
||||
AddonModQuizOfflineProvider,
|
||||
AddonModQuizHelperProvider,
|
||||
AddonModQuizSyncProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -70,7 +70,7 @@ export interface AddonModQuizAccessRuleHandler extends CoreDelegateHandler {
|
|||
*
|
||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||
*/
|
||||
getPreflightComponent?(): Type<unknown> | Promise<Type<unknown>>;
|
||||
getPreflightComponent?(): undefined | Type<unknown> | Promise<Type<unknown>>;
|
||||
|
||||
/**
|
||||
* Function called when the preflight check has passed. This is a chance to record that fact in some way.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -25,6 +25,11 @@ import { AddonModResourceListLinkHandler } from './services/handlers/list-link';
|
|||
import { AddonModResourceModuleHandlerService, AddonModResourceModuleHandler } from './services/handlers/module';
|
||||
import { AddonModResourcePluginFileHandler } from './services/handlers/pluginfile';
|
||||
import { AddonModResourcePrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModResourceProvider } from './services/resource';
|
||||
|
||||
export const ADDON_MOD_RESOURCE_SERVICES: Type<unknown>[] = [
|
||||
AddonModResourceProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
|
@ -23,6 +23,13 @@ import { AddonModUrlIndexLinkHandler } from './services/handlers/index-link';
|
|||
import { AddonModUrlListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModUrlModuleHandler, AddonModUrlModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModUrlPrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModUrlProvider } from './services/url';
|
||||
import { AddonModUrlHelperProvider } from './services/url-helper';
|
||||
|
||||
export const ADDON_MOD_URL_SERVICES: Type<unknown>[] = [
|
||||
AddonModUrlProvider,
|
||||
AddonModUrlHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { CoreCronDelegate } from '@services/cron';
|
||||
|
@ -26,6 +26,13 @@ import { AddonNotificationsCronHandler } from './services/handlers/cron';
|
|||
import { AddonNotificationsPushClickHandler } from './services/handlers/push-click';
|
||||
import { AddonNotificationsSettingsHandler, AddonNotificationsSettingsHandlerService } from './services/handlers/settings';
|
||||
import { CoreSitePreferencesRoutingModule } from '@features/settings/pages/site/site-routing';
|
||||
import { AddonNotificationsProvider } from './services/notifications';
|
||||
import { AddonNotificationsHelperProvider } from './services/notifications-helper';
|
||||
|
||||
export const ADDON_NOTIFICATIONS_SERVICES: Type<unknown>[] = [
|
||||
AddonNotificationsProvider,
|
||||
AddonNotificationsHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,13 +12,20 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
|
||||
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
|
||||
import { AddonPrivateFilesMainMenuHandler, AddonPrivateFilesMainMenuHandlerService } from './services/handlers/mainmenu';
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { AddonPrivateFilesProvider } from './services/privatefiles';
|
||||
import { AddonPrivateFilesHelperProvider } from './services/privatefiles-helper';
|
||||
|
||||
export const ADDON_PRIVATEFILES_SERVICES: Type<unknown>[] = [
|
||||
AddonPrivateFilesProvider,
|
||||
AddonPrivateFilesHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -32,6 +32,9 @@ export class AddonQbehaviourDeferredCBMComponent {
|
|||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Input() contextLevel?: string; // The context level.
|
||||
@Input() contextInstanceId?: number; // The instance ID related to the context.
|
||||
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
|
||||
@Input() review?: boolean; // Whether the user is in review mode.
|
||||
@Input() preferredBehaviour?: string; // Preferred behaviour.
|
||||
@Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
|
||||
@Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ export class AddonQbehaviourInformationItemComponent {
|
|||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Input() contextLevel?: string; // The context level.
|
||||
@Input() contextInstanceId?: number; // The instance ID related to the context.
|
||||
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
|
||||
@Input() review?: boolean; // Whether the user is in review mode.
|
||||
@Input() preferredBehaviour?: string; // Preferred behaviour.
|
||||
@Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
|
||||
@Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { COMPILER_OPTIONS, NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouteReuseStrategy } from '@angular/router';
|
||||
|
@ -28,6 +28,7 @@ import { AddonsModule } from '@/addons/addons.module';
|
|||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
|
||||
|
||||
// For translate loader. AoT requires an exported function for factories.
|
||||
export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
||||
|
@ -55,6 +56,8 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
|||
],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
|
||||
{ provide: JitCompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
AfterViewInit,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
SimpleChange,
|
||||
} from '@angular/core';
|
||||
import { IonSlides } from '@ionic/angular';
|
||||
import { BackButtonEvent } from '@ionic/core';
|
||||
|
@ -153,7 +154,8 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
ngOnChanges(changes: Record<string, SimpleChange>): void {
|
||||
// Wait for ngAfterViewInit so it works in the case that each tab has its own component.
|
||||
if (!this.initialized && this.hideUntil && this.afterViewInitTriggered) {
|
||||
// Tabs should be shown, initialize them.
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
AfterViewInit,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
SimpleChange,
|
||||
} from '@angular/core';
|
||||
import { IonTabs } from '@ionic/angular';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
@ -70,17 +71,6 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
|||
super(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
super.ngOnInit();
|
||||
|
||||
this.tabs.forEach((tab) => {
|
||||
this.initTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Init tab info.
|
||||
*
|
||||
|
@ -117,12 +107,16 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
|||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(): void {
|
||||
ngOnChanges(changes: Record<string, SimpleChange>): void {
|
||||
if (changes.tabs) {
|
||||
this.tabs.forEach((tab) => {
|
||||
this.initTab(tab);
|
||||
});
|
||||
|
||||
super.ngOnChanges();
|
||||
this.calculateSlides();
|
||||
}
|
||||
|
||||
super.ngOnChanges(changes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { ApplicationInitStatus, Injector, NgModule } from '@angular/core';
|
||||
import { ApplicationInitStatus, Injector, NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CoreApplicationInitStatus } from './classes/application-init-status';
|
||||
import { CoreFeaturesModule } from './features/features.module';
|
||||
|
@ -21,6 +21,63 @@ import { CoreInterceptor } from './classes/interceptor';
|
|||
import { getDatabaseProviders } from './services/database';
|
||||
import { getInitializerProviders } from './initializers';
|
||||
|
||||
import { CoreDbProvider } from '@services/db';
|
||||
import { CoreAppProvider } from '@services/app';
|
||||
import { CoreConfigProvider } from '@services/config';
|
||||
import { CoreLangProvider } from '@services/lang';
|
||||
import { CoreTextUtilsProvider } from '@services/utils/text';
|
||||
import { CoreDomUtilsProvider } from '@services/utils/dom';
|
||||
import { CoreIframeUtilsProvider } from '@services/utils/iframe';
|
||||
import { CoreTimeUtilsProvider } from '@services/utils/time';
|
||||
import { CoreUrlUtilsProvider } from '@services/utils/url';
|
||||
import { CoreUtilsProvider } from '@services/utils/utils';
|
||||
import { CoreMimetypeUtilsProvider } from '@services/utils/mimetype';
|
||||
import { CoreFileProvider } from '@services/file';
|
||||
import { CoreWSProvider } from '@services/ws';
|
||||
import { CoreSitesProvider } from '@services/sites';
|
||||
import { CoreLocalNotificationsProvider } from '@services/local-notifications';
|
||||
import { CoreGroupsProvider } from '@services/groups';
|
||||
import { CoreCronDelegateService } from '@services/cron';
|
||||
import { CoreFileSessionProvider } from '@services/file-session';
|
||||
import { CoreFilepoolProvider } from '@services/filepool';
|
||||
import { CoreUpdateManagerProvider } from '@services/update-manager';
|
||||
import { CorePluginFileDelegateService } from '@services/plugin-file-delegate';
|
||||
import { CoreSyncProvider } from '@services/sync';
|
||||
import { CoreFileHelperProvider } from '@services/file-helper';
|
||||
import { CoreGeolocationProvider } from '@services/geolocation';
|
||||
import { CoreNavigatorService } from '@services/navigator';
|
||||
import { CoreScreenService } from '@services/screen';
|
||||
|
||||
export const CORE_SERVICES: Type<unknown>[] = [
|
||||
CoreAppProvider,
|
||||
CoreConfigProvider,
|
||||
CoreCronDelegateService,
|
||||
// @todo CoreCustomURLSchemesProvider,
|
||||
CoreDbProvider,
|
||||
CoreFileHelperProvider,
|
||||
CoreFileSessionProvider,
|
||||
CoreFileProvider,
|
||||
CoreFilepoolProvider,
|
||||
CoreGeolocationProvider,
|
||||
CoreGroupsProvider,
|
||||
CoreLangProvider,
|
||||
CoreLocalNotificationsProvider,
|
||||
CoreNavigatorService,
|
||||
CorePluginFileDelegateService,
|
||||
CoreScreenService,
|
||||
CoreSitesProvider,
|
||||
CoreSyncProvider,
|
||||
CoreUpdateManagerProvider,
|
||||
CoreDomUtilsProvider,
|
||||
CoreIframeUtilsProvider,
|
||||
CoreMimetypeUtilsProvider,
|
||||
CoreTextUtilsProvider,
|
||||
CoreTimeUtilsProvider,
|
||||
CoreUrlUtilsProvider,
|
||||
CoreUtilsProvider,
|
||||
CoreWSProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreFeaturesModule,
|
||||
|
|
|
@ -12,13 +12,17 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreBlockDefaultHandler } from './services/handlers/default-block';
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CoreBlockDelegateService } from './services/block-delegate';
|
||||
import { CoreBlockHelperProvider } from './services/block-helper';
|
||||
|
||||
export const CORE_BLOCK_SERVICES: Type<unknown>[] = [
|
||||
CoreBlockDelegateService,
|
||||
CoreBlockHelperProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
CoreBlockDefaultHandler,
|
||||
],
|
||||
providers: [],
|
||||
})
|
||||
export class CoreBlockModule {
|
||||
}
|
||||
export class CoreBlockModule {}
|
||||
|
|
|
@ -18,7 +18,7 @@ import { CoreBlockBaseHandler } from '../../classes/base-block-handler';
|
|||
/**
|
||||
* Default handler used when a block type doesn't have a specific implementation.
|
||||
*/
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreBlockDefaultHandler extends CoreBlockBaseHandler {
|
||||
|
||||
name = 'CoreBlockDefault';
|
||||
|
|
|
@ -12,16 +12,24 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { CoreCronDelegate } from '@services/cron';
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { CoreCommentsComponentsModule } from './components/components.module';
|
||||
import { CoreComments } from './services/comments';
|
||||
import { CoreComments, CoreCommentsProvider } from './services/comments';
|
||||
import { CoreCommentsOfflineProvider } from './services/comments-offline';
|
||||
import { CoreCommentsSyncProvider } from './services/comments-sync';
|
||||
import { COMMENTS_OFFLINE_SITE_SCHEMA } from './services/database/comments';
|
||||
import { CoreCommentsSyncCronHandler } from './services/handlers/sync-cron';
|
||||
|
||||
export const CORE_COMMENTS_SERVICES: Type<unknown>[] = [
|
||||
CoreCommentsOfflineProvider,
|
||||
CoreCommentsSyncProvider,
|
||||
CoreCommentsProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'comments',
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// (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 { CoreCompileHtmlComponent } from './compile-html';
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCompileHtmlComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreSharedModule,
|
||||
],
|
||||
exports: [
|
||||
CoreCompileHtmlComponent,
|
||||
],
|
||||
})
|
||||
export class CoreCompileHtmlComponentModule {}
|
|
@ -0,0 +1,291 @@
|
|||
// (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 {
|
||||
Component,
|
||||
Input,
|
||||
OnInit,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
ViewChild,
|
||||
ComponentRef,
|
||||
SimpleChange,
|
||||
ChangeDetectorRef,
|
||||
ElementRef,
|
||||
Output,
|
||||
EventEmitter,
|
||||
DoCheck,
|
||||
KeyValueDiffers,
|
||||
AfterContentInit,
|
||||
AfterViewInit,
|
||||
Type,
|
||||
KeyValueDiffer,
|
||||
} from '@angular/core';
|
||||
|
||||
import { CoreCompile } from '@features/compile/services/compile';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
|
||||
/**
|
||||
* This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its
|
||||
* components and directives are instantiated.
|
||||
*
|
||||
* IMPORTANT: Use this component only if it is a must. It will create and compile a new component and module everytime this
|
||||
* component is used, so it can slow down the app.
|
||||
*
|
||||
* This component has its own module to prevent circular dependencies. If you want to use it,
|
||||
* you need to import CoreCompileHtmlComponentModule.
|
||||
*
|
||||
* You can provide some Javascript code (as text) to be executed inside the component. The context of the javascript code (this)
|
||||
* will be the component instance created to compile the template. This means your javascript code can interact with the template.
|
||||
* The component instance will have most of the providers so you can use them in the javascript code. E.g. if you want to use
|
||||
* CoreAppProvider, you can do it with "this.CoreAppProvider".
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-compile-html',
|
||||
template: '<core-loading [hideUntil]="loaded"><ng-container #dynamicComponent></ng-container></core-loading>',
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/no-conflicting-lifecycle
|
||||
export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||
|
||||
@Input() text!: string; // The HTML text to display.
|
||||
@Input() javascript?: string; // The Javascript to execute in the component.
|
||||
@Input() jsData?: Record<string, unknown>; // Data to pass to the fake component.
|
||||
@Input() extraImports: unknown[] = []; // Extra import modules.
|
||||
@Input() extraProviders: Type<unknown>[] = []; // Extra providers.
|
||||
@Input() forceCompile?: boolean; // Set it to true to force compile even if the text/javascript hasn't changed.
|
||||
@Output() created = new EventEmitter<unknown>(); // Will emit an event when the component is instantiated.
|
||||
@Output() compiling = new EventEmitter<boolean>(); // Event that indicates whether the template is being compiled.
|
||||
|
||||
// Get the container where to put the content.
|
||||
@ViewChild('dynamicComponent', { read: ViewContainerRef }) container?: ViewContainerRef;
|
||||
|
||||
loaded?: boolean;
|
||||
componentInstance?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
protected componentRef?: ComponentRef<unknown>;
|
||||
protected element: HTMLElement;
|
||||
protected differ: KeyValueDiffer<unknown, unknown>; // To detect changes in the jsData input.
|
||||
protected creatingComponent = false;
|
||||
protected pendingCalls = {};
|
||||
|
||||
constructor(
|
||||
protected changeDetector: ChangeDetectorRef,
|
||||
element: ElementRef,
|
||||
differs: KeyValueDiffers,
|
||||
) {
|
||||
this.element = element.nativeElement;
|
||||
this.differ = differs.find([]).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
|
||||
*/
|
||||
// eslint-disable-next-line @angular-eslint/no-conflicting-lifecycle
|
||||
ngDoCheck(): void {
|
||||
if (!this.componentInstance || this.creatingComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's any change in the jsData object.
|
||||
const changes = this.differ.diff(this.jsData || {});
|
||||
if (changes) {
|
||||
this.setInputData();
|
||||
|
||||
if (this.componentInstance.ngOnChanges) {
|
||||
this.componentInstance.ngOnChanges(CoreDomUtils.createChangesFromKeyValueDiff(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
// eslint-disable-next-line @angular-eslint/no-conflicting-lifecycle
|
||||
async ngOnChanges(changes: Record<string, SimpleChange>): Promise<void> {
|
||||
// Only compile if text/javascript has changed or the forceCompile flag has been set to true.
|
||||
if (this.text && (changes.text || changes.javascript ||
|
||||
(changes.forceCompile && CoreUtils.isTrueOrOne(this.forceCompile)))) {
|
||||
|
||||
// Create a new component and a new module.
|
||||
this.creatingComponent = true;
|
||||
this.compiling.emit(true);
|
||||
|
||||
try {
|
||||
const factory = await CoreCompile.createAndCompileComponent(
|
||||
this.text,
|
||||
this.getComponentClass(),
|
||||
this.extraImports,
|
||||
);
|
||||
|
||||
// Destroy previous components.
|
||||
this.componentRef?.destroy();
|
||||
|
||||
if (factory) {
|
||||
// Create the component.
|
||||
this.componentRef = this.container?.createComponent(factory);
|
||||
this.componentRef && this.created.emit(this.componentRef.instance);
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModal(error);
|
||||
|
||||
this.loaded = true;
|
||||
} finally {
|
||||
this.creatingComponent = false;
|
||||
this.compiling.emit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
// eslint-disable-next-line @angular-eslint/no-conflicting-lifecycle
|
||||
ngOnDestroy(): void {
|
||||
this.componentRef?.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a class that defines the dynamic component.
|
||||
*
|
||||
* @return The component class.
|
||||
*/
|
||||
protected getComponentClass(): Type<unknown> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const compileInstance = this;
|
||||
|
||||
// Create the component, using the text as the template.
|
||||
return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
|
||||
|
||||
constructor() {
|
||||
// Store this instance so it can be accessed by the outer component.
|
||||
compileInstance.componentInstance = this;
|
||||
|
||||
// Create 2 empty properties that can be used by the template to store data.
|
||||
this['dataObject'] = {};
|
||||
this['dataArray'] = [];
|
||||
|
||||
// Inject the libraries.
|
||||
CoreCompile.injectLibraries(this, compileInstance.extraProviders);
|
||||
|
||||
// Always add these elements, they could be needed on component init (componentObservable).
|
||||
this['ChangeDetectorRef'] = compileInstance.changeDetector;
|
||||
this['componentContainer'] = compileInstance.element;
|
||||
|
||||
// Add the data passed to the component.
|
||||
compileInstance.setInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// If there is some javascript to run, do it now.
|
||||
if (compileInstance.javascript) {
|
||||
CoreCompile.executeJavascript(this, compileInstance.javascript);
|
||||
}
|
||||
|
||||
// Call the pending functions.
|
||||
for (const name in compileInstance.pendingCalls) {
|
||||
const pendingCall = compileInstance.pendingCalls[name];
|
||||
|
||||
if (typeof this[name] == 'function') {
|
||||
// Call the function.
|
||||
Promise.resolve(this[name].apply(this, pendingCall.params)).then(pendingCall.defer.resolve)
|
||||
.catch(pendingCall.defer.reject);
|
||||
} else {
|
||||
// Function not defined, resolve the promise.
|
||||
pendingCall.defer.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
compileInstance.pendingCalls = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Content has been initialized.
|
||||
*/
|
||||
ngAfterContentInit(): void {
|
||||
// To be overridden.
|
||||
}
|
||||
|
||||
/**
|
||||
* View has been initialized.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
// To be overridden.
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
// To be overridden.
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JS data as input data of the component instance.
|
||||
*/
|
||||
protected setInputData(): void {
|
||||
if (!this.componentInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const name in this.jsData) {
|
||||
this.componentInstance[name] = this.jsData[name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a certain function on the component instance.
|
||||
*
|
||||
* @param name Name of the function to call.
|
||||
* @param params List of params to send to the function.
|
||||
* @param callWhenCreated If this param is true and the component hasn't been created yet, call the function
|
||||
* once the component has been created.
|
||||
* @return Result of the call. Undefined if no component instance or the function doesn't exist.
|
||||
*/
|
||||
callComponentFunction(name: string, params?: unknown[], callWhenCreated: boolean = true): unknown {
|
||||
if (this.componentInstance) {
|
||||
if (typeof this.componentInstance[name] == 'function') {
|
||||
return this.componentInstance[name].apply(this.componentInstance, params);
|
||||
}
|
||||
} else if (callWhenCreated) {
|
||||
// Call it when the component is created.
|
||||
|
||||
if (this.pendingCalls[name]) {
|
||||
// Call already pending, just update the params (allow only 1 call per function until it's initialized).
|
||||
this.pendingCalls[name].params = params;
|
||||
|
||||
return this.pendingCalls[name].defer.promise;
|
||||
}
|
||||
|
||||
const defer = CoreUtils.promiseDefer();
|
||||
|
||||
this.pendingCalls[name] = {
|
||||
params,
|
||||
defer,
|
||||
};
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {
|
||||
Injectable,
|
||||
Injector,
|
||||
Component,
|
||||
NgModule,
|
||||
Compiler,
|
||||
ComponentFactory,
|
||||
ComponentRef,
|
||||
NgModuleRef,
|
||||
NO_ERRORS_SCHEMA,
|
||||
Type,
|
||||
} from '@angular/core';
|
||||
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
|
||||
import {
|
||||
Platform,
|
||||
ActionSheetController,
|
||||
AlertController,
|
||||
LoadingController,
|
||||
ModalController,
|
||||
PopoverController,
|
||||
ToastController,
|
||||
} from '@ionic/angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
// Import core services.
|
||||
import { CORE_SERVICES } from '@/core/core.module';
|
||||
import { CORE_BLOCK_SERVICES } from '@features/block/block.module';
|
||||
import { CORE_COMMENTS_SERVICES } from '@features/comments/comments.module';
|
||||
import { CORE_CONTENTLINKS_SERVICES } from '@features/contentlinks/contentlinks.module';
|
||||
import { CORE_COURSE_SERVICES } from '@features/course/course.module';
|
||||
import { CORE_COURSES_SERVICES } from '@features/courses/courses.module';
|
||||
import { CORE_EDITOR_SERVICES } from '@features/editor/editor.module';
|
||||
import { IONIC_NATIVE_SERVICES } from '@features/emulator/emulator.module';
|
||||
import { CORE_FILEUPLOADER_SERVICES } from '@features/fileuploader/fileuploader.module';
|
||||
import { CORE_FILTER_SERVICES } from '@features/filter/filter.module';
|
||||
import { CORE_GRADES_SERVICES } from '@features/grades/grades.module';
|
||||
import { CORE_H5P_SERVICES } from '@features/h5p/h5p.module';
|
||||
import { CORE_LOGIN_SERVICES } from '@features/login/login.module';
|
||||
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_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';
|
||||
import { CORE_TAG_SERVICES } from '@features/tag/tag.module';
|
||||
import { CORE_USER_SERVICES } from '@features/user/user.module';
|
||||
import { CORE_XAPI_SERVICES } from '@features/xapi/xapi.module';
|
||||
import { CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins';
|
||||
|
||||
// Import other libraries and providers.
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import moment from 'moment';
|
||||
import { Md5 } from 'ts-md5/dist/md5';
|
||||
|
||||
// Import core classes that can be useful for site plugins.
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { CoreWindow } from '@singletons/window';
|
||||
import { CoreCache } from '@classes/cache';
|
||||
import { CoreDelegate } from '@classes/delegate';
|
||||
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksModuleGradeHandler } from '@features/contentlinks/classes/module-grade-handler';
|
||||
import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
|
||||
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
||||
import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler';
|
||||
import { CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation';
|
||||
|
||||
// Import all core modules that define components, directives and pipes.
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
|
||||
import { CoreCourseDirectivesModule } from '@features/course/directives/directives.module';
|
||||
import { CoreCoursesComponentsModule } from '@features/courses/components/components.module';
|
||||
import { CoreSitePluginsDirectivesModule } from '@features/siteplugins/directives/directives.module';
|
||||
import { CoreUserComponentsModule } from '@features/user/components/components.module';
|
||||
import { CoreQuestionComponentsModule } from '@features/question/components/components.module';
|
||||
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
|
||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
|
||||
import { CoreSearchComponentsModule } from '@features/search/components/components.module';
|
||||
|
||||
// Import some components so they can be injected dynamically.
|
||||
import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module';
|
||||
import { CoreCourseFormatSingleActivityComponent } from '@features/course/format/singleactivity/components/singleactivity';
|
||||
import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index';
|
||||
import { CoreSitePluginsBlockComponent } from '@features/siteplugins/components/block/block';
|
||||
import { CoreSitePluginsCourseFormatComponent } from '@features/siteplugins/components/course-format/course-format';
|
||||
import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from '@features/siteplugins/components/question-behaviour/question-behaviour';
|
||||
import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field';
|
||||
import { CoreSitePluginsQuizAccessRuleComponent } from '@features/siteplugins/components/quiz-access-rule/quiz-access-rule';
|
||||
import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback';
|
||||
import { CoreSitePluginsAssignSubmissionComponent } from '@features/siteplugins/components/assign-submission/assign-submission';
|
||||
|
||||
// Import addon providers. Do not import database module because it causes circular dependencies.
|
||||
import { ADDON_BADGES_SERVICES } from '@addons/badges/badges.module';
|
||||
import { ADDON_CALENDAR_SERVICES } from '@addons/calendar/calendar.module';
|
||||
// @todo import { ADDON_COMPETENCY_SERVICES } from '@addons/competency/competency.module';
|
||||
import { ADDON_MESSAGEOUTPUT_SERVICES } from '@addons/messageoutput/messageoutput.module';
|
||||
import { ADDON_MESSAGES_SERVICES } from '@addons/messages/messages.module';
|
||||
import { ADDON_MOD_ASSIGN_SERVICES } from '@addons/mod/assign/assign.module';
|
||||
import { ADDON_MOD_BOOK_SERVICES } from '@addons/mod/book/book.module';
|
||||
// @todo import { ADDON_MOD_CHAT_SERVICES } from '@addons/mod/chat/chat.module';
|
||||
// @todo import { ADDON_MOD_CHOICE_SERVICES } from '@addons/mod/choice/choice.module';
|
||||
// @todo import { ADDON_MOD_FEEDBACK_SERVICES } from '@addons/mod/feedback/feedback.module';
|
||||
import { ADDON_MOD_FOLDER_SERVICES } from '@addons/mod/folder/folder.module';
|
||||
import { ADDON_MOD_FORUM_SERVICES } from '@addons/mod/forum/forum.module';
|
||||
// @todo import { ADDON_MOD_GLOSSARY_SERVICES } from '@addons/mod/glossary/glossary.module';
|
||||
// @todo import { ADDON_MOD_H5P_ACTIVITY_SERVICES } from '@addons/mod/h5pactivity/h5pactivity.module';
|
||||
import { ADDON_MOD_IMSCP_SERVICES } from '@addons/mod/imscp/imscp.module';
|
||||
import { ADDON_MOD_LESSON_SERVICES } from '@addons/mod/lesson/lesson.module';
|
||||
// @todo import { ADDON_MOD_LTI_SERVICES } from '@addons/mod/lti/lti.module';
|
||||
import { ADDON_MOD_PAGE_SERVICES } from '@addons/mod/page/page.module';
|
||||
import { ADDON_MOD_QUIZ_SERVICES } from '@addons/mod/quiz/quiz.module';
|
||||
import { ADDON_MOD_RESOURCE_SERVICES } from '@addons/mod/resource/resource.module';
|
||||
// @todo import { ADDON_MOD_SCORM_SERVICES } from '@addons/mod/scorm/scorm.module';
|
||||
// @todo import { ADDON_MOD_SURVEY_SERVICES } from '@addons/mod/survey/survey.module';
|
||||
import { ADDON_MOD_URL_SERVICES } from '@addons/mod/url/url.module';
|
||||
// @todo import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module';
|
||||
// @todo import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module';
|
||||
// @todo import { ADDON_NOTES_SERVICES } from '@addons/notes/notes.module';
|
||||
import { ADDON_NOTIFICATIONS_SERVICES } from '@addons/notifications/notifications.module';
|
||||
import { ADDON_PRIVATEFILES_SERVICES } from '@addons/privatefiles/privatefiles.module';
|
||||
// @todo import { ADDON_REMOTETHEMES_SERVICES } from '@addons/remotethemes/remotethemes.module';
|
||||
|
||||
// Import some addon modules that define components, directives and pipes. Only import the important ones.
|
||||
import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module';
|
||||
// @todo import { AddonModWorkshopComponentsModule } from '@addons/mod/workshop/components/components.module';
|
||||
|
||||
/**
|
||||
* Service to provide functionalities regarding compiling dynamic HTML and Javascript.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreCompileProvider {
|
||||
|
||||
protected logger: CoreLogger;
|
||||
protected compiler: Compiler;
|
||||
|
||||
// Other Ionic/Angular providers that don't depend on where they are injected.
|
||||
protected readonly OTHER_SERVICES: unknown[] = [
|
||||
TranslateService, HttpClient, Platform, DomSanitizer, ActionSheetController, AlertController, LoadingController,
|
||||
ModalController, PopoverController, ToastController, FormBuilder,
|
||||
];
|
||||
|
||||
// List of imports for dynamic module. Since the template can have any component we need to import all core components modules.
|
||||
protected readonly IMPORTS = [
|
||||
CoreSharedModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreUserComponentsModule,
|
||||
CoreCourseDirectivesModule, CoreQuestionComponentsModule, AddonModAssignComponentsModule,
|
||||
CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, CoreSitePluginsDirectivesModule,
|
||||
// @todo AddonModWorkshopComponentsModule,
|
||||
];
|
||||
|
||||
constructor(protected injector: Injector, compilerFactory: JitCompilerFactory) {
|
||||
this.logger = CoreLogger.getInstance('CoreCompileProvider');
|
||||
|
||||
this.compiler = compilerFactory.createCompiler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and compile a dynamic component.
|
||||
*
|
||||
* @param template The template of the component.
|
||||
* @param componentClass The JS class of the component.
|
||||
* @param extraImports Extra imported modules if needed and not imported by this class.
|
||||
* @return Promise resolved with the factory to instantiate the component.
|
||||
*/
|
||||
async createAndCompileComponent<T = unknown>(
|
||||
template: string,
|
||||
componentClass: Type<T>,
|
||||
extraImports: any[] = [], // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
): Promise<ComponentFactory<T> | undefined> {
|
||||
// Create the component using the template and the class.
|
||||
const component = Component({ template })(componentClass);
|
||||
|
||||
const imports = [
|
||||
...this.IMPORTS,
|
||||
...extraImports,
|
||||
];
|
||||
|
||||
// Now create the module containing the component.
|
||||
const module = NgModule({ imports, declarations: [component], schemas: [NO_ERRORS_SCHEMA] })(class {});
|
||||
|
||||
try {
|
||||
// Compile the module and the component.
|
||||
const factories = await this.compiler.compileModuleAndAllComponentsAsync(module);
|
||||
|
||||
// Search and return the factory of the component we just created.
|
||||
return factories.componentFactories.find(factory => factory.componentType == component);
|
||||
} catch (error) {
|
||||
this.logger.error('Error compiling template', template);
|
||||
this.logger.error(error);
|
||||
error.message = 'Template has some errors and cannot be displayed.';
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eval some javascript using the context of the function.
|
||||
*
|
||||
* @param javascript The javascript to eval.
|
||||
* @return Result of the eval.
|
||||
*/
|
||||
protected evalInContext(javascript: string): unknown {
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(javascript);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute some javascript code, using a certain instance as the context.
|
||||
*
|
||||
* @param instance Instance to use as the context. In the JS code, "this" will be this instance.
|
||||
* @param javascript The javascript code to eval.
|
||||
* @return Result of the javascript execution.
|
||||
*/
|
||||
executeJavascript(instance: unknown, javascript: string): unknown {
|
||||
try {
|
||||
return this.evalInContext.call(instance, javascript);
|
||||
} catch (ex) {
|
||||
this.logger.error('Error evaluating javascript', ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject all the core libraries in a certain object.
|
||||
*
|
||||
* @param instance The instance where to inject the libraries.
|
||||
* @param extraProviders Extra imported providers if needed and not imported by this class.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
injectLibraries(instance: any, extraProviders: Type<unknown>[] = []): void {
|
||||
const providers = [
|
||||
...CORE_SERVICES,
|
||||
...CORE_BLOCK_SERVICES,
|
||||
...CORE_COMMENTS_SERVICES,
|
||||
...CORE_CONTENTLINKS_SERVICES,
|
||||
...CORE_COURSE_SERVICES,
|
||||
...CORE_COURSES_SERVICES,
|
||||
...CORE_EDITOR_SERVICES,
|
||||
...CORE_FILEUPLOADER_SERVICES,
|
||||
...CORE_FILTER_SERVICES,
|
||||
...CORE_GRADES_SERVICES,
|
||||
...CORE_H5P_SERVICES,
|
||||
...CORE_MAINMENU_SERVICES,
|
||||
...CORE_LOGIN_SERVICES,
|
||||
...CORE_QUESTION_SERVICES,
|
||||
...CORE_PUSHNOTIFICATIONS_SERVICES,
|
||||
...CORE_SEARCH_SERVICES,
|
||||
...CORE_SETTINGS_SERVICES,
|
||||
// @todo ...CORE_SHAREDFILES_SERVICES,
|
||||
...CORE_SITEHOME_SERVICES,
|
||||
CoreSitePluginsProvider,
|
||||
...CORE_TAG_SERVICES,
|
||||
...CORE_USER_SERVICES,
|
||||
...CORE_XAPI_SERVICES,
|
||||
...IONIC_NATIVE_SERVICES,
|
||||
...this.OTHER_SERVICES,
|
||||
...extraProviders,
|
||||
...ADDON_BADGES_SERVICES,
|
||||
...ADDON_CALENDAR_SERVICES,
|
||||
// @todo ...ADDON_COMPETENCY_SERVICES,
|
||||
...ADDON_MESSAGEOUTPUT_SERVICES,
|
||||
...ADDON_MESSAGES_SERVICES,
|
||||
...ADDON_MOD_ASSIGN_SERVICES,
|
||||
...ADDON_MOD_BOOK_SERVICES,
|
||||
// @todo ...ADDON_MOD_CHAT_SERVICES,
|
||||
// @todo ...ADDON_MOD_CHOICE_SERVICES,
|
||||
// @todo ...ADDON_MOD_FEEDBACK_SERVICES,
|
||||
...ADDON_MOD_FOLDER_SERVICES,
|
||||
...ADDON_MOD_FORUM_SERVICES,
|
||||
// @todo ...ADDON_MOD_GLOSSARY_SERVICES,
|
||||
// @todo ...ADDON_MOD_H5P_ACTIVITY_SERVICES,
|
||||
...ADDON_MOD_IMSCP_SERVICES,
|
||||
...ADDON_MOD_LESSON_SERVICES,
|
||||
// @todo ...ADDON_MOD_LTI_SERVICES,
|
||||
...ADDON_MOD_PAGE_SERVICES,
|
||||
...ADDON_MOD_QUIZ_SERVICES,
|
||||
...ADDON_MOD_RESOURCE_SERVICES,
|
||||
// @todo ...ADDON_MOD_SCORM_SERVICES,
|
||||
// @todo ...ADDON_MOD_SURVEY_SERVICES,
|
||||
...ADDON_MOD_URL_SERVICES,
|
||||
// @todo ...ADDON_MOD_WIKI_SERVICES,
|
||||
// @todo ...ADDON_MOD_WORKSHOP_SERVICES,
|
||||
// @todo ...ADDON_NOTES_SERVICES,
|
||||
...ADDON_NOTIFICATIONS_SERVICES,
|
||||
...ADDON_PRIVATEFILES_SERVICES,
|
||||
// @todo ...ADDON_REMOTETHEMES_SERVICES,
|
||||
];
|
||||
|
||||
// We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
|
||||
for (const i in providers) {
|
||||
const providerDef = providers[i];
|
||||
if (typeof providerDef == 'function' && providerDef.name) {
|
||||
try {
|
||||
// Inject the provider to the instance. We use the class name as the property name.
|
||||
instance[providerDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get(providerDef);
|
||||
} catch (ex) {
|
||||
this.logger.warn('Error injecting provider', providerDef.name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inject current service.
|
||||
instance['CoreCompileProvider'] = this;
|
||||
|
||||
// Add some final classes.
|
||||
instance['injector'] = this.injector;
|
||||
instance['Validators'] = Validators;
|
||||
instance['CoreConstants'] = CoreConstants;
|
||||
instance['CoreConfigConstants'] = CoreConstants.CONFIG;
|
||||
instance['CoreEventsProvider'] = CoreEvents;
|
||||
instance['CoreLoggerProvider'] = CoreLogger;
|
||||
instance['moment'] = moment;
|
||||
instance['Md5'] = Md5;
|
||||
instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider;
|
||||
instance['CoreArray'] = CoreArray;
|
||||
instance['CoreUrl'] = CoreUrl;
|
||||
instance['CoreWindow'] = CoreWindow;
|
||||
instance['CoreCache'] = CoreCache;
|
||||
instance['CoreDelegate'] = CoreDelegate;
|
||||
instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase;
|
||||
instance['CoreContentLinksModuleGradeHandler'] = CoreContentLinksModuleGradeHandler;
|
||||
instance['CoreContentLinksModuleIndexHandler'] = CoreContentLinksModuleIndexHandler;
|
||||
instance['CoreCourseActivityPrefetchHandlerBase'] = CoreCourseActivityPrefetchHandlerBase;
|
||||
instance['CoreCourseResourcePrefetchHandlerBase'] = CoreCourseResourcePrefetchHandlerBase;
|
||||
instance['CoreCourseUnsupportedModuleComponent'] = CoreCourseUnsupportedModuleComponent;
|
||||
instance['CoreCourseFormatSingleActivityComponent'] = CoreCourseFormatSingleActivityComponent;
|
||||
instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent;
|
||||
instance['CoreSitePluginsBlockComponent'] = CoreSitePluginsBlockComponent;
|
||||
instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent;
|
||||
instance['CoreSitePluginsQuestionComponent'] = CoreSitePluginsQuestionComponent;
|
||||
instance['CoreSitePluginsQuestionBehaviourComponent'] = CoreSitePluginsQuestionBehaviourComponent;
|
||||
instance['CoreSitePluginsUserProfileFieldComponent'] = CoreSitePluginsUserProfileFieldComponent;
|
||||
instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent;
|
||||
instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent;
|
||||
instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent;
|
||||
instance['CoreGeolocationError'] = CoreGeolocationError;
|
||||
instance['CoreGeolocationErrorReason'] = CoreGeolocationErrorReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a dynamic component.
|
||||
*
|
||||
* @param template The template of the component.
|
||||
* @param componentClass The JS class of the component.
|
||||
* @param injector The injector to use. It's recommended to pass it so NavController and similar can be injected.
|
||||
* @return Promise resolved with the component instance.
|
||||
*/
|
||||
async instantiateDynamicComponent<T = unknown>(
|
||||
template: string,
|
||||
componentClass: Type<T>,
|
||||
injector?: Injector,
|
||||
): Promise<ComponentRef<T> | undefined> {
|
||||
injector = injector || this.injector;
|
||||
|
||||
const factory = await this.createAndCompileComponent(template, componentClass);
|
||||
|
||||
if (factory) {
|
||||
// Create and return the component.
|
||||
return factory.create(injector, undefined, undefined, injector.get(NgModuleRef));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreCompile = makeSingleton(CoreCompileProvider);
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { CoreContentLinksDelegateService } from './services/contentlinks-delegate';
|
||||
import { CoreContentLinksHelperProvider } from './services/contentlinks-helper';
|
||||
|
||||
export const CORE_CONTENTLINKS_SERVICES: Type<unknown>[] = [
|
||||
CoreContentLinksDelegateService,
|
||||
CoreContentLinksHelperProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [],
|
||||
providers: [],
|
||||
exports: [],
|
||||
})
|
||||
export class CoreContentLinksModule {}
|
|
@ -507,6 +507,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
refresher?.detail.complete();
|
||||
done?.();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
|
@ -24,14 +24,33 @@ import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course';
|
|||
import { SITE_SCHEMA as LOG_SITE_SCHEMA } from './services/database/log';
|
||||
import { SITE_SCHEMA as PREFETCH_SITE_SCHEMA } from './services/database/module-prefetch';
|
||||
import { CoreCourseIndexRoutingModule } from './pages/index/index-routing.module';
|
||||
import { CoreCourseModulePrefetchDelegate } from './services/module-prefetch-delegate';
|
||||
import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchDelegateService } from './services/module-prefetch-delegate';
|
||||
import { CoreCronDelegate } from '@services/cron';
|
||||
import { CoreCourseLogCronHandler } from './services/handlers/log-cron';
|
||||
import { CoreCourseSyncCronHandler } from './services/handlers/sync-cron';
|
||||
import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate';
|
||||
import { CoreCourseTagAreaHandler } from './services/handlers/course-tag-area';
|
||||
import { CoreCourseModulesTagAreaHandler } from './services/handlers/modules-tag-area';
|
||||
import { CoreCourse } from './services/course';
|
||||
import { CoreCourse, CoreCourseProvider } from './services/course';
|
||||
import { CoreCourseHelperProvider } from './services/course-helper';
|
||||
import { CoreCourseLogHelperProvider } from './services/log-helper';
|
||||
import { CoreCourseFormatDelegateService } from './services/format-delegate';
|
||||
import { CoreCourseModuleDelegateService } from './services/module-delegate';
|
||||
import { CoreCourseOptionsDelegateService } from './services/course-options-delegate';
|
||||
import { CoreCourseOfflineProvider } from './services/course-offline';
|
||||
import { CoreCourseSyncProvider } from './services/sync';
|
||||
|
||||
export const CORE_COURSE_SERVICES: Type<unknown>[] = [
|
||||
CoreCourseProvider,
|
||||
CoreCourseHelperProvider,
|
||||
CoreCourseLogHelperProvider,
|
||||
CoreCourseFormatDelegateService,
|
||||
CoreCourseModuleDelegateService,
|
||||
CoreCourseModulePrefetchDelegateService,
|
||||
CoreCourseOptionsDelegateService,
|
||||
CoreCourseOfflineProvider,
|
||||
CoreCourseSyncProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@ import { CoreCourseHelper, CoreCourseModuleCompletionData } from './course-helpe
|
|||
import { CoreCourseFormatDelegate } from './format-delegate';
|
||||
import { CoreCronDelegate } from '@services/cron';
|
||||
import { CoreCourseLogCronHandler } from './handlers/log-cron';
|
||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmCourse:';
|
||||
|
||||
|
@ -1026,7 +1027,7 @@ export class CoreCourseProvider {
|
|||
const loading = await CoreDomUtils.showModalLoading();
|
||||
|
||||
// Wait for site plugins to be fetched.
|
||||
// @todo await this.sitePluginsProvider.waitFetchPlugins();
|
||||
await CoreSitePlugins.waitFetchPlugins();
|
||||
|
||||
if (!('format' in course) || typeof course.format == 'undefined') {
|
||||
const result = await CoreCourseHelper.getCourse(course.id);
|
||||
|
@ -1034,7 +1035,9 @@ export class CoreCourseProvider {
|
|||
course = result.course;
|
||||
}
|
||||
|
||||
if (course) { // @todo Replace with: if (!this.sitePluginsProvider.sitePluginPromiseExists('format_' + course.format)) {
|
||||
const format = 'format' in course && `format_${course.format}`;
|
||||
|
||||
if (!format || !CoreSitePlugins.sitePluginPromiseExists(`format_${format}`)) {
|
||||
// No custom format plugin. We don't need to wait for anything.
|
||||
await CoreCourseFormatDelegate.openCourse(<CoreCourseAnyCourseData> course, params);
|
||||
loading.dismiss();
|
||||
|
@ -1044,11 +1047,12 @@ export class CoreCourseProvider {
|
|||
|
||||
// This course uses a custom format plugin, wait for the format plugin to finish loading.
|
||||
try {
|
||||
/* @todo await this.sitePluginsProvider.sitePluginLoaded('format_' + course.format);
|
||||
await CoreSitePlugins.sitePluginLoaded(format);
|
||||
|
||||
// The format loaded successfully, but the handlers wont be registered until all site plugins have loaded.
|
||||
if (this.sitePluginsProvider.sitePluginsFinishedLoading) {
|
||||
return CoreCourseFormatDelegate.openCourse(course, params);
|
||||
}*/
|
||||
if (CoreSitePlugins.sitePluginsFinishedLoading) {
|
||||
return CoreCourseFormatDelegate.openCourse(<CoreCourseAnyCourseData> course, params);
|
||||
}
|
||||
|
||||
// Wait for plugins to be loaded.
|
||||
const deferred = CoreUtils.promiseDefer<void>();
|
||||
|
|
|
@ -82,7 +82,7 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
|
|||
*
|
||||
* @return The icon src.
|
||||
*/
|
||||
getIconSrc?(): string;
|
||||
getIconSrc?(): string | undefined;
|
||||
|
||||
/**
|
||||
* Check if this type of module supports a certain feature.
|
||||
|
@ -336,7 +336,7 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu
|
|||
* @param modicon The mod icon string.
|
||||
* @return The icon src.
|
||||
*/
|
||||
getModuleIconSrc(modname: string, modicon?: string): string {
|
||||
getModuleIconSrc(modname: string, modicon?: string): string | undefined {
|
||||
return this.executeFunctionOnEnabled<string>(modname, 'getIconSrc') ||
|
||||
CoreCourse.getModuleIconSrc(modname, modicon);
|
||||
}
|
||||
|
|
|
@ -12,13 +12,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
|
||||
import { CoreMainMenuHomeRoutingModule } from '@features/mainmenu/pages/home/home-routing.module';
|
||||
import { CoreMainMenuHomeDelegate } from '@features/mainmenu/services/home-delegate';
|
||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||
import { CoreCoursesProvider } from './services/courses';
|
||||
import { CoreCoursesHelperProvider } from './services/courses-helper';
|
||||
import { CoreCoursesDashboardProvider } from './services/dashboard';
|
||||
import { CoreCoursesCourseLinkHandler } from './services/handlers/course-link';
|
||||
import { CoreCoursesIndexLinkHandler } from './services/handlers/courses-index-link';
|
||||
|
||||
|
@ -28,6 +31,12 @@ import { CoreCoursesEnrolPushClickHandler } from './services/handlers/enrol-push
|
|||
import { CoreCoursesMyCoursesHomeHandler, CoreCoursesMyCoursesHomeHandlerService } from './services/handlers/my-courses-home';
|
||||
import { CoreCoursesRequestPushClickHandler } from './services/handlers/request-push-click';
|
||||
|
||||
export const CORE_COURSES_SERVICES: Type<unknown>[] = [
|
||||
CoreCoursesProvider,
|
||||
CoreCoursesDashboardProvider,
|
||||
CoreCoursesHelperProvider,
|
||||
];
|
||||
|
||||
const mainMenuHomeChildrenRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
|
|
|
@ -18,7 +18,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
|
|||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreStatusWithWarningsWSResponse, CoreWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreEvents, CoreEventSiteData } from '@singletons/events';
|
||||
import { CoreWSError } from '@classes/errors/wserror';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmCourses:';
|
||||
|
@ -853,7 +853,7 @@ export class CoreCoursesProvider {
|
|||
|
||||
if (added.length || removed.length) {
|
||||
// At least 1 course was added or removed, trigger the event.
|
||||
CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, {
|
||||
CoreEvents.trigger<CoreCoursesMyCoursesChangedEventData>(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, {
|
||||
added: added,
|
||||
removed: removed,
|
||||
}, site.getId());
|
||||
|
@ -1169,7 +1169,7 @@ export const CoreCourses = makeSingleton(CoreCoursesProvider);
|
|||
/**
|
||||
* Data sent to the EVENT_MY_COURSES_UPDATED.
|
||||
*/
|
||||
export type CoreCoursesMyCoursesUpdatedEventData = {
|
||||
export type CoreCoursesMyCoursesUpdatedEventData = CoreEventSiteData & {
|
||||
action: string; // Action performed.
|
||||
courseId?: number; // Course ID affected (if any).
|
||||
course?: CoreCourseAnyCourseData; // Course affected (if any).
|
||||
|
@ -1177,6 +1177,14 @@ export type CoreCoursesMyCoursesUpdatedEventData = {
|
|||
value?: boolean; // The new value for the state changed.
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent to the EVENT_MY_COURSES_CHANGED.
|
||||
*/
|
||||
export type CoreCoursesMyCoursesChangedEventData = CoreEventSiteData & {
|
||||
added: number[];
|
||||
removed: number[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Params of core_enrol_get_users_courses WS.
|
||||
*/
|
||||
|
|
|
@ -12,11 +12,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { CoreEditorComponentsModule } from './components/components.module';
|
||||
import { SITE_SCHEMA } from './services/database/editor';
|
||||
import { CoreEditorOfflineProvider } from './services/editor-offline';
|
||||
|
||||
export const CORE_EDITOR_SERVICES: Type<unknown>[] = [
|
||||
CoreEditorOfflineProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
@ -45,6 +45,33 @@ import { StatusBar } from '@ionic-native/status-bar/ngx';
|
|||
import { WebIntent } from '@ionic-native/web-intent/ngx';
|
||||
import { Zip } from '@ionic-native/zip/ngx';
|
||||
|
||||
export const IONIC_NATIVE_SERVICES = [
|
||||
Badge,
|
||||
Camera,
|
||||
Chooser,
|
||||
Clipboard,
|
||||
Device,
|
||||
Diagnostic,
|
||||
File,
|
||||
FileOpener,
|
||||
FileTransfer,
|
||||
Geolocation,
|
||||
HTTP,
|
||||
InAppBrowser,
|
||||
Keyboard,
|
||||
LocalNotifications,
|
||||
Media,
|
||||
MediaCapture,
|
||||
Network,
|
||||
Push,
|
||||
QRScanner,
|
||||
SplashScreen,
|
||||
StatusBar,
|
||||
SQLite,
|
||||
WebIntent,
|
||||
WebView,
|
||||
Zip,
|
||||
];
|
||||
// Mock services.
|
||||
import { CameraMock } from './services/camera';
|
||||
import { ClipboardMock } from './services/clipboard';
|
||||
|
|
|
@ -31,6 +31,7 @@ import { CoreXAPIModule } from './xapi/xapi.module';
|
|||
import { CoreViewerModule } from './viewer/viewer.module';
|
||||
import { CoreSearchModule } from './search/search.module';
|
||||
import { CoreCommentsModule } from './comments/comments.module';
|
||||
import { CoreSitePluginsModule } from './siteplugins/siteplugins.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -51,6 +52,7 @@ import { CoreCommentsModule } from './comments/comments.module';
|
|||
CoreH5PModule,
|
||||
CoreViewerModule,
|
||||
CoreCommentsModule,
|
||||
CoreSitePluginsModule,
|
||||
],
|
||||
})
|
||||
export class CoreFeaturesModule {}
|
||||
|
|
|
@ -12,15 +12,22 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CoreFileUploaderDelegate } from './services/fileuploader-delegate';
|
||||
import { CoreFileUploaderProvider } from './services/fileuploader';
|
||||
import { CoreFileUploaderDelegate, CoreFileUploaderDelegateService } from './services/fileuploader-delegate';
|
||||
import { CoreFileUploaderHelperProvider } from './services/fileuploader-helper';
|
||||
import { CoreFileUploaderAlbumHandler } from './services/handlers/album';
|
||||
import { CoreFileUploaderAudioHandler } from './services/handlers/audio';
|
||||
import { CoreFileUploaderCameraHandler } from './services/handlers/camera';
|
||||
import { CoreFileUploaderFileHandler } from './services/handlers/file';
|
||||
import { CoreFileUploaderVideoHandler } from './services/handlers/video';
|
||||
|
||||
export const CORE_FILEUPLOADER_SERVICES: Type<unknown>[] = [
|
||||
CoreFileUploaderProvider,
|
||||
CoreFileUploaderHelperProvider,
|
||||
CoreFileUploaderDelegateService,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { CoreFilterProvider } from './services/filter';
|
||||
import { CoreFilterDelegateService } from './services/filter-delegate';
|
||||
import { CoreFilterHelperProvider } from './services/filter-helper';
|
||||
|
||||
export const CORE_FILTER_SERVICES: Type<unknown>[] = [
|
||||
CoreFilterProvider,
|
||||
CoreFilterDelegateService,
|
||||
CoreFilterHelperProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [],
|
||||
providers: [],
|
||||
})
|
||||
export class CoreFilterModule { }
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index-routing.module';
|
||||
|
@ -21,12 +21,19 @@ import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.m
|
|||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
|
||||
import { CoreUserDelegate } from '@features/user/services/user-delegate';
|
||||
import { CoreGradesProvider } from './services/grades';
|
||||
import { CoreGradesHelperProvider } from './services/grades-helper';
|
||||
import { CoreGradesCourseOptionHandler } from './services/handlers/course-option';
|
||||
import { CoreGradesMainMenuHandler, CoreGradesMainMenuHandlerService } from './services/handlers/mainmenu';
|
||||
import { CoreGradesOverviewLinkHandler } from './services/handlers/overview-link';
|
||||
import { CoreGradesUserHandler } from './services/handlers/user';
|
||||
import { CoreGradesUserLinkHandler } from './services/handlers/user-link';
|
||||
|
||||
export const CORE_GRADES_SERVICES: Type<unknown>[] = [
|
||||
CoreGradesProvider,
|
||||
CoreGradesHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: CoreGradesMainMenuHandlerService.PAGE_NAME,
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CorePluginFileDelegate } from '@services/plugin-file-delegate';
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { CoreH5PComponentsModule } from './components/components.module';
|
||||
import { SITE_SCHEMA } from './services/database/h5p';
|
||||
import { CoreH5PProvider } from './services/h5p';
|
||||
import { CoreH5PPluginFileHandler } from './services/handlers/pluginfile';
|
||||
|
||||
export const CORE_H5P_SERVICES: Type<unknown>[] = [
|
||||
CoreH5PProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreH5PComponentsModule,
|
||||
|
|
|
@ -16,6 +16,11 @@ import { NgModule } from '@angular/core';
|
|||
import { Routes } from '@angular/router';
|
||||
|
||||
import { AppRoutingModule } from '@/app/app-routing.module';
|
||||
import { CoreLoginHelperProvider } from './services/login-helper';
|
||||
|
||||
export const CORE_LOGIN_SERVICES = [
|
||||
CoreLoginHelperProvider,
|
||||
];
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -31,11 +31,11 @@
|
|||
|
||||
<form [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #credentialsForm>
|
||||
<ion-item *ngIf="siteChecked && !isBrowserSSO">
|
||||
<ion-label>
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}"
|
||||
formControlName="username" autocapitalize="none" autocorrect="off" autocomplete="username" enterkeyhint="next"
|
||||
required="true"></ion-input>
|
||||
</ion-label>
|
||||
required="true">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="siteChecked && !isBrowserSSO" class="ion-margin-bottom">
|
||||
<ion-label></ion-label>
|
||||
|
|
|
@ -29,11 +29,10 @@
|
|||
</ion-item>
|
||||
</ion-radio-group>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}"
|
||||
formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]="autoFocus">
|
||||
</ion-input>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button type="submit" class="ion-margin" expand="block" [disabled]="!myForm.valid">
|
||||
{{ 'core.courses.search' | translate }}
|
||||
|
|
|
@ -18,8 +18,16 @@ import { CoreMainMenuAuthGuard } from '@features/mainmenu/guards/auth';
|
|||
|
||||
import { AppRoutingModule } from '@/app/app-routing.module';
|
||||
|
||||
import { CoreMainMenuDelegate } from './services/mainmenu-delegate';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuDelegateService } from './services/mainmenu-delegate';
|
||||
import { CoreMainMenuHomeHandler } from './services/handlers/mainmenu';
|
||||
import { CoreMainMenuProvider } from './services/mainmenu';
|
||||
import { CoreMainMenuHomeDelegateService } from './services/home-delegate';
|
||||
|
||||
export const CORE_MAINMENU_SERVICES = [
|
||||
CoreMainMenuHomeDelegateService,
|
||||
CoreMainMenuDelegateService,
|
||||
CoreMainMenuProvider,
|
||||
];
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,14 +12,20 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CoreCronDelegate } from '@services/cron';
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
import { SITE_SCHEMA } from './services/database/pushnotifications';
|
||||
import { CorePushNotificationsRegisterCronHandler } from './services/handlers/register-cron';
|
||||
import { CorePushNotificationsUnregisterCronHandler } from './services/handlers/unregister-cron';
|
||||
import { CorePushNotifications } from './services/pushnotifications';
|
||||
import { CorePushNotificationsDelegateService } from './services/push-delegate';
|
||||
import { CorePushNotifications, CorePushNotificationsProvider } from './services/pushnotifications';
|
||||
|
||||
export const CORE_PUSHNOTIFICATIONS_SERVICES: Type<unknown>[] = [
|
||||
CorePushNotificationsProvider,
|
||||
CorePushNotificationsDelegateService,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
@ -39,6 +39,7 @@ export class CoreQuestionBaseComponent {
|
|||
@Input() contextInstanceId?: number; // The instance ID related to the context.
|
||||
@Input() courseId?: number; // The course the question belongs to (if any).
|
||||
@Input() review?: boolean; // Whether the user is in review mode.
|
||||
@Input() preferredBehaviour?: string; // Preferred behaviour.
|
||||
@Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
|
||||
@Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ export class CoreQuestionComponent implements OnInit {
|
|||
contextInstanceId: this.contextInstanceId,
|
||||
courseId: this.courseId,
|
||||
review: this.review,
|
||||
preferredBehaviour: this.preferredBehaviour,
|
||||
buttonClicked: this.buttonClicked,
|
||||
onAbort: this.onAbort,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// (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 { CoreQuestionBehaviourDelegateService } from './services/behaviour-delegate';
|
||||
import { CoreQuestionProvider } from './services/question';
|
||||
import { CoreQuestionDelegateService } from './services/question-delegate';
|
||||
import { CoreQuestionHelperProvider } from './services/question-helper';
|
||||
|
||||
export const CORE_QUESTION_SERVICES: Type<unknown>[] = [
|
||||
CoreQuestionProvider,
|
||||
CoreQuestionDelegateService,
|
||||
CoreQuestionBehaviourDelegateService,
|
||||
CoreQuestionHelperProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [],
|
||||
providers: [],
|
||||
exports: [],
|
||||
})
|
||||
export class CoreQuestionModule {}
|
|
@ -1,11 +1,11 @@
|
|||
<ion-card>
|
||||
<form (ngSubmit)="submitForm($event)" role="search" #searchForm>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<ion-label></ion-label>
|
||||
<ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder"
|
||||
[autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus"
|
||||
[disabled]="disabled" role="searchbox" (ionFocus)="focus($event)"></ion-input>
|
||||
</ion-label>
|
||||
[disabled]="disabled" role="searchbox" (ionFocus)="focus($event)">
|
||||
</ion-input>
|
||||
<ion-button slot="end" fill="clear" type="submit" size="small" [attr.aria-label]="searchLabel"
|
||||
[disabled]="disabled || !searchText || (searchText.length < lengthCheck)">
|
||||
<ion-icon name="fas-search" slot="icon-only"></ion-icon>
|
||||
|
|
|
@ -12,12 +12,17 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
|
||||
import { CoreSearchComponentsModule } from './components/components.module';
|
||||
import { SITE_SCHEMA } from './services/search-history-db';
|
||||
import { CoreSearchHistoryProvider } from './services/search-history.service';
|
||||
|
||||
export const CORE_SEARCH_SERVICES: Type<unknown>[] = [
|
||||
CoreSearchHistoryProvider,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
|
|
@ -12,13 +12,18 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { AppRoutingModule } from '@/app/app-routing.module';
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
|
||||
import { CoreSettingsHelperProvider } from './services/settings-helper';
|
||||
import { CoreSettingsDelegateService } from './services/settings-delegate';
|
||||
|
||||
export const CORE_SETTINGS_SERVICES: Type<unknown>[] = [
|
||||
CoreSettingsDelegateService,
|
||||
CoreSettingsHelperProvider,
|
||||
];
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { CoreSiteHomeIndexLinkHandler } from './services/handlers/index-link';
|
||||
|
@ -20,6 +20,11 @@ import { CoreContentLinksDelegate } from '@features/contentlinks/services/conten
|
|||
import { CoreSiteHomeHomeHandler, CoreSiteHomeHomeHandlerService } from './services/handlers/sitehome-home';
|
||||
import { CoreMainMenuHomeDelegate } from '@features/mainmenu/services/home-delegate';
|
||||
import { CoreMainMenuHomeRoutingModule } from '@features/mainmenu/pages/home/home-routing.module';
|
||||
import { CoreSiteHomeProvider } from './services/sitehome';
|
||||
|
||||
export const CORE_SITEHOME_SERVICES: Type<unknown>[] = [
|
||||
CoreSiteHomeProvider,
|
||||
];
|
||||
|
||||
const mainMenuHomeRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
// (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 { Input, OnInit, ElementRef, Directive } from '@angular/core';
|
||||
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content';
|
||||
import { CoreSitePluginsCallWSBaseDirective } from './call-ws-directive';
|
||||
|
||||
/**
|
||||
* Base class for directives to call a WS when the element is clicked.
|
||||
*
|
||||
* The directives that inherit from this class will call a WS method when the element is clicked.
|
||||
*/
|
||||
@Directive()
|
||||
export class CoreSitePluginsCallWSOnClickBaseDirective extends CoreSitePluginsCallWSBaseDirective implements OnInit {
|
||||
|
||||
@Input() confirmMessage?: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message.
|
||||
@Input() showError?: boolean | string; // Whether to show an error message if the WS call fails. Defaults to true.
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
parentContent: CoreSitePluginsPluginContentComponent | null,
|
||||
) {
|
||||
super(element, parentContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.element.addEventListener('click', async (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (typeof this.confirmMessage != 'undefined') {
|
||||
// Ask for confirm.
|
||||
try {
|
||||
await CoreDomUtils.showConfirm(this.confirmMessage || Translate.instant('core.areyousure'));
|
||||
} catch {
|
||||
// User cancelled, stop.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.callWS();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected async callWS(): Promise<void> {
|
||||
const modal = await CoreDomUtils.showModalLoading();
|
||||
|
||||
try {
|
||||
await super.callWS();
|
||||
} catch (error) {
|
||||
if (typeof this.showError == 'undefined' || CoreUtils.isTrueOrOne(this.showError)) {
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.serverconnection', true);
|
||||
}
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// (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 { Input, OnInit, OnDestroy, ElementRef, Output, EventEmitter, Directive } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content';
|
||||
import { CoreSitePlugins } from '../services/siteplugins';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
|
||||
/**
|
||||
* Base class for directives that need to call a WS.
|
||||
*/
|
||||
@Directive()
|
||||
export class CoreSitePluginsCallWSBaseDirective implements OnInit, OnDestroy {
|
||||
|
||||
@Input() name!: string; // The name of the WS to call.
|
||||
@Input() params?: Record<string, unknown>; // The params for the WS call.
|
||||
@Input() preSets?: CoreSiteWSPreSets; // The preSets for the WS call.
|
||||
@Input() useOtherDataForWS?: string[] | unknown; // Whether to include other data in the params for the WS.
|
||||
@Input() form?: string; // ID or name to identify a form. The form data will be retrieved and sent to the WS.
|
||||
@Output() onSuccess = new EventEmitter<unknown>(); // Sends the result when the WS call succeeds.
|
||||
@Output() onError = new EventEmitter<unknown>(); // Sends the error when the WS call fails.
|
||||
@Output() onDone = new EventEmitter<void>(); // Notifies when the WS call is done (either success or fail).
|
||||
|
||||
protected logger: CoreLogger;
|
||||
protected element: HTMLElement;
|
||||
protected invalidateObserver?: Subscription;
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
protected parentContent: CoreSitePluginsPluginContentComponent | null,
|
||||
) {
|
||||
this.element = element.nativeElement || element;
|
||||
this.logger = CoreLogger.getInstance('CoreSitePluginsCallWS');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (!this.parentContent?.invalidateObservable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.invalidateObserver = this.parentContent.invalidateObservable.subscribe(() => {
|
||||
this.invalidate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a WS.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async callWS(): Promise<void> {
|
||||
try {
|
||||
const params = this.getParamsForWS();
|
||||
|
||||
const result = await CoreSitePlugins.callWS(this.name, params, this.preSets);
|
||||
|
||||
this.onSuccess.emit(result);
|
||||
|
||||
return this.wsCallSuccess(result);
|
||||
} catch (error) {
|
||||
this.onError.emit(error);
|
||||
this.logger.error(`Error calling WS ${this.name}`, error);
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
this.onDone.emit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the params for the WS call.
|
||||
*
|
||||
* @return Params.
|
||||
*/
|
||||
protected getParamsForWS(): Record<string, unknown> {
|
||||
let params = this.params || {};
|
||||
|
||||
if (this.parentContent) {
|
||||
params = CoreSitePlugins.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS);
|
||||
}
|
||||
|
||||
if (this.form && document.forms[this.form]) {
|
||||
params = Object.assign(params, CoreDomUtils.getDataFromForm(document.forms[this.form]));
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when the WS call is successful.
|
||||
*
|
||||
* @param result Result of the WS call.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected wsCallSuccess(result: unknown): void {
|
||||
// Function to be overridden.
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the WS call.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
invalidate(): Promise<void> {
|
||||
const params = this.getParamsForWS();
|
||||
|
||||
return CoreSitePlugins.instance.invalidateCallWS(this.name, params, this.preSets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directive destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.invalidateObserver?.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// (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 { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreSitePlugins, CoreSitePluginsInitHandlerData } from '../services/siteplugins';
|
||||
|
||||
/**
|
||||
* Base class for components that will display a component using core-compile-html and want to call a
|
||||
* componentInit function returned by the handler JS.
|
||||
*/
|
||||
export class CoreSitePluginsCompileInitComponent {
|
||||
|
||||
content = ''; // Content.
|
||||
jsData: Record<string, unknown> = {}; // Data to pass to the component.
|
||||
protected handlerSchema?: CoreSitePluginsInitHandlerData; // The handler data.
|
||||
|
||||
/**
|
||||
* Function called when the component is created.
|
||||
*
|
||||
* @param instance The component instance.
|
||||
*/
|
||||
componentCreated(instance: unknown): void {
|
||||
// Check if the JS defined an init function.
|
||||
if (instance && this.handlerSchema?.methodJSResult?.componentInit) {
|
||||
this.handlerSchema.methodJSResult.componentInit.apply(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handler data.
|
||||
*
|
||||
* @param name The name of the handler.
|
||||
*/
|
||||
getHandlerData(name: string): void {
|
||||
// Retrieve the handler data.
|
||||
const handler = CoreSitePlugins.getSitePluginHandler(name);
|
||||
|
||||
this.handlerSchema = handler?.handlerSchema;
|
||||
|
||||
if (!this.handlerSchema) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load first template.
|
||||
if (this.handlerSchema.methodTemplates?.length) {
|
||||
this.content = this.handlerSchema.methodTemplates[0].html;
|
||||
this.jsData.CONTENT_TEMPLATES = CoreUtils.objectToKeyValueMap(
|
||||
this.handlerSchema.methodTemplates,
|
||||
'id',
|
||||
'html',
|
||||
);
|
||||
}
|
||||
|
||||
// Pass data from the method result to the component.
|
||||
if (this.handlerSchema.methodOtherdata) {
|
||||
this.jsData.CONTENT_OTHERDATA = this.handlerSchema.methodOtherdata;
|
||||
}
|
||||
|
||||
if (this.handlerSchema.methodJSResult) {
|
||||
this.jsData.CONTENT_JS_RESULT = this.handlerSchema.methodJSResult;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { AddonModAssignDefaultFeedbackHandler } from '@addons/mod/assign/services/handlers/default-feedback';
|
||||
import { AddonModAssignPlugin } from '@addons/mod/assign/services/assign';
|
||||
import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Handler to display an assign feedback site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsAssignFeedbackHandler extends AddonModAssignDefaultFeedbackHandler {
|
||||
|
||||
constructor(public name: string, public type: string, protected prefix: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getComponent(): Type<unknown> | undefined {
|
||||
return CoreSitePluginsAssignFeedbackComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
// Check if there's a translated string for the plugin.
|
||||
const translationId = this.prefix + 'pluginname';
|
||||
const translation = Translate.instant(translationId);
|
||||
|
||||
if (translationId != translation) {
|
||||
// Translation found, use it.
|
||||
return translation;
|
||||
}
|
||||
|
||||
// Fallback to WS string.
|
||||
return plugin.name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { AddonModAssignPlugin } from '@addons/mod/assign/services/assign';
|
||||
import { AddonModAssignDefaultSubmissionHandler } from '@addons/mod/assign/services/handlers/default-submission';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreSitePluginsAssignSubmissionComponent } from '../../components/assign-submission/assign-submission';
|
||||
|
||||
/**
|
||||
* Handler to display an assign submission site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsAssignSubmissionHandler extends AddonModAssignDefaultSubmissionHandler {
|
||||
|
||||
constructor(public name: string, public type: string, protected prefix: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getComponent(): Type<unknown> {
|
||||
return CoreSitePluginsAssignSubmissionComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
// Check if there's a translated string for the plugin.
|
||||
const translationId = this.prefix + 'pluginname';
|
||||
const translation = Translate.instant(translationId);
|
||||
|
||||
if (translationId != translation) {
|
||||
// Translation found, use it.
|
||||
return translation;
|
||||
}
|
||||
|
||||
// Fallback to WS string.
|
||||
return plugin.name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { CoreDelegateHandler } from '@classes/delegate';
|
||||
|
||||
/**
|
||||
* Super class for handlers for site plugins.
|
||||
*/
|
||||
export class CoreSitePluginsBaseHandler implements CoreDelegateHandler {
|
||||
|
||||
constructor(public name: string) { }
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreBlockPreRenderedComponent } from '@features/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockDelegate, CoreBlockHandler, CoreBlockHandlerData } from '@features/block/services/block-delegate';
|
||||
import { CoreCourseBlock } from '@features/course/services/course';
|
||||
import { CoreSitePluginsBlockComponent } from '@features/siteplugins/components/block/block';
|
||||
import { CoreSitePluginsOnlyTitleBlockComponent } from '@features/siteplugins/components/only-title-block/only-title-block';
|
||||
import { CoreSitePluginsBlockHandlerData, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to support a block using a site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsBlockHandler extends CoreSitePluginsBaseHandler implements CoreBlockHandler {
|
||||
|
||||
protected logger: CoreLogger;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
public title: string,
|
||||
public blockName: string,
|
||||
protected handlerSchema: CoreSitePluginsBlockHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.logger = CoreLogger.getInstance('CoreSitePluginsBlockHandler');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getDisplayData(
|
||||
block: CoreCourseBlock,
|
||||
contextLevel: string,
|
||||
instanceId: number,
|
||||
): Promise<CoreBlockHandlerData> {
|
||||
const className = this.handlerSchema.displaydata?.class || 'block_' + block.name;
|
||||
let component: Type<unknown> | undefined;
|
||||
|
||||
if (this.handlerSchema.displaydata?.type == 'title') {
|
||||
component = CoreSitePluginsOnlyTitleBlockComponent;
|
||||
} else if (this.handlerSchema.displaydata?.type == 'prerendered') {
|
||||
component = CoreBlockPreRenderedComponent;
|
||||
} else if (this.handlerSchema.fallback && !this.handlerSchema.method) {
|
||||
// Try to use the fallback block.
|
||||
const originalName = block.name;
|
||||
block.name = this.handlerSchema.fallback;
|
||||
|
||||
try {
|
||||
const displayData = await CoreBlockDelegate.getBlockDisplayData(block, contextLevel, instanceId);
|
||||
|
||||
if (!displayData) {
|
||||
throw new CoreError('Cannot get display data for fallback block.');
|
||||
}
|
||||
|
||||
this.logger.debug(`Using fallback "${this.handlerSchema.fallback}" for block "${originalName}"`);
|
||||
component = displayData.component;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error using fallback "${this.handlerSchema.fallback}" for block "${originalName}", ` +
|
||||
'maybe it doesn\'t exist or isn\'t enabled.', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
component = CoreSitePluginsBlockComponent;
|
||||
}
|
||||
|
||||
return {
|
||||
title: this.title,
|
||||
class: className,
|
||||
component,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { CoreCourseFormatHandler } from '@features/course/services/format-delegate';
|
||||
import { CoreSitePluginsCourseFormatHandlerData } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
import { CoreSitePluginsCourseFormatComponent } from '../../components/course-format/course-format';
|
||||
|
||||
/**
|
||||
* Handler to support a course format using a site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandler implements CoreCourseFormatHandler {
|
||||
|
||||
constructor(name: string, public format: string, protected handlerSchema: CoreSitePluginsCourseFormatHandlerData) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
canViewAllSections(): boolean {
|
||||
return this.handlerSchema.canviewallsections ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
displayEnableDownload(): boolean {
|
||||
return this.handlerSchema.displayenabledownload ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
displaySectionSelector(): boolean {
|
||||
return this.handlerSchema.displaysectionselector ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getCourseFormatComponent(): Promise<Type<unknown> | undefined> {
|
||||
if (this.handlerSchema.method) {
|
||||
return CoreSitePluginsCourseFormatComponent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// (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 { Md5 } from 'ts-md5';
|
||||
|
||||
import {
|
||||
CoreCourseOptionsHandler,
|
||||
CoreCourseOptionsHandlerData,
|
||||
CoreCourseOptionsMenuHandlerData,
|
||||
} from '@features/course/services/course-options-delegate';
|
||||
import { CoreCourseAnyCourseDataWithOptions } from '@features/courses/services/courses';
|
||||
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
|
||||
import {
|
||||
CoreSitePlugins,
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsCourseOptionHandlerData,
|
||||
CoreSitePluginsPlugin,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in course options.
|
||||
*/
|
||||
export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandler implements CoreCourseOptionsHandler {
|
||||
|
||||
priority: number;
|
||||
isMenuHandler: boolean;
|
||||
|
||||
protected updatingDefer?: PromiseDefer<void>;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsCourseOptionHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.priority = handlerSchema.priority || 0;
|
||||
this.isMenuHandler = !!handlerSchema.ismenuhandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabledForCourse(courseId: number): Promise<boolean> {
|
||||
// Wait for "init" result to be updated.
|
||||
if (this.updatingDefer) {
|
||||
await this.updatingDefer.promise;
|
||||
}
|
||||
|
||||
return CoreSitePlugins.isHandlerEnabledForCourse(
|
||||
courseId,
|
||||
this.handlerSchema.restricttoenrolledcourses,
|
||||
this.initResult?.restrict,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getDisplayData(): CoreCourseOptionsHandlerData {
|
||||
return {
|
||||
title: this.title,
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
page: `siteplugins/${this.name}`,
|
||||
pageParams: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getMenuDisplayData(course: CoreCourseAnyCourseDataWithOptions): CoreCourseOptionsMenuHandlerData {
|
||||
const args = {
|
||||
courseid: course.id,
|
||||
};
|
||||
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));
|
||||
|
||||
return {
|
||||
title: this.title,
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
icon: this.handlerSchema.displaydata?.icon || '',
|
||||
page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/${hash}`,
|
||||
pageParams: {
|
||||
title: this.title,
|
||||
args,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
prefetch(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void> {
|
||||
const args = {
|
||||
courseid: course.id,
|
||||
};
|
||||
const component = this.plugin.component;
|
||||
|
||||
return CoreSitePlugins.prefetchFunctions(component, args, this.handlerSchema, course.id, undefined, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set init result.
|
||||
*
|
||||
* @param result Result to set.
|
||||
*/
|
||||
setInitResult(result: CoreSitePluginsContent | null): void {
|
||||
this.initResult = result;
|
||||
|
||||
this.updatingDefer?.resolve();
|
||||
delete this.updatingDefer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark init being updated.
|
||||
*/
|
||||
updatingInit(): void {
|
||||
this.updatingDefer = CoreUtils.promiseDefer();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// (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 { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
|
||||
import {
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsMainMenuHandlerData,
|
||||
CoreSitePluginsPlugin,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in the main menu.
|
||||
*/
|
||||
export class CoreSitePluginsMainMenuHandler extends CoreSitePluginsBaseHandler implements CoreMainMenuHandler {
|
||||
|
||||
priority: number;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsMainMenuHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.priority = handlerSchema.priority || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerData {
|
||||
return {
|
||||
title: this.title,
|
||||
icon: this.handlerSchema.displaydata?.icon || 'fas-question',
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/0`,
|
||||
pageParams: {
|
||||
title: this.title,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
onlyInMore: true,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// (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 { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerData } from '@features/mainmenu/services/home-delegate';
|
||||
import {
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsMainMenuHomeHandlerData,
|
||||
CoreSitePluginsPlugin,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in the main menu.
|
||||
*/
|
||||
export class CoreSitePluginsMainMenuHomeHandler extends CoreSitePluginsBaseHandler implements CoreMainMenuHomeHandler {
|
||||
|
||||
priority: number;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsMainMenuHomeHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.priority = handlerSchema.priority || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHomeHandlerData {
|
||||
return {
|
||||
title: this.title,
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/0`,
|
||||
pageParams: {
|
||||
title: this.title,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// (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 { AddonMessageOutputHandler, AddonMessageOutputHandlerData } from '@addons/messageoutput/services/messageoutput-delegate';
|
||||
import {
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsMessageOutputHandlerData,
|
||||
CoreSitePluginsPlugin,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a message output settings option.
|
||||
*/
|
||||
export class CoreSitePluginsMessageOutputHandler extends CoreSitePluginsBaseHandler implements AddonMessageOutputHandler {
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
public processorName: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsMessageOutputHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getDisplayData(): AddonMessageOutputHandlerData {
|
||||
return {
|
||||
priority: this.handlerSchema.priority || 0,
|
||||
label: this.title,
|
||||
icon: this.handlerSchema.displaydata?.icon || 'fas-question',
|
||||
page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/0`,
|
||||
pageParams: {
|
||||
title: this.title,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course';
|
||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||
import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index';
|
||||
import {
|
||||
CoreSitePlugins,
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsCourseModuleHandlerData,
|
||||
CoreSitePluginsPlugin,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to support a module using a site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler implements CoreCourseModuleHandler {
|
||||
|
||||
supportedFeatures?: Record<string, unknown>;
|
||||
supportsFeature?: (feature: string) => unknown;
|
||||
|
||||
protected logger: CoreLogger;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
public modName: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsCourseModuleHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.logger = CoreLogger.getInstance('CoreSitePluginsModuleHandler');
|
||||
this.supportedFeatures = handlerSchema.supportedfeatures;
|
||||
|
||||
if (initResult?.jsResult && initResult.jsResult.supportsFeature) {
|
||||
// The init result defines a function to check if a feature is supported, use it.
|
||||
this.supportsFeature = initResult.jsResult.supportsFeature.bind(initResult.jsResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getData(
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
sectionId?: number,
|
||||
forCoursePage?: boolean,
|
||||
): CoreCourseModuleHandlerData {
|
||||
const callMethod = forCoursePage && this.handlerSchema.coursepagemethod;
|
||||
|
||||
if ('noviewlink' in module && module.noviewlink && !callMethod) {
|
||||
// The module doesn't link to a new page (similar to label). Only display the description.
|
||||
const title = module.description;
|
||||
module.description = '';
|
||||
|
||||
return {
|
||||
icon: this.getIconSrc(),
|
||||
title: title || '',
|
||||
a11yTitle: '',
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
};
|
||||
}
|
||||
|
||||
const hasOffline = !!(this.handlerSchema.offlinefunctions && Object.keys(this.handlerSchema.offlinefunctions).length);
|
||||
const showDowloadButton = this.handlerSchema.downloadbutton;
|
||||
const handlerData: CoreCourseModuleHandlerData = {
|
||||
title: module.name,
|
||||
icon: this.getIconSrc(),
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
showDownloadButton: typeof showDowloadButton != 'undefined' ? showDowloadButton : hasOffline,
|
||||
};
|
||||
|
||||
if (this.handlerSchema.method) {
|
||||
// There is a method, add an action.
|
||||
handlerData.action = (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
options = options || {};
|
||||
options.params = {
|
||||
title: module.name,
|
||||
module,
|
||||
};
|
||||
|
||||
CoreNavigator.navigateToSitePath(`siteplugins/module/${courseId}/${module.id}`, options);
|
||||
};
|
||||
}
|
||||
|
||||
if (callMethod && module.visibleoncoursepage !== 0) {
|
||||
// Call the method to get the course page template.
|
||||
this.loadCoursePageTemplate(module, courseId, handlerData);
|
||||
}
|
||||
|
||||
return handlerData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and use template for course page.
|
||||
*
|
||||
* @param module Module.
|
||||
* @param courseId Course ID.
|
||||
* @param handlerData Handler data.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadCoursePageTemplate(
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
handlerData: CoreCourseModuleHandlerData,
|
||||
): Promise<void> {
|
||||
// Call the method to get the course page template.
|
||||
handlerData.loading = true;
|
||||
|
||||
const args = {
|
||||
courseid: courseId,
|
||||
cmid: module.id,
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await CoreSitePlugins.getContent(
|
||||
this.plugin.component,
|
||||
this.handlerSchema.coursepagemethod!,
|
||||
args,
|
||||
);
|
||||
|
||||
// Use the html returned.
|
||||
handlerData.title = result.templates[0]?.html ?? '';
|
||||
(<CoreCourseWSModule> module).description = '';
|
||||
} catch (error) {
|
||||
this.logger.error('Error calling course page method:', error);
|
||||
} finally {
|
||||
handlerData.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getIconSrc(): string | undefined {
|
||||
return this.handlerSchema.displaydata?.icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getMainComponent(): Promise<Type<unknown>> {
|
||||
return CoreSitePluginsModuleIndexComponent;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
// (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 { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
|
||||
import { CoreSitePlugins, CoreSitePluginsCourseModuleHandlerData } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreFilepool } from '@services/filepool';
|
||||
import { CoreFileSizeSum } from '@services/plugin-file-delegate';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
|
||||
/**
|
||||
* Handler to prefetch a module site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsModulePrefetchHandler extends CoreCourseActivityPrefetchHandlerBase {
|
||||
|
||||
protected isResource: boolean;
|
||||
|
||||
constructor(
|
||||
component: string,
|
||||
name: string,
|
||||
modName: string,
|
||||
protected handlerSchema: CoreSitePluginsCourseModuleHandlerData,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.component = component;
|
||||
this.name = name;
|
||||
this.modName = modName;
|
||||
this.isResource = !!handlerSchema.isresource;
|
||||
|
||||
if (handlerSchema.updatesnames) {
|
||||
try {
|
||||
this.updatesNames = new RegExp(handlerSchema.updatesnames);
|
||||
} catch (ex) {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
download(module: CoreCourseAnyModuleData, courseId: number, dirPath?: string): Promise<void> {
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
return this.prefetchPackage(
|
||||
module,
|
||||
courseId,
|
||||
this.downloadPrefetchPlugin.bind(this, module, courseId, false, dirPath, siteId),
|
||||
siteId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download or prefetch the plugin, downloading the files and calling the needed WS.
|
||||
*
|
||||
* @param module The module object returned by WS.
|
||||
* @param courseId Course ID.
|
||||
* @param prefetch True to prefetch, false to download right away.
|
||||
* @param dirPath Path of the directory where to store all the content files.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async downloadPrefetchPlugin(
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
prefetch: boolean,
|
||||
dirPath?: string,
|
||||
siteId?: string,
|
||||
): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
const args = {
|
||||
courseid: courseId,
|
||||
cmid: module.id,
|
||||
userid: site.getUserId(),
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
// Download the files (if any).
|
||||
this.downloadOrPrefetchFiles(site.getId(), module, courseId, prefetch, dirPath),
|
||||
|
||||
// Call all the offline functions.
|
||||
CoreSitePlugins.prefetchFunctions(
|
||||
this.component,
|
||||
args,
|
||||
this.handlerSchema,
|
||||
courseId,
|
||||
module,
|
||||
prefetch,
|
||||
dirPath,
|
||||
site,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download or prefetch the plugin files.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
* @param module The module object returned by WS.
|
||||
* @param courseId Course ID.
|
||||
* @param prefetch True to prefetch, false to download right away.
|
||||
* @param dirPath Path of the directory where to store all the content files.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async downloadOrPrefetchFiles(
|
||||
siteId: string,
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
prefetch: boolean,
|
||||
dirPath?: string,
|
||||
): Promise<void> {
|
||||
// Load module contents (ignore cache so we always have the latest data).
|
||||
await this.loadContents(module, courseId, true);
|
||||
|
||||
// Get the intro files.
|
||||
const introFiles = await this.getIntroFiles(module, courseId);
|
||||
|
||||
const contentFiles = this.getContentDownloadableFiles(module);
|
||||
|
||||
if (dirPath) {
|
||||
await Promise.all([
|
||||
// Download intro files in filepool root folder.
|
||||
CoreFilepool.downloadOrPrefetchFiles(siteId, introFiles, prefetch, false, this.component, module.id),
|
||||
|
||||
// Download content files inside dirPath.
|
||||
CoreFilepool.downloadOrPrefetchFiles(
|
||||
siteId,
|
||||
contentFiles,
|
||||
prefetch,
|
||||
false,
|
||||
this.component,
|
||||
module.id,
|
||||
dirPath,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
// No dirPath, download everything in filepool root folder.
|
||||
await CoreFilepool.downloadOrPrefetchFiles(
|
||||
siteId,
|
||||
introFiles.concat(contentFiles),
|
||||
prefetch,
|
||||
false,
|
||||
this.component,
|
||||
module.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getDownloadSize(): Promise<CoreFileSizeSum> {
|
||||
// In most cases, to calculate the size we'll have to do all the WS calls. Just return unknown size.
|
||||
return { size: -1, total: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async invalidateContent(moduleId: number, courseId: number): Promise<void> {
|
||||
const currentSite = CoreSites.getCurrentSite();
|
||||
if (!currentSite) {
|
||||
return;
|
||||
}
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
const siteId = currentSite.getId();
|
||||
const args = {
|
||||
courseid: courseId,
|
||||
cmid: moduleId,
|
||||
userid: currentSite.getUserId(),
|
||||
};
|
||||
|
||||
// Invalidate files and the module.
|
||||
promises.push(CoreFilepool.invalidateFilesByComponent(siteId, this.component, moduleId));
|
||||
promises.push(CoreCourse.invalidateModule(moduleId, siteId));
|
||||
|
||||
// Also invalidate all the WS calls.
|
||||
for (const method in this.handlerSchema.offlinefunctions) {
|
||||
if (currentSite.wsAvailable(method)) {
|
||||
// The method is a WS.
|
||||
promises.push(currentSite.invalidateWsCacheForKey(CoreSitePlugins.getCallWSCacheKey(method, args)));
|
||||
} else {
|
||||
// It's a method to get content.
|
||||
promises.push(CoreSitePlugins.invalidateContent(this.component, method, args, siteId));
|
||||
}
|
||||
}
|
||||
|
||||
return CoreUtils.allPromises(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async loadContents(module: CoreCourseAnyModuleData, courseId: number, ignoreCache?: boolean): Promise<void> {
|
||||
if (this.isResource) {
|
||||
return CoreCourse.loadModuleContents(module, courseId, undefined, false, ignoreCache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
prefetch(module: CoreCourseAnyModuleData, courseId?: number, single?: boolean, dirPath?: string): Promise<void> {
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
return this.prefetchPackage(
|
||||
module,
|
||||
courseId,
|
||||
this.downloadPrefetchPlugin.bind(this, module, courseId, true, dirPath, siteId),
|
||||
siteId,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { CoreQuestionBehaviourBaseHandler } from '@features/question/classes/base-behaviour-handler';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from '@features/siteplugins/components/question-behaviour/question-behaviour';
|
||||
|
||||
/**
|
||||
* Handler to display a question behaviour site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsQuestionBehaviourHandler extends CoreQuestionBehaviourBaseHandler {
|
||||
|
||||
constructor(public name: string, public type: string, public hasTemplate: boolean) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
handleQuestion(): undefined | Type<unknown>[] {
|
||||
if (this.hasTemplate) {
|
||||
return [CoreSitePluginsQuestionBehaviourComponent];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { CoreQuestionBaseHandler } from '@features/question/classes/base-question-handler';
|
||||
import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question';
|
||||
|
||||
/**
|
||||
* Handler to display a question site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsQuestionHandler extends CoreQuestionBaseHandler {
|
||||
|
||||
constructor(public name: string, public type: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getComponent(): Type<unknown> {
|
||||
return CoreSitePluginsQuestionComponent;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||
import { CoreSitePluginsQuizAccessRuleComponent } from '../../components/quiz-access-rule/quiz-access-rule';
|
||||
|
||||
/**
|
||||
* Handler to display a quiz access rule site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsQuizAccessRuleHandler implements AddonModQuizAccessRuleHandler {
|
||||
|
||||
constructor(public name: string, public ruleName: string, public hasTemplate: boolean) { }
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
isPreflightCheckRequired(): boolean {
|
||||
return this.hasTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getFixedPreflightData(): void {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getPreflightComponent(): undefined | Type<unknown> {
|
||||
if (this.hasTemplate) {
|
||||
return CoreSitePluginsQuizAccessRuleComponent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
notifyPreflightCheckPassed(): void {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
notifyPreflightCheckFailed(): void {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
shouldShowTimeLeft(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// (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 { CoreSettingsHandler, CoreSettingsHandlerData } from '@features/settings/services/settings-delegate';
|
||||
import {
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsPlugin,
|
||||
CoreSitePluginsSettingsHandlerData,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in the settings.
|
||||
*/
|
||||
export class CoreSitePluginsSettingsHandler extends CoreSitePluginsBaseHandler implements CoreSettingsHandler {
|
||||
|
||||
priority: number;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsSettingsHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.priority = handlerSchema.priority || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return Data.
|
||||
*/
|
||||
getDisplayData(): CoreSettingsHandlerData {
|
||||
return {
|
||||
title: this.title,
|
||||
icon: this.handlerSchema.displaydata?.icon,
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/0`,
|
||||
params: {
|
||||
title: this.title,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// (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 {
|
||||
CoreSitePlugins,
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsPlugin,
|
||||
CoreSitePluginsUserHandlerData,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreUserProfile } from '@features/user/services/user';
|
||||
import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
|
||||
import { Md5 } from 'ts-md5';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in the user profile.
|
||||
*/
|
||||
export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandler implements CoreUserProfileHandler {
|
||||
|
||||
priority: number;
|
||||
type: string;
|
||||
|
||||
protected updatingDefer?: PromiseDefer<void>;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
protected title: string,
|
||||
protected plugin: CoreSitePluginsPlugin,
|
||||
protected handlerSchema: CoreSitePluginsUserHandlerData,
|
||||
protected initResult: CoreSitePluginsContent | null,
|
||||
) {
|
||||
super(name);
|
||||
|
||||
this.priority = handlerSchema.priority || 0;
|
||||
|
||||
// Only support TYPE_COMMUNICATION and TYPE_NEW_PAGE.
|
||||
this.type = handlerSchema.type != CoreUserDelegateService.TYPE_COMMUNICATION ?
|
||||
CoreUserDelegateService.TYPE_NEW_PAGE : CoreUserDelegateService.TYPE_COMMUNICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabledForUser(
|
||||
user: CoreUserProfile,
|
||||
courseId?: number,
|
||||
): Promise<boolean> {
|
||||
// First check if it's enabled for the user.
|
||||
const enabledForUser = CoreSitePlugins.isHandlerEnabledForUser(
|
||||
user.id,
|
||||
this.handlerSchema.restricttocurrentuser,
|
||||
this.initResult?.restrict,
|
||||
);
|
||||
|
||||
if (!enabledForUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
courseId = courseId || CoreSites.getCurrentSiteHomeId();
|
||||
|
||||
// Enabled for user, check if it's enabled for the course.
|
||||
return CoreSitePlugins.isHandlerEnabledForCourse(
|
||||
courseId,
|
||||
this.handlerSchema.restricttoenrolledcourses,
|
||||
this.initResult?.restrict,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getDisplayData(): CoreUserProfileHandlerData {
|
||||
return {
|
||||
title: this.title,
|
||||
icon: this.handlerSchema.displaydata?.icon,
|
||||
class: this.handlerSchema.displaydata?.class,
|
||||
action: (event: Event, user: CoreUserProfile, courseId?: number): void => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const args = {
|
||||
courseid: courseId,
|
||||
userid: user.id,
|
||||
};
|
||||
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));
|
||||
|
||||
CoreNavigator.navigateToSitePath(
|
||||
`siteplugins/${this.plugin.component}/${this.handlerSchema.method}/${hash}`,
|
||||
{
|
||||
params: {
|
||||
title: this.title,
|
||||
args,
|
||||
initResult: this.initResult,
|
||||
ptrEnabled: this.handlerSchema.ptrenabled,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set init result.
|
||||
*
|
||||
* @param result Result to set.
|
||||
*/
|
||||
setInitResult(result: CoreSitePluginsContent | null): void {
|
||||
this.initResult = result;
|
||||
|
||||
this.updatingDefer?.resolve();
|
||||
delete this.updatingDefer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark init being updated.
|
||||
*/
|
||||
updatingInit(): void {
|
||||
this.updatingDefer = CoreUtils.promiseDefer();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// (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 { Type } from '@angular/core';
|
||||
|
||||
import { AuthEmailSignupProfileField } from '@features/login/services/login-helper';
|
||||
import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field';
|
||||
import { CoreUserProfileField } from '@features/user/services/user';
|
||||
import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@features/user/services/user-profile-field-delegate';
|
||||
import { CoreSitePluginsBaseHandler } from './base-handler';
|
||||
|
||||
/**
|
||||
* Handler to display a site plugin in the user profile.
|
||||
*/
|
||||
export class CoreSitePluginsUserProfileFieldHandler extends CoreSitePluginsBaseHandler implements CoreUserProfileFieldHandler {
|
||||
|
||||
constructor(name: string, public type: string) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getComponent(): Type<unknown> {
|
||||
return CoreSitePluginsUserProfileFieldComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getData(
|
||||
field: AuthEmailSignupProfileField | CoreUserProfileField,
|
||||
signup: boolean,
|
||||
registerAuth: string,
|
||||
formValues: Record<string, unknown>,
|
||||
): Promise<CoreUserProfileFieldHandlerData> {
|
||||
// No getData function implemented, use a default behaviour.
|
||||
const name = 'profile_field_' + field.shortname;
|
||||
|
||||
return {
|
||||
type: 'type' in field ? field.type : field.datatype!,
|
||||
name: name,
|
||||
value: formValues[name],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// (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 { Component, OnInit, Input } from '@angular/core';
|
||||
|
||||
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '@addons/mod/assign/services/assign';
|
||||
import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feedback-delegate';
|
||||
import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component';
|
||||
|
||||
/**
|
||||
* Component that displays an assign feedback plugin created using a site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-assign-feedback',
|
||||
templateUrl: 'core-siteplugins-assign-feedback.html',
|
||||
})
|
||||
export class CoreSitePluginsAssignFeedbackComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
|
||||
@Input() assign!: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission!: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() userId!: number; // The user ID of the submission.
|
||||
@Input() canEdit = false; // Whether the user can edit.
|
||||
@Input() edit = false; // Whether the user is editing.
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Pass the input and output data to the component.
|
||||
this.jsData.assign = this.assign;
|
||||
this.jsData.submission = this.submission;
|
||||
this.jsData.plugin = this.plugin;
|
||||
this.jsData.userId = this.userId;
|
||||
this.jsData.edit = this.edit;
|
||||
this.jsData.canEdit = this.canEdit;
|
||||
|
||||
if (this.plugin) {
|
||||
this.getHandlerData(AddonModAssignFeedbackDelegate.getHandlerName(this.plugin.type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
invalidate(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
|
@ -0,0 +1,61 @@
|
|||
// (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 { Component, OnInit, Input } from '@angular/core';
|
||||
|
||||
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '@addons/mod/assign/services/assign';
|
||||
import { AddonModAssignSubmissionDelegate } from '@addons/mod/assign/services/submission-delegate';
|
||||
import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component';
|
||||
|
||||
/**
|
||||
* Component that displays an assign submission plugin created using a site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-assign-submission',
|
||||
templateUrl: 'core-siteplugins-assign-submission.html',
|
||||
})
|
||||
export class CoreSitePluginsAssignSubmissionComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
|
||||
@Input() assign!: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission!: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() edit = false; // Whether the user is editing.
|
||||
@Input() allowOffline = false; // Whether to allow offline.
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Pass the input and output data to the component.
|
||||
this.jsData.assign = this.assign;
|
||||
this.jsData.submission = this.submission;
|
||||
this.jsData.plugin = this.plugin;
|
||||
this.jsData.edit = this.edit;
|
||||
this.jsData.allowOffline = this.allowOffline;
|
||||
|
||||
if (this.plugin) {
|
||||
this.getHandlerData(AddonModAssignSubmissionDelegate.getHandlerName(this.plugin.type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
invalidate(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
|
@ -0,0 +1,80 @@
|
|||
// (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 { Component, OnChanges, ViewChild } from '@angular/core';
|
||||
|
||||
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
|
||||
import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content';
|
||||
|
||||
/**
|
||||
* Component that displays the index of a course format site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-block',
|
||||
templateUrl: 'core-siteplugins-block.html',
|
||||
})
|
||||
export class CoreSitePluginsBlockComponent extends CoreBlockBaseComponent implements OnChanges {
|
||||
|
||||
@ViewChild(CoreSitePluginsPluginContentComponent) content?: CoreSitePluginsPluginContentComponent;
|
||||
|
||||
component?: string;
|
||||
method?: string;
|
||||
args?: Record<string, unknown>;
|
||||
initResult?: CoreSitePluginsContent | null;
|
||||
|
||||
constructor() {
|
||||
super('CoreSitePluginsBlockComponent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(): void {
|
||||
if (this.component) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
const handlerName = CoreBlockDelegate.getHandlerName(this.block.name);
|
||||
const handler = CoreSitePlugins.getSitePluginHandler(handlerName);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.component = handler.plugin.component;
|
||||
this.method = handler.handlerSchema.method;
|
||||
this.args = {
|
||||
contextlevel: this.contextLevel,
|
||||
instanceid: this.instanceId,
|
||||
blockid: this.block.instanceid,
|
||||
};
|
||||
this.initResult = handler.initResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate block data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
if (!this.component || !this.method) {
|
||||
return;
|
||||
}
|
||||
|
||||
return CoreSitePlugins.invalidateContent(this.component, this.method, this.args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args"
|
||||
[initResult]="initResult">
|
||||
</core-site-plugins-plugin-content>
|
|
@ -0,0 +1,68 @@
|
|||
// (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 { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module';
|
||||
import { CoreSitePluginsPluginContentComponent } from './plugin-content/plugin-content';
|
||||
import { CoreSitePluginsModuleIndexComponent } from './module-index/module-index';
|
||||
import { CoreSitePluginsCourseFormatComponent } from './course-format/course-format';
|
||||
import { CoreSitePluginsUserProfileFieldComponent } from './user-profile-field/user-profile-field';
|
||||
import { CoreSitePluginsQuestionComponent } from './question/question';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from './question-behaviour/question-behaviour';
|
||||
import { CoreSitePluginsQuizAccessRuleComponent } from './quiz-access-rule/quiz-access-rule';
|
||||
import { CoreSitePluginsAssignFeedbackComponent } from './assign-feedback/assign-feedback';
|
||||
import { CoreSitePluginsAssignSubmissionComponent } from './assign-submission/assign-submission';
|
||||
// @todo
|
||||
// import { CoreSitePluginsWorkshopAssessmentStrategyComponent } from './workshop-assessment-strategy/workshop-assessment-strategy';
|
||||
import { CoreSitePluginsBlockComponent } from './block/block';
|
||||
import { CoreSitePluginsOnlyTitleBlockComponent } from './only-title-block/only-title-block';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreSitePluginsPluginContentComponent,
|
||||
CoreSitePluginsModuleIndexComponent,
|
||||
CoreSitePluginsBlockComponent,
|
||||
CoreSitePluginsOnlyTitleBlockComponent,
|
||||
CoreSitePluginsCourseFormatComponent,
|
||||
CoreSitePluginsUserProfileFieldComponent,
|
||||
CoreSitePluginsQuestionComponent,
|
||||
CoreSitePluginsQuestionBehaviourComponent,
|
||||
CoreSitePluginsQuizAccessRuleComponent,
|
||||
CoreSitePluginsAssignFeedbackComponent,
|
||||
CoreSitePluginsAssignSubmissionComponent,
|
||||
// @todo CoreSitePluginsWorkshopAssessmentStrategyComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreSharedModule,
|
||||
CoreCompileHtmlComponentModule,
|
||||
],
|
||||
providers: [],
|
||||
exports: [
|
||||
CoreSitePluginsPluginContentComponent,
|
||||
CoreSitePluginsModuleIndexComponent,
|
||||
CoreSitePluginsBlockComponent,
|
||||
CoreSitePluginsOnlyTitleBlockComponent,
|
||||
CoreSitePluginsCourseFormatComponent,
|
||||
CoreSitePluginsUserProfileFieldComponent,
|
||||
CoreSitePluginsQuestionComponent,
|
||||
CoreSitePluginsQuestionBehaviourComponent,
|
||||
CoreSitePluginsQuizAccessRuleComponent,
|
||||
CoreSitePluginsAssignFeedbackComponent,
|
||||
CoreSitePluginsAssignSubmissionComponent,
|
||||
// @todo CoreSitePluginsWorkshopAssessmentStrategyComponent,
|
||||
],
|
||||
})
|
||||
export class CoreSitePluginsComponentsModule {}
|
|
@ -0,0 +1,3 @@
|
|||
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args"
|
||||
[initResult]="initResult" [data]="data">
|
||||
</core-site-plugins-plugin-content>
|
|
@ -0,0 +1,104 @@
|
|||
// (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 { Component, OnChanges, Input, ViewChild, Output, EventEmitter } from '@angular/core';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
|
||||
import { CoreCourseFormatComponent } from '@features/course/components/format/format';
|
||||
import { CoreCourseModuleCompletionData, CoreCourseSectionWithStatus } from '@features/course/services/course-helper';
|
||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||
import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content';
|
||||
|
||||
/**
|
||||
* Component that displays the index of a course format site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-course-format',
|
||||
templateUrl: 'core-siteplugins-course-format.html',
|
||||
})
|
||||
export class CoreSitePluginsCourseFormatComponent implements OnChanges {
|
||||
|
||||
@Input() course?: CoreCourseAnyCourseData; // The course to render.
|
||||
@Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections. The status will be calculated in this component.
|
||||
@Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
|
||||
@Input() initialSectionId?: number; // The section to load first (by ID).
|
||||
@Input() initialSectionNumber?: number; // The section to load first (by number).
|
||||
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
|
||||
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when any module completion changes.
|
||||
|
||||
// Special input, allows access to the parent instance properties and methods.
|
||||
// Please notice that all the other inputs/outputs are also accessible through this instance, so they could be removed.
|
||||
// However, we decided to keep them to support ngOnChanges and to make templates easier to read.
|
||||
@Input() coreCourseFormatComponent?: CoreCourseFormatComponent;
|
||||
|
||||
@ViewChild(CoreSitePluginsPluginContentComponent) content?: CoreSitePluginsPluginContentComponent;
|
||||
|
||||
component?: string;
|
||||
method?: string;
|
||||
args?: Record<string, unknown>;
|
||||
initResult?: CoreSitePluginsContent | null;
|
||||
data?: Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(): void {
|
||||
if (!this.course || !this.course.format) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.component) {
|
||||
// Initialize the data.
|
||||
const handlerName = CoreCourseFormatDelegate.getHandlerName(this.course.format);
|
||||
const handler = CoreSitePlugins.getSitePluginHandler(handlerName);
|
||||
|
||||
if (handler) {
|
||||
this.component = handler.plugin.component;
|
||||
this.method = handler.handlerSchema.method;
|
||||
this.args = {
|
||||
courseid: this.course.id,
|
||||
downloadenabled: this.downloadEnabled,
|
||||
};
|
||||
this.initResult = handler.initResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass input data to the component.
|
||||
this.data = {
|
||||
course: this.course,
|
||||
sections: this.sections,
|
||||
downloadEnabled: this.downloadEnabled,
|
||||
initialSectionId: this.initialSectionId,
|
||||
initialSectionNumber: this.initialSectionNumber,
|
||||
moduleId: this.moduleId,
|
||||
completionChanged: this.completionChanged,
|
||||
coreCourseFormatComponent: this.coreCourseFormatComponent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
* @param done Function to call when done.
|
||||
* @param afterCompletionChange Whether the refresh is due to a completion change.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, afterCompletionChange?: boolean): Promise<void> {
|
||||
await this.content?.refreshContent(afterCompletionChange);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<!-- Buttons to add to the header. -->
|
||||
<core-navbar-buttons slot="end">
|
||||
<core-context-menu>
|
||||
<core-context-menu-item [hidden]="!displayOpenInBrowser || !externalUrl || (
|
||||
content?.compileComponent?.componentInstance?.displayOpenInBrowser === false)" [priority]="900"
|
||||
[content]="'core.openinbrowser' | translate" [href]="externalUrl" iconAction="fas-external-link-alt">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!displayDescription || !description || (
|
||||
content?.compileComponent?.componentInstance?.displayDescription === false)" [priority]="800"
|
||||
[content]="'core.moduleintro' | translate" (action)="expandDescription()" iconAction="fas-arrow-right">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!displayRefresh || (
|
||||
content?.compileComponent?.componentInstance?.displayRefresh === false)" [priority]="700"
|
||||
[content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon"
|
||||
[closeOnClick]="false">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!displayPrefetch || !prefetchStatusIcon || (
|
||||
content?.compileComponent?.componentInstance?.displayPrefetch === false)" [priority]="600" [content]="prefetchText"
|
||||
(action)="prefetch()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!displaySize || !size || (
|
||||
content?.compileComponent?.componentInstance?.displaySize === false)" [priority]="500"
|
||||
[content]="'core.clearstoreddata' | translate:{$a: size}" [iconDescription]="'cube'" (action)="removeFiles()"
|
||||
iconAction="fas-trash" [closeOnClick]="false">
|
||||
</core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args"
|
||||
[initResult]="initResult" [data]="jsData" [pageTitle]="pageTitle" [preSets]="preSets" (onContentLoaded)="contentLoaded($event)"
|
||||
(onLoadingContent)="contentLoading()">
|
||||
</core-site-plugins-plugin-content>
|
|
@ -0,0 +1,205 @@
|
|||
// (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 { CoreConstants } from '@/core/constants';
|
||||
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
|
||||
|
||||
import { CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreCourseHelper, CoreCourseModule } from '@features/course/services/course-helper';
|
||||
import {
|
||||
CoreCourseModuleDelegate,
|
||||
CoreCourseModuleMainComponent,
|
||||
} from '@features/course/services/module-delegate';
|
||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||
import {
|
||||
CoreSitePlugins,
|
||||
CoreSitePluginsContent,
|
||||
CoreSitePluginsCourseModuleHandlerData,
|
||||
} from '@features/siteplugins/services/siteplugins';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content';
|
||||
|
||||
/**
|
||||
* Component that displays the index of a module site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-module-index',
|
||||
templateUrl: 'core-siteplugins-module-index.html',
|
||||
})
|
||||
export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent {
|
||||
|
||||
@Input() module!: CoreCourseModule; // The module.
|
||||
@Input() courseId!: number; // Course ID the module belongs to.
|
||||
@Input() pageTitle?: string; // Current page title. It can be used by the "new-content" directives.
|
||||
|
||||
@ViewChild(CoreSitePluginsPluginContentComponent) content?: CoreSitePluginsPluginContentComponent;
|
||||
|
||||
component?: string;
|
||||
method?: string;
|
||||
args?: Record<string, unknown>;
|
||||
initResult?: CoreSitePluginsContent | null;
|
||||
preSets?: CoreSiteWSPreSets;
|
||||
|
||||
// Data for context menu.
|
||||
externalUrl?: string;
|
||||
description?: string;
|
||||
refreshIcon?: string;
|
||||
prefetchStatus?: string;
|
||||
prefetchStatusIcon?: string;
|
||||
prefetchText?: string;
|
||||
size?: string;
|
||||
contextMenuStatusObserver?: CoreEventObserver;
|
||||
contextFileStatusObserver?: CoreEventObserver;
|
||||
displayOpenInBrowser = true;
|
||||
displayDescription = true;
|
||||
displayRefresh = true;
|
||||
displayPrefetch = true;
|
||||
displaySize = true;
|
||||
ptrEnabled = true;
|
||||
isDestroyed = false;
|
||||
|
||||
jsData?: Record<string, unknown>; // Data to pass to the component.
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.refreshIcon = 'spinner';
|
||||
|
||||
if (!this.module) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlerName = CoreCourseModuleDelegate.getHandlerName(this.module.modname);
|
||||
const handler = CoreSitePlugins.getSitePluginHandler(handlerName);
|
||||
|
||||
if (handler) {
|
||||
this.component = handler.plugin.component;
|
||||
this.preSets = { componentId: this.module.id };
|
||||
this.method = handler.handlerSchema.method;
|
||||
this.args = {
|
||||
courseid: this.courseId,
|
||||
cmid: this.module.id,
|
||||
};
|
||||
this.initResult = handler.initResult;
|
||||
this.jsData = {
|
||||
module: this.module,
|
||||
courseId: this.courseId,
|
||||
};
|
||||
|
||||
const handlerSchema = <CoreSitePluginsCourseModuleHandlerData> handler.handlerSchema;
|
||||
|
||||
this.displayOpenInBrowser = !CoreUtils.isFalseOrZero(handlerSchema.displayopeninbrowser);
|
||||
this.displayDescription = !CoreUtils.isFalseOrZero(handlerSchema.displaydescription);
|
||||
this.displayRefresh = !CoreUtils.isFalseOrZero(handlerSchema.displayrefresh);
|
||||
this.displayPrefetch = !CoreUtils.isFalseOrZero(handlerSchema.displayprefetch);
|
||||
this.displaySize = !CoreUtils.isFalseOrZero(handlerSchema.displaysize);
|
||||
this.ptrEnabled = !CoreUtils.isFalseOrZero(handlerSchema.ptrenabled);
|
||||
}
|
||||
|
||||
// Get the data for the context menu.
|
||||
this.description = this.module.description;
|
||||
this.externalUrl = this.module.url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
* @param done Function to call when done.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher> | null, done?: () => void): Promise<void> {
|
||||
if (this.content) {
|
||||
this.refreshIcon = CoreConstants.ICON_LOADING;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.content?.refreshContent(false);
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
done && done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when the data of the site plugin content is loaded.
|
||||
*/
|
||||
contentLoaded(refresh: boolean): void {
|
||||
this.refreshIcon = CoreConstants.ICON_REFRESH;
|
||||
|
||||
// Check if there is a prefetch handler for this type of module.
|
||||
if (CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module)) {
|
||||
CoreCourseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when starting to load the data of the site plugin content.
|
||||
*/
|
||||
contentLoading(): void {
|
||||
this.refreshIcon = CoreConstants.ICON_LOADING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the description.
|
||||
*/
|
||||
expandDescription(): void {
|
||||
CoreTextUtils.viewText(Translate.instant('core.description'), this.description!, {
|
||||
component: this.component,
|
||||
componentId: this.module.id,
|
||||
filter: true,
|
||||
contextLevel: 'module',
|
||||
instanceId: this.module.id,
|
||||
courseId: this.courseId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch the module.
|
||||
*/
|
||||
prefetch(): void {
|
||||
CoreCourseHelper.contextMenuPrefetch(this, this.module, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm and remove downloaded files.
|
||||
*/
|
||||
removeFiles(): void {
|
||||
CoreCourseHelper.confirmAndRemoveFiles(this.module, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a certain function on the component instance.
|
||||
*
|
||||
* @param name Name of the function to call.
|
||||
* @param params List of params to send to the function.
|
||||
* @return Result of the call. Undefined if no component instance or the function doesn't exist.
|
||||
*/
|
||||
callComponentFunction(name: string, params?: unknown[]): unknown | undefined {
|
||||
return this.content?.callComponentFunction(name, params);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<ion-item-divider class="ion-text-wrap" detail="true" (click)="gotoBlock()">
|
||||
<ion-label>
|
||||
<h2>{{ title | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
|
@ -0,0 +1,75 @@
|
|||
// (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 { OnInit, Component } from '@angular/core';
|
||||
import { Md5 } from 'ts-md5';
|
||||
|
||||
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
|
||||
import { CoreSitePlugins, CoreSitePluginsUserHandlerData } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
|
||||
/**
|
||||
* Component to render blocks with only a title and link.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-siteplugins-only-title-block',
|
||||
templateUrl: 'core-siteplugins-only-title-block.html',
|
||||
})
|
||||
export class CoreSitePluginsOnlyTitleBlockComponent extends CoreBlockBaseComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
super('CoreSitePluginsOnlyTitleBlockComponent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
super.ngOnInit();
|
||||
|
||||
this.fetchContentDefaultError = 'Error getting ' + (this.block.contents?.title || 'block') + ' data.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the block page.
|
||||
*/
|
||||
gotoBlock(): void {
|
||||
const handlerName = CoreBlockDelegate.getHandlerName(this.block.name);
|
||||
const handler = CoreSitePlugins.getSitePluginHandler(handlerName);
|
||||
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
const args = {
|
||||
contextlevel: this.contextLevel,
|
||||
instanceid: this.instanceId,
|
||||
};
|
||||
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));
|
||||
|
||||
CoreNavigator.navigateToSitePath(
|
||||
`siteplugins/${handler.plugin.component}/${handler.handlerSchema.method}/${hash}`,
|
||||
{
|
||||
params: {
|
||||
title: this.title,
|
||||
args,
|
||||
initResult: handler.initResult,
|
||||
ptrEnabled: (<CoreSitePluginsUserHandlerData> handler.handlerSchema).ptrenabled,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<core-loading [hideUntil]="dataLoaded" class="core-loading-center">
|
||||
<core-compile-html [text]="content" [javascript]="javascript" [jsData]="jsData" [forceCompile]="forceCompile" #compile>
|
||||
</core-compile-html>
|
||||
</core-loading>
|
|
@ -0,0 +1,226 @@
|
|||
// (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 { Component, OnInit, Input, Output, EventEmitter, DoCheck, KeyValueDiffers, ViewChild, KeyValueDiffer } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Md5 } from 'ts-md5';
|
||||
|
||||
import { CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreCompileHtmlComponent } from '@features/compile/components/compile-html/compile-html';
|
||||
import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
||||
/**
|
||||
* Component to render a site plugin content.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-plugin-content',
|
||||
templateUrl: 'core-siteplugins-plugin-content.html',
|
||||
})
|
||||
export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
|
||||
|
||||
// Get the compile element. Don't set the right type to prevent circular dependencies.
|
||||
@ViewChild('compile') compileComponent?: CoreCompileHtmlComponent;
|
||||
|
||||
@Input() component!: string;
|
||||
@Input() method!: string;
|
||||
@Input() args?: Record<string, unknown>;
|
||||
@Input() initResult?: CoreSitePluginsContent | null; // Result of the init WS call of the handler.
|
||||
@Input() data?: Record<string, unknown>; // Data to pass to the component.
|
||||
@Input() preSets?: CoreSiteWSPreSets; // The preSets for the WS call.
|
||||
@Input() pageTitle?: string; // Current page title. It can be used by the "new-content" directives.
|
||||
@Output() onContentLoaded = new EventEmitter<boolean>(); // Emits an event when the content is loaded.
|
||||
@Output() onLoadingContent = new EventEmitter<boolean>(); // Emits an event when starts to load the content.
|
||||
|
||||
content?: string; // Content.
|
||||
javascript?: string; // Javascript to execute.
|
||||
otherData?: Record<string, unknown>; // Other data of the content.
|
||||
dataLoaded = false;
|
||||
invalidateObservable = new Subject<void>(); // An observable to notify observers when to invalidate data.
|
||||
jsData?: Record<string, unknown>; // Data to pass to the component.
|
||||
forceCompile?: boolean; // Force compilation on PTR.
|
||||
|
||||
protected differ: KeyValueDiffer<unknown, unknown>; // To detect changes in the data input.
|
||||
|
||||
constructor(differs: KeyValueDiffers) {
|
||||
this.differ = differs.find([]).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.fetchContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
|
||||
*/
|
||||
ngDoCheck(): void {
|
||||
if (!this.data || !this.jsData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's any change in the data object.
|
||||
const changes = this.differ.diff(this.data);
|
||||
if (changes) {
|
||||
this.jsData = Object.assign(this.jsData, this.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the content to render.
|
||||
*
|
||||
* @param refresh Whether the user is refreshing.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async fetchContent(refresh?: boolean): Promise<void> {
|
||||
this.onLoadingContent.emit(refresh);
|
||||
|
||||
this.forceCompile = false;
|
||||
|
||||
const preSets = Object.assign({}, this.preSets);
|
||||
preSets.component = preSets.component || this.component;
|
||||
|
||||
try {
|
||||
const result = await CoreSitePlugins.getContent(this.component, this.method, this.args, preSets);
|
||||
|
||||
this.content = result.templates.length ? result.templates[0].html : ''; // Load first template.
|
||||
this.javascript = result.javascript;
|
||||
this.otherData = result.otherdata;
|
||||
this.data = this.data || {};
|
||||
this.forceCompile = true;
|
||||
|
||||
this.jsData = Object.assign(this.data, CoreSitePlugins.createDataForJS(this.initResult, result));
|
||||
|
||||
// Pass some methods as jsData so they can be called from the template too.
|
||||
this.jsData.fetchContent = this.fetchContent.bind(this);
|
||||
this.jsData.openContent = this.openContent.bind(this);
|
||||
this.jsData.refreshContent = this.refreshContent.bind(this);
|
||||
this.jsData.updateContent = this.updateContent.bind(this);
|
||||
|
||||
this.onContentLoaded.emit(refresh);
|
||||
} catch (error) {
|
||||
// Make it think it's loaded - otherwise it sticks on 'loading' and stops navigation working.
|
||||
this.content = '<div></div>';
|
||||
this.onContentLoaded.emit(refresh);
|
||||
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
||||
} finally {
|
||||
this.dataLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new page with a new content.
|
||||
*
|
||||
* @param title The title to display with the new content.
|
||||
* @param args New params.
|
||||
* @param component New component. If not provided, current component
|
||||
* @param method New method. If not provided, current method
|
||||
* @param jsData JS variables to pass to the new view so they can be used in the template or JS.
|
||||
* If true is supplied instead of an object, all initial variables from current page will be copied.
|
||||
* @param preSets The preSets for the WS call of the new content.
|
||||
* @param ptrEnabled Whether PTR should be enabled in the new page. Defaults to true.
|
||||
*/
|
||||
openContent(
|
||||
title: string,
|
||||
args?: Record<string, unknown>,
|
||||
component?: string,
|
||||
method?: string,
|
||||
jsData?: Record<string, unknown> | boolean,
|
||||
preSets?: CoreSiteWSPreSets,
|
||||
ptrEnabled?: boolean,
|
||||
): void {
|
||||
if (jsData === true) {
|
||||
jsData = this.data;
|
||||
}
|
||||
|
||||
component = component || this.component;
|
||||
method = method || this.method;
|
||||
args = args || {};
|
||||
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));
|
||||
|
||||
CoreNavigator.navigateToSitePath(`siteplugins/${component}/${method}/${hash}`, {
|
||||
params: {
|
||||
title,
|
||||
args,
|
||||
initResult: this.initResult,
|
||||
jsData,
|
||||
preSets,
|
||||
ptrEnabled,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
*
|
||||
* @param showSpinner Whether to show spinner while refreshing.
|
||||
*/
|
||||
async refreshContent(showSpinner: boolean = true): Promise<void> {
|
||||
if (showSpinner) {
|
||||
this.dataLoaded = false;
|
||||
}
|
||||
|
||||
this.invalidateObservable.next(); // Notify observers.
|
||||
|
||||
try {
|
||||
await CoreSitePlugins.invalidateContent(this.component, this.method, this.args);
|
||||
} finally {
|
||||
await this.fetchContent(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the content, usually with a different method or params.
|
||||
*
|
||||
* @param args New params.
|
||||
* @param component New component. If not provided, current component
|
||||
* @param method New method. If not provided, current method
|
||||
* @param jsData JS variables to pass to the new view so they can be used in the template or JS.
|
||||
* @param preSets New preSets to use. If not provided, use current preSets.
|
||||
*/
|
||||
updateContent(
|
||||
args?: Record<string, unknown>,
|
||||
component?: string,
|
||||
method?: string,
|
||||
jsData?: Record<string, unknown>,
|
||||
preSets?: CoreSiteWSPreSets,
|
||||
): void {
|
||||
this.component = component || this.component;
|
||||
this.method = method || this.method;
|
||||
this.args = args;
|
||||
this.dataLoaded = false;
|
||||
this.preSets = preSets || this.preSets;
|
||||
if (jsData) {
|
||||
Object.assign(this.data, jsData);
|
||||
}
|
||||
|
||||
this.fetchContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a certain function on the component instance.
|
||||
*
|
||||
* @param name Name of the function to call.
|
||||
* @param params List of params to send to the function.
|
||||
* @return Result of the call. Undefined if no component instance or the function doesn't exist.
|
||||
*/
|
||||
callComponentFunction(name: string, params?: unknown[]): unknown | undefined {
|
||||
return this.compileComponent?.callComponentFunction(name, params);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
|
@ -0,0 +1,68 @@
|
|||
// (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 { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
|
||||
import { CoreQuestionBehaviourDelegate } from '@features/question/services/behaviour-delegate';
|
||||
import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper';
|
||||
import { CoreSitePluginsCompileInitComponent } from '@features/siteplugins/classes/compile-init-component';
|
||||
|
||||
|
||||
/**
|
||||
* Component that displays a question behaviour created using a site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-question-behaviour',
|
||||
templateUrl: 'core-siteplugins-question-behaviour.html',
|
||||
})
|
||||
export class CoreSitePluginsQuestionBehaviourComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
|
||||
@Input() question?: CoreQuestionQuestion; // The question.
|
||||
@Input() component?: string; // The component the question belongs to.
|
||||
@Input() componentId?: number; // ID of the component the question belongs to.
|
||||
@Input() attemptId?: number; // Attempt ID.
|
||||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Input() contextLevel?: string; // The context level.
|
||||
@Input() contextInstanceId?: number; // The instance ID related to the context.
|
||||
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
|
||||
@Input() review?: boolean; // Whether the user is in review mode.
|
||||
@Input() preferredBehaviour?: string; // Preferred behaviour.
|
||||
@Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
|
||||
@Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Pass the input and output data to the component.
|
||||
this.jsData.question = this.question;
|
||||
this.jsData.component = this.component;
|
||||
this.jsData.componentId = this.componentId;
|
||||
this.jsData.attemptId = this.attemptId;
|
||||
this.jsData.offlineEnabled = this.offlineEnabled;
|
||||
this.jsData.contextLevel = this.contextLevel;
|
||||
this.jsData.contextInstanceId = this.contextInstanceId;
|
||||
this.jsData.buttonClicked = this.buttonClicked;
|
||||
this.jsData.onAbort = this.onAbort;
|
||||
|
||||
if (this.question) {
|
||||
this.getHandlerData(CoreQuestionBehaviourDelegate.getHandlerName(this.preferredBehaviour || ''));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue