diff --git a/src/core/course/pages/section/section.html b/src/core/course/pages/section/section.html
index 83bf61d17..56cd025b4 100644
--- a/src/core/course/pages/section/section.html
+++ b/src/core/course/pages/section/section.html
@@ -30,7 +30,7 @@
-
+
diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts
index 0e0ced8b7..093e0a29f 100644
--- a/src/core/course/pages/section/section.ts
+++ b/src/core/course/pages/section/section.ts
@@ -45,7 +45,6 @@ export class CoreCourseSectionPage implements OnDestroy {
sectionId: number;
sectionNumber: number;
courseHandlers: CoreCourseOptionsHandlerToDisplay[];
- handlerData: any = {}; // Data to send to the handlers components.
dataLoaded: boolean;
downloadEnabled: boolean;
downloadEnabledIcon = 'square-outline'; // Disabled by default.
@@ -70,7 +69,6 @@ export class CoreCourseSectionPage implements OnDestroy {
this.sectionId = navParams.get('sectionId');
this.sectionNumber = navParams.get('sectionNumber');
this.module = navParams.get('module');
- this.handlerData.courseId = this.course.id;
// Get the title to display. We dont't have sections yet.
this.title = courseFormatDelegate.getCourseTitle(this.course);
@@ -194,6 +192,12 @@ export class CoreCourseSectionPage implements OnDestroy {
// Load the course handlers.
promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, false).then((handlers) => {
+ // Add the courseId to the handler component data.
+ handlers.forEach((handler) => {
+ handler.data.componentData = handler.data.componentData || {};
+ handler.data.componentData.courseId = this.course.id;
+ });
+
this.courseHandlers = handlers;
}));
diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts
index 35182a369..e73a4b6ab 100644
--- a/src/core/course/providers/options-delegate.ts
+++ b/src/core/course/providers/options-delegate.ts
@@ -90,6 +90,12 @@ export interface CoreCourseOptionsHandlerData {
* When the component is created, it will receive the courseId as input.
*/
component: any;
+
+ /**
+ * Data to pass to the component. All the properties in this object will be passed to the component as inputs.
+ * @type {any}
+ */
+ componentData?: any;
}
/**
diff --git a/src/core/siteaddons/classes/module-prefetch-handler.ts b/src/core/siteaddons/classes/module-prefetch-handler.ts
index f87750000..31c502892 100644
--- a/src/core/siteaddons/classes/module-prefetch-handler.ts
+++ b/src/core/siteaddons/classes/module-prefetch-handler.ts
@@ -80,48 +80,8 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc
promises.push(this.downloadOrPrefetchFiles(site.id, module, courseId, prefetch, dirPath));
// Call all the offline functions.
- for (const method in this.handlerSchema.offlinefunctions) {
- if (site.wsAvailable(method)) {
- // The method is a WS.
- const paramsList = this.handlerSchema.offlinefunctions[method],
- cacheKey = this.siteAddonsProvider.getCallWSCacheKey(method, args);
- let params = {};
-
- if (!paramsList.length) {
- // No params defined, send the default ones.
- params = args;
- } else {
- for (const i in paramsList) {
- const paramName = paramsList[i];
-
- if (typeof args[paramName] != 'undefined') {
- params[paramName] = args[paramName];
- } else {
- // The param is not one of the default ones. Try to calculate the param to use.
- const value = this.getDownloadParam(module, courseId, paramName);
- if (typeof value != 'undefined') {
- params[paramName] = value;
- }
- }
- }
- }
-
- promises.push(this.siteAddonsProvider.callWS(method, params, {cacheKey: cacheKey}));
- } else {
- // It's a method to get content.
- promises.push(this.siteAddonsProvider.getContent(this.component, method, args).then((result) => {
- const subPromises = [];
-
- // Prefetch the files in the content.
- if (result.files && result.files.length) {
- subPromises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, result.files, prefetch, false,
- this.component, module.id, dirPath));
- }
-
- return Promise.all(subPromises);
- }));
- }
- }
+ promises.push(this.siteAddonsProvider.prefetchFunctions(this.component, args, this.handlerSchema, courseId,
+ module, prefetch, dirPath, site));
return Promise.all(promises);
});
@@ -165,29 +125,6 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc
});
}
- /**
- * Get the value of a WS param for prefetch.
- *
- * @param {any} module The module object returned by WS.
- * @param {number} courseId Course ID.
- * @param {string} paramName Name of the param as defined by the handler.
- * @return {any} The value.
- */
- protected getDownloadParam(module: any, courseId: number, paramName: string): any {
- switch (paramName) {
- case 'courseids':
- // The WS needs the list of course IDs. Create the list.
- return [courseId];
-
- case this.component + 'id':
- // The WS needs the instance id.
- return module.instance;
-
- default:
- // No more params supported for now.
- }
- }
-
/**
* Invalidate the prefetched content.
*
diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts
index 200e8be8f..61021a6b9 100644
--- a/src/core/siteaddons/components/components.module.ts
+++ b/src/core/siteaddons/components/components.module.ts
@@ -20,11 +20,13 @@ import { CoreComponentsModule } from '../../../components/components.module';
import { CoreCompileHtmlComponentModule } from '../../compile/components/compile-html/compile-html.module';
import { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-content';
import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index';
+import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-option';
@NgModule({
declarations: [
CoreSiteAddonsAddonContentComponent,
- CoreSiteAddonsModuleIndexComponent
+ CoreSiteAddonsModuleIndexComponent,
+ CoreSiteAddonsCourseOptionComponent
],
imports: [
CommonModule,
@@ -37,10 +39,12 @@ import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index'
],
exports: [
CoreSiteAddonsAddonContentComponent,
- CoreSiteAddonsModuleIndexComponent
+ CoreSiteAddonsModuleIndexComponent,
+ CoreSiteAddonsCourseOptionComponent
],
entryComponents: [
- CoreSiteAddonsModuleIndexComponent
+ CoreSiteAddonsModuleIndexComponent,
+ CoreSiteAddonsCourseOptionComponent
]
})
export class CoreSiteAddonsComponentsModule {}
diff --git a/src/core/siteaddons/components/course-option/course-option.html b/src/core/siteaddons/components/course-option/course-option.html
new file mode 100644
index 000000000..ada863f5e
--- /dev/null
+++ b/src/core/siteaddons/components/course-option/course-option.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/core/siteaddons/components/course-option/course-option.ts b/src/core/siteaddons/components/course-option/course-option.ts
new file mode 100644
index 000000000..99a26249a
--- /dev/null
+++ b/src/core/siteaddons/components/course-option/course-option.ts
@@ -0,0 +1,66 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Component, OnInit, Input, ViewChild } from '@angular/core';
+import { CoreSiteAddonsProvider } from '../../providers/siteaddons';
+import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content';
+
+/**
+ * Component that displays the index of a course option site addon.
+ */
+@Component({
+ selector: 'core-site-addons-course-option',
+ templateUrl: 'course-option.html',
+})
+export class CoreSiteAddonsCourseOptionComponent implements OnInit {
+ @Input() courseId: number;
+ @Input() handlerUniqueName: string;
+
+ @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent;
+
+ component: string;
+ method: string;
+ args: any;
+ bootstrapResult: any;
+
+ constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { }
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit(): void {
+ if (this.handlerUniqueName) {
+ const handler = this.siteAddonsProvider.getSiteAddonHandler(this.handlerUniqueName);
+ if (handler) {
+ this.component = handler.addon.component;
+ this.method = handler.handlerSchema.method;
+ this.args = {
+ courseid: this.courseId,
+ };
+ this.bootstrapResult = handler.bootstrapResult;
+ }
+ }
+ }
+
+ /**
+ * Refresh the data.
+ *
+ * @param {any} refresher Refresher.
+ */
+ refreshData(refresher: any): void {
+ this.content.refreshData().finally(() => {
+ refresher.complete();
+ });
+ }
+}
diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts
index 2869aca16..66885636a 100644
--- a/src/core/siteaddons/providers/helper.ts
+++ b/src/core/siteaddons/providers/helper.ts
@@ -24,9 +24,13 @@ import {
CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData
} from '../../course/providers/module-delegate';
import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate';
+import {
+ CoreCourseOptionsDelegate, CoreCourseOptionsHandler, CoreCourseOptionsHandlerData
+} from '../../course/providers/options-delegate';
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate';
import { CoreDelegateHandler } from '../../../classes/delegate';
import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index';
+import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option';
import { CoreSiteAddonsProvider } from './siteaddons';
import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler';
import { CoreCompileProvider } from '../../compile/providers/compile';
@@ -47,7 +51,7 @@ export class CoreSiteAddonsHelperProvider {
private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider,
private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate,
private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider,
- private coursesProvider: CoreCoursesProvider) {
+ private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate) {
this.logger = logger.getInstance('CoreSiteAddonsHelperProvider');
}
@@ -130,6 +134,54 @@ export class CoreSiteAddonsHelperProvider {
return this.getHandlerPrefixForStrings(handlerName) + key;
}
+ /**
+ * Check if a handler is enabled for a certain course.
+ *
+ * @param {number} courseId Course ID to check.
+ * @param {boolean} [restrictEnrolled] If true or undefined, handler is only enabled for courses the user is enrolled in.
+ * @param {any} [restrict] Users and courses the handler is restricted to.
+ * @return {boolean | Promise} Whether the handler is enabled.
+ */
+ protected isHandlerEnabledForCourse(courseId: number, restrictEnrolled?: boolean, restrict?: any): boolean | Promise {
+ if (restrict && restrict.courses && restrict.courses.indexOf(courseId) == -1) {
+ // Course is not in the list of restricted courses.
+ return false;
+ }
+
+ if (restrictEnrolled || typeof restrictEnrolled == 'undefined') {
+ // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course.
+ return this.coursesProvider.getUserCourse(courseId, true).then(() => {
+ return true;
+ }).catch(() => {
+ return false;
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if a handler is enabled for a certain user.
+ *
+ * @param {number} userId User ID to check.
+ * @param {boolean} [restrictCurrent] Whether handler is only enabled for current user.
+ * @param {any} [restrict] Users and courses the handler is restricted to.
+ * @return {boolean} Whether the handler is enabled.
+ */
+ protected isHandlerEnabledForUser(userId: number, restrictCurrent?: boolean, restrict?: any): boolean {
+ if (restrictCurrent && userId != this.sitesProvider.getCurrentSite().getUserId()) {
+ // Only enabled for current user.
+ return false;
+ }
+
+ if (restrict && restrict.users && restrict.users.indexOf(userId) == -1) {
+ // User is not in the list of restricted users.
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Check if a certain addon is a site addon and it's enabled in a certain site.
*
@@ -227,6 +279,11 @@ export class CoreSiteAddonsHelperProvider {
result.restrict);
break;
+ case 'CoreCourseOptionsDelegate':
+ uniqueName = this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result.jsResult,
+ result.restrict);
+ break;
+
default:
// Nothing to do.
}
@@ -243,6 +300,60 @@ export class CoreSiteAddonsHelperProvider {
});
}
+ /**
+ * Given a handler in an addon, register it in the course options delegate.
+ *
+ * @param {any} addon Data of the addon.
+ * @param {string} handlerName Name of the handler in the addon.
+ * @param {any} handlerSchema Data about the handler.
+ * @param {any} [bootstrapResult] Result of executing the bootstrap JS.
+ * @param {any} [restrict] List of users and courses the handler is restricted to.
+ * @return {string} A string to identify the handler.
+ */
+ protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any,
+ restrict?: any): string {
+ if (!handlerSchema || !handlerSchema.displaydata) {
+ // Required data not provided, stop.
+ return;
+ }
+
+ // Create the base handler.
+ const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName),
+ baseHandler = this.getBaseHandler(uniqueName),
+ prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title);
+ let handler: CoreCourseOptionsHandler;
+
+ // Extend the base handler, adding the properties required by the delegate.
+ handler = Object.assign(baseHandler, {
+ priority: handlerSchema.priority,
+ isEnabledForCourse: (courseId: number, accessData: any, navOptions?: any, admOptions?: any)
+ : boolean | Promise => {
+ return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict);
+ },
+ getDisplayData: (courseId: number): CoreCourseOptionsHandlerData => {
+ return {
+ title: prefixedTitle,
+ class: handlerSchema.displaydata.class,
+ component: CoreSiteAddonsCourseOptionComponent,
+ componentData: {
+ handlerUniqueName: uniqueName
+ }
+ };
+ },
+ prefetch: (course: any): Promise => {
+ const args = {
+ courseid: course.id,
+ };
+
+ return this.siteAddonsProvider.prefetchFunctions(addon.component, args, handlerSchema, course.id, undefined, true);
+ }
+ });
+
+ this.courseOptionsDelegate.registerHandler(handler);
+
+ return uniqueName;
+ }
+
/**
* Given a handler in an addon, register it in the main menu delegate.
*
@@ -378,31 +489,14 @@ export class CoreSiteAddonsHelperProvider {
priority: handlerSchema.priority,
type: handlerSchema.type,
isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => {
- if (handlerSchema.restricttocurrentuser && user.id != this.sitesProvider.getCurrentSite().getUserId()) {
- // Only enabled for current user.
+ // First check if it's enabled for the user.
+ const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, restrict);
+ if (!enabledForUser) {
return false;
}
- if (restrict) {
- if (restrict.users && restrict.users.indexOf(user.id) == -1) {
- // User is not in the list of restricted users.
- return false;
- } else if (restrict.courses && restrict.courses.indexOf(courseId) == -1) {
- // Course is not in the list of restricted courses.
- return false;
- }
- }
-
- if (handlerSchema.restricttoenrolledcourses || typeof handlerSchema.restricttoenrolledcourses == 'undefined') {
- // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course.
- return this.coursesProvider.getUserCourse(courseId, true).then(() => {
- return true;
- }).catch(() => {
- return false;
- });
- }
-
- return true;
+ // Enabled for user, check if it's enabled for the course.
+ return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict);
},
getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => {
return {
diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts
index dd3bd05ae..94fca9258 100644
--- a/src/core/siteaddons/providers/siteaddons.ts
+++ b/src/core/siteaddons/providers/siteaddons.ts
@@ -15,6 +15,7 @@
import { Injectable } from '@angular/core';
import { Platform } from 'ionic-angular';
import { CoreAppProvider } from '../../../providers/app';
+import { CoreFilepoolProvider } from '../../../providers/filepool';
import { CoreLangProvider } from '../../../providers/lang';
import { CoreLoggerProvider } from '../../../providers/logger';
import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site';
@@ -62,7 +63,8 @@ export class CoreSiteAddonsProvider {
protected siteAddons: {[name: string]: CoreSiteAddonsHandler} = {}; // Site addons registered.
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
- private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform) {
+ private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform,
+ private filepoolProvider: CoreFilepoolProvider) {
this.logger = logger.getInstance('CoreUserProvider');
}
@@ -207,6 +209,30 @@ export class CoreSiteAddonsProvider {
return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + this.utils.sortAndStringify(args);
}
+ /**
+ * Get the value of a WS param for prefetch.
+ *
+ * @param {string} component The component of the handler.
+ * @param {string} paramName Name of the param as defined by the handler.
+ * @param {number} [courseId] Course ID (if prefetching a course).
+ * @param {any} [module] The module object returned by WS (if prefetching a module).
+ * @return {any} The value.
+ */
+ protected getDownloadParam(component: string, paramName: string, courseId?: number, module?: any): any {
+ switch (paramName) {
+ case 'courseids':
+ // The WS needs the list of course IDs. Create the list.
+ return [courseId];
+
+ case component + 'id':
+ // The WS needs the instance id.
+ return module && module.instance;
+
+ default:
+ // No more params supported for now.
+ }
+ }
+
/**
* Get the unique name of a handler (addon + handler).
*
@@ -320,6 +346,71 @@ export class CoreSiteAddonsProvider {
return args;
}
+ /**
+ * Prefetch offline functions for a site addon handler.
+ *
+ * @param {string} component The component of the handler.
+ * @param {any} args Params to send to the get_content calls.
+ * @param {any} handlerSchema The handler schema.
+ * @param {number} [courseId] Course ID (if prefetching a course).
+ * @param {any} [module] The module object returned by WS (if prefetching a module).
+ * @param {boolean} [prefetch] True to prefetch, false to download right away.
+ * @param {string} [dirPath] Path of the directory where to store all the content files.
+ * @param {CoreSite} [site] Site. If not defined, current site.
+ * @return {Promise} Promise resolved when done.
+ */
+ prefetchFunctions(component: string, args: any, handlerSchema: any, courseId?: number, module?: any, prefetch?: boolean,
+ dirPath?: string, site?: CoreSite): Promise {
+ site = site || this.sitesProvider.getCurrentSite();
+
+ const promises = [];
+
+ for (const method in handlerSchema.offlinefunctions) {
+ if (site.wsAvailable(method)) {
+ // The method is a WS.
+ const paramsList = handlerSchema.offlinefunctions[method],
+ cacheKey = this.getCallWSCacheKey(method, args);
+ let params = {};
+
+ if (!paramsList.length) {
+ // No params defined, send the default ones.
+ params = args;
+ } else {
+ for (const i in paramsList) {
+ const paramName = paramsList[i];
+
+ if (typeof args[paramName] != 'undefined') {
+ params[paramName] = args[paramName];
+ } else {
+ // The param is not one of the default ones. Try to calculate the param to use.
+ const value = this.getDownloadParam(component, paramName, courseId, module);
+ if (typeof value != 'undefined') {
+ params[paramName] = value;
+ }
+ }
+ }
+ }
+
+ promises.push(this.callWS(method, params, {cacheKey: cacheKey}));
+ } else {
+ // It's a method to get content.
+ promises.push(this.getContent(component, method, args).then((result) => {
+ const subPromises = [];
+
+ // Prefetch the files in the content.
+ if (result.files && result.files.length) {
+ subPromises.push(this.filepoolProvider.downloadOrPrefetchFiles(site.id, result.files, prefetch, false,
+ component, module.id, dirPath));
+ }
+
+ return Promise.all(subPromises);
+ }));
+ }
+ }
+
+ return Promise.all(promises);
+ }
+
/**
* Store a site addon handler.
*