diff --git a/scripts/langindex.json b/scripts/langindex.json
index e1beb3757..092fbfb2c 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1517,10 +1517,8 @@
"core.course": "moodle",
"core.course.activitydisabled": "local_moodlemobileapp",
"core.course.activitynotyetviewableremoteaddon": "local_moodlemobileapp",
- "core.course.activitynotyetviewablesiteupgradeneeded": "local_moodlemobileapp",
"core.course.allsections": "local_moodlemobileapp",
"core.course.aria:sectionprogress": "local_moodlemobileapp",
- "core.course.askadmintosupport": "local_moodlemobileapp",
"core.course.availablespace": "local_moodlemobileapp",
"core.course.cannotdeletewhiledownloading": "local_moodlemobileapp",
"core.course.completion_automatic:done": "course",
@@ -2357,7 +2355,6 @@
"core.whatisyourage": "moodle",
"core.wheredoyoulive": "moodle",
"core.whoissiteadmin": "local_moodlemobileapp",
- "core.whoops": "local_moodlemobileapp",
"core.whyisthishappening": "local_moodlemobileapp",
"core.whyisthisrequired": "moodle",
"core.wsfunctionnotavailable": "local_moodlemobileapp",
diff --git a/src/addons/calendar/components/calendar/calendar.scss b/src/addons/calendar/components/calendar/calendar.scss
index 35cd3ccbd..057745e3b 100644
--- a/src/addons/calendar/components/calendar/calendar.scss
+++ b/src/addons/calendar/components/calendar/calendar.scss
@@ -133,20 +133,10 @@
margin-right: 1px;
margin-left: 1px;
- &.calendar_event_category {
- background-color: var(--addon-calendar-event-category-color);
- }
- &.calendar_event_course {
- background-color: var(--addon-calendar-event-course-color);
- }
- &.calendar_event_group {
- background-color: var(--addon-calendar-event-group-color);
- }
- &.calendar_event_user {
- background-color: var(--addon-calendar-event-user-color);
- }
- &.calendar_event_site {
- background-color: var(--addon-calendar-event-site-color);
+ @each $category, $value in $calendar-event-category-colors {
+ &.calendar_event_#{$category} {
+ background-color: $value;
+ }
}
}
diff --git a/src/addons/storagemanager/services/handlers/course-menu.ts b/src/addons/storagemanager/services/handlers/course-menu.ts
index 7aa36c6de..74dc8f409 100644
--- a/src/addons/storagemanager/services/handlers/course-menu.ts
+++ b/src/addons/storagemanager/services/handlers/course-menu.ts
@@ -48,7 +48,7 @@ export class AddonStorageManagerCourseMenuHandlerService implements CoreCourseOp
course: CoreCourseAnyCourseDataWithOptions,
): CoreCourseOptionsMenuHandlerData {
return {
- icon: 'cloud-download',
+ icon: 'fas-cloud-download-alt',
title: 'addon.storagemanager.coursedownloads',
page: 'storage/' + course.id,
pageParams: {
diff --git a/src/assets/fonts/moodle/font-awesome/cloud-done.svg b/src/assets/fonts/moodle/font-awesome/cloud-done.svg
new file mode 100644
index 000000000..9f80a5392
--- /dev/null
+++ b/src/assets/fonts/moodle/font-awesome/cloud-done.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/fonts/moodle/font-awesome/cloud-refresh.svg b/src/assets/fonts/moodle/font-awesome/cloud-refresh.svg
new file mode 100644
index 000000000..2e336c793
--- /dev/null
+++ b/src/assets/fonts/moodle/font-awesome/cloud-refresh.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/fonts/moodle/moodle/agg_mean.svg b/src/assets/fonts/moodle/moodle/agg_mean.svg
new file mode 100644
index 000000000..c10adf743
--- /dev/null
+++ b/src/assets/fonts/moodle/moodle/agg_mean.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/fonts/moodle/moodle/agg_sum.svg b/src/assets/fonts/moodle/moodle/agg_sum.svg
new file mode 100644
index 000000000..6108dc6df
--- /dev/null
+++ b/src/assets/fonts/moodle/moodle/agg_sum.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/img/grades/agg_mean.svg b/src/assets/img/grades/agg_mean.svg
deleted file mode 100644
index ac4368d15..000000000
--- a/src/assets/img/grades/agg_mean.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/assets/img/grades/agg_sum.svg b/src/assets/img/grades/agg_sum.svg
deleted file mode 100644
index 9ebd5c23d..000000000
--- a/src/assets/img/grades/agg_sum.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/core/components/download-refresh/core-download-refresh.html b/src/core/components/download-refresh/core-download-refresh.html
index f9580727f..21d380920 100644
--- a/src/core/components/download-refresh/core-download-refresh.html
+++ b/src/core/components/download-refresh/core-download-refresh.html
@@ -2,19 +2,19 @@
-
+
-
+
+ name="fam-cloud-done" [attr.aria-label]="(statusTranslatable || 'core.downloaded') | translate" role="status">
diff --git a/src/core/components/mod-icon/mod-icon.scss b/src/core/components/mod-icon/mod-icon.scss
index 45b236cb6..f5f7dbdcb 100644
--- a/src/core/components/mod-icon/mod-icon.scss
+++ b/src/core/components/mod-icon/mod-icon.scss
@@ -6,6 +6,7 @@
--margin-end: 0px;
--margin-vertical: 0px;
--icon-radius: var(--small-radius);
+ --filter: brightness(0) invert(1);
margin-top: var(--margin-vertical);
margin-bottom: var(--margin-vertical);
@@ -20,7 +21,7 @@
&.#{$type} {
background-color: var(--activity#{$type});
img {
- filter: brightness(0) invert(1);
+ filter: var(--filter);
}
}
}
diff --git a/src/core/constants.ts b/src/core/constants.ts
index 7113598cc..0fcd0ddd3 100644
--- a/src/core/constants.ts
+++ b/src/core/constants.ts
@@ -93,10 +93,10 @@ export class CoreConstants {
static readonly NOT_DOWNLOADABLE = 'notdownloadable';
// Download / prefetch status icon.
- static readonly ICON_DOWNLOADED = 'cloud-done';
+ static readonly ICON_DOWNLOADED = 'fam-cloud-done';
static readonly ICON_DOWNLOADING = 'spinner';
- static readonly ICON_NOT_DOWNLOADED = 'cloud-download';
- static readonly ICON_OUTDATED = 'fas-redo-alt';
+ static readonly ICON_NOT_DOWNLOADED = 'fas-cloud-download-alt';
+ static readonly ICON_OUTDATED = 'fam-cloud-refresh';
static readonly ICON_NOT_DOWNLOADABLE = '';
// General download and sync icons.
diff --git a/src/core/directives/fa-icon.ts b/src/core/directives/fa-icon.ts
index 8a262dd45..9de2de27a 100644
--- a/src/core/directives/fa-icon.ts
+++ b/src/core/directives/fa-icon.ts
@@ -45,47 +45,58 @@ export class CoreFaIconDirective implements AfterViewInit, OnChanges {
* Detect icon name and use svg.
*/
async setIcon(): Promise {
- let library = 'ionic';
+ let library = '';
let iconName = this.name;
+ let font = 'ionicons';
const parts = iconName.split('-', 2);
if (parts.length == 2) {
switch (parts[0]) {
case 'far':
library = 'regular';
- iconName = iconName.substring(4);
+ font = 'font-awesome';
break;
case 'fa':
case 'fas':
library = 'solid';
- iconName = iconName.substring(parts[0].length + 1);
+ font = 'font-awesome';
break;
case 'fab':
library = 'brands';
- iconName = iconName.substring(4);
+ font = 'font-awesome';
+ break;
+ case 'moodle':
+ library = 'moodle';
+ font = 'moodle';
+ break;
+ case 'fam':
+ library = 'font-awesome';
+ font = 'moodle';
break;
default:
break;
}
}
- if (library != 'ionic') {
- const src = `assets/fonts/font-awesome/${library}/${iconName}.svg`;
- this.element.setAttribute('src', src);
- this.element.classList.add('faicon');
-
- if (CoreConstants.BUILD.isDevelopment || CoreConstants.BUILD.isTesting) {
- try {
- await Http.get(src, { responseType: 'text' }).toPromise();
- } catch (error) {
- this.logger.error(`Icon ${this.name} not found`);
- }
- }
- } else {
+ if (font == 'ionicons') {
this.element.removeAttribute('src');
this.logger.warn(`Ionic icon ${this.name} detected`);
+
+ return;
}
- return;
+ iconName = iconName.substring(parts[0].length + 1);
+
+ const src = `assets/fonts/${font}/${library}/${iconName}.svg`;
+ this.element.setAttribute('src', src);
+ this.element.classList.add('faicon');
+
+ if (CoreConstants.BUILD.isDevelopment || CoreConstants.BUILD.isTesting) {
+ try {
+ await Http.get(src, { responseType: 'text' }).toPromise();
+ } catch (error) {
+ this.logger.error(`Icon ${this.name} not found`);
+ }
+ }
}
/**
diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html
index 256f2c789..f6f50dd35 100644
--- a/src/core/features/course/components/module-info/core-course-module-info.html
+++ b/src/core/features/course/components/module-info/core-course-module-info.html
@@ -7,6 +7,8 @@
+
@@ -20,22 +22,33 @@
-
+
+
-
-
-
- {{ date.label }} {{ date.timestamp * 1000 |
- coreFormatDate:'strftimedatetime' }}
-
-
-
+
+
+
+
+
+ {{ date.label }} {{ date.timestamp
+ *
+ 1000 | coreFormatDate:'strftimedatetime' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/course/components/module-info/course-module-info.scss b/src/core/features/course/components/module-info/course-module-info.scss
index 46d6c970c..75e76aedd 100644
--- a/src/core/features/course/components/module-info/course-module-info.scss
+++ b/src/core/features/course/components/module-info/course-module-info.scss
@@ -16,16 +16,38 @@
align-self: flex-start;
}
- .core-module-dates {
+ h1 ion-icon {
+ color: var(--medium);
+ @include margin-horizontal(8px, null);
+ font-size: 80%;
+
+ }
+
+ .core-module-dates-availabilityinfo {
background: var(--light);
border-radius: var(--small-radius);
padding: 8px;
-
+ margin: 8px;
+ font-size: 90%;
ion-icon {
+ position: static;
@include margin-horizontal(null, 8px);
}
+
+ p,
+ ul {
+ margin-top: 4px;
+ margin-bottom: 4px;
+ }
}
+ .core-module-dates + .core-module-availabilityinfo {
+ border-top: 1px solid var(--stroke);
+ padding-top: 8px;
+ }
+
+
+
}
:host-context(.core-iframe-fullscreen) {
diff --git a/src/core/features/course/components/module-info/module-info.ts b/src/core/features/course/components/module-info/module-info.ts
index f57b95e7e..bd1fe0780 100644
--- a/src/core/features/course/components/module-info/module-info.ts
+++ b/src/core/features/course/components/module-info/module-info.ts
@@ -44,6 +44,8 @@ export class CoreCourseModuleInfoComponent implements OnInit {
@Input() description?: string | false; // The description to display. If false, no description will be shown.
@Input() expandDescription = false; // If the description should be expanded by default.
+ @Input() showAvailabilityInfo = false; // If show availability info on the box.
+
@Input() hasDataToSync = false; // If the activity has any data to be synced.
modicon = '';
diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html
index 820e944ef..6c60ba185 100644
--- a/src/core/features/course/components/module-summary/module-summary.html
+++ b/src/core/features/course/components/module-summary/module-summary.html
@@ -58,7 +58,7 @@
-
+
{{ 'addon.storagemanager.downloads' | translate }}
@@ -81,7 +81,7 @@
-
+
{{ 'core.download' | translate }}
diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html
index e1048efe5..579f101e5 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -17,6 +17,8 @@
+
diff --git a/src/core/features/course/components/module/module.scss b/src/core/features/course/components/module/module.scss
index da557da41..c4fd942bd 100644
--- a/src/core/features/course/components/module/module.scss
+++ b/src/core/features/course/components/module/module.scss
@@ -13,6 +13,7 @@
.core-module-title .item-heading ion-icon {
@include margin-horizontal(8px, null);
+ vertical-align: middle;
}
.core-module-buttons,
diff --git a/src/core/features/course/components/module/module.ts b/src/core/features/course/components/module/module.ts
index 0761f2ad6..478dc0b62 100644
--- a/src/core/features/course/components/module/module.ts
+++ b/src/core/features/course/components/module/module.ts
@@ -22,6 +22,12 @@ import {
} from '@features/course/services/course-helper';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
+import {
+ CoreCourseModulePrefetchDelegate,
+ CoreCourseModulePrefetchHandler,
+} from '@features/course/services/module-prefetch-delegate';
+import { CoreConstants } from '@/core/constants';
+import { CoreEventObserver, CoreEvents } from '@singletons/events';
/**
* Component to display a module entry in a list of modules.
@@ -47,11 +53,16 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
hasInfo = false;
showLegacyCompletion = false; // Whether to show module completion in the old format.
showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
+ prefetchStatusIcon = ''; // Module prefetch status icon.
+ prefetchStatusText = ''; // Module prefetch status text.
+ protected prefetchHandler?: CoreCourseModulePrefetchHandler;
+
+ protected moduleStatusObserver?: CoreEventObserver;
/**
- * Component being initialized.
+ * @inheritdoc
*/
- ngOnInit(): void {
+ async ngOnInit(): Promise
{
this.modNameTranslated = CoreCourse.translateModuleName(this.module.modname) || '';
this.showLegacyCompletion = !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.11');
this.checkShowManualCompletion();
@@ -70,6 +81,60 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
(this.module.visible !== 0 && this.module.isStealth) ||
(this.module.availabilityinfo)
);
+
+ if (this.module.handlerData?.showDownloadButton) {
+ const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.module.course);
+ this.updateModuleStatus(status);
+
+ // Listen for changes on this module status, even if download isn't enabled.
+ this.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module.modname);
+ if (!this.prefetchHandler) {
+ return;
+ }
+
+ this.moduleStatusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
+ if (this.module.id != data.componentId || data.component != this.prefetchHandler?.component) {
+ return;
+ }
+
+ let status = data.status;
+ if (this.prefetchHandler.determineStatus) {
+ // Call determineStatus to get the right status to display.
+ status = this.prefetchHandler.determineStatus(this.module, status, true);
+ }
+
+ // Update the status.
+ this.updateModuleStatus(status);
+ }, CoreSites.getCurrentSiteId());
+ }
+ }
+
+ /**
+ * Show module status.
+ *
+ * @param prefetchstatus Module status.
+ */
+ protected updateModuleStatus(prefetchstatus: string): void {
+ if (!prefetchstatus) {
+ return;
+ }
+
+ switch (prefetchstatus) {
+ case CoreConstants.OUTDATED:
+ this.prefetchStatusIcon = CoreConstants.ICON_OUTDATED;
+ this.prefetchStatusText = 'core.outdated';
+ break;
+ case CoreConstants.DOWNLOADED:
+ this.prefetchStatusIcon = CoreConstants.ICON_DOWNLOADED;
+ this.prefetchStatusText = 'core.downloaded';
+ break;
+ default:
+ this.prefetchStatusIcon = '';
+ this.prefetchStatusText = '';
+ break;
+ }
+
+ this.module.handlerData?.updateStatus?.(prefetchstatus);
}
/**
@@ -109,10 +174,11 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
}
/**
- * Component destroyed.
+ * @inheritdoc
*/
ngOnDestroy(): void {
this.module.handlerData?.onDestroy?.();
+ this.moduleStatusObserver?.off();
}
}
diff --git a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
index 4fd22a35d..f8b7bd52f 100644
--- a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
+++ b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
@@ -1,21 +1,22 @@
-
-
{{ 'core.whoops' | translate }}
-
{{ 'core.uhoh' | translate }}
-
-
{{ 'core.course.activitydisabled' | translate }}
-
- {{ 'core.course.activitynotyetviewablesiteupgradeneeded' | translate }}
-
-
- {{ 'core.course.activitynotyetviewableremoteaddon' | translate }}
-
-
{{ 'core.course.askadmintosupport' | translate }}
-
-
-
{{ 'core.course.useactivityonbrowser' | translate }}
-
+
+
+
+
+ {{ 'core.uhoh' | translate }}
+ {{ 'core.course.activitydisabled' | translate }}
+
+ {{ 'core.course.activitynotyetviewableremoteaddon' | translate }}
+
+
+
+
+
+
+
+ {{ 'core.course.useactivityonbrowser' | translate }}
+
{{ 'core.openinbrowser' | translate }}
-
-
+
+
diff --git a/src/core/features/course/components/unsupported-module/unsupported-module.ts b/src/core/features/course/components/unsupported-module/unsupported-module.ts
index 5df40ab8f..15c3e7902 100644
--- a/src/core/features/course/components/unsupported-module/unsupported-module.ts
+++ b/src/core/features/course/components/unsupported-module/unsupported-module.ts
@@ -14,7 +14,6 @@
import { Component, Input, OnInit } from '@angular/core';
-import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@@ -27,12 +26,10 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg
})
export class CoreCourseUnsupportedModuleComponent implements OnInit {
- @Input() courseId?: number; // The course to module belongs to.
+ @Input() courseId?: number; // The course to module belongs to (unused).
@Input() module?: CoreCourseModuleData; // The module to render.
- isDisabledInSite?: boolean;
- isSupportedByTheApp?: boolean;
- moduleName?: string;
+ isDisabledInSite = false; // It is implicit than if not disabled it will be unsupported.
/**
* Component being initialized.
@@ -43,8 +40,6 @@ export class CoreCourseUnsupportedModuleComponent implements OnInit {
}
this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname);
- this.isSupportedByTheApp = CoreCourseModuleDelegate.hasHandler(this.module.modname);
- this.moduleName = CoreCourse.translateModuleName(this.module.modname);
}
}
diff --git a/src/core/features/course/lang.json b/src/core/features/course/lang.json
index 36f792de9..554f22f43 100644
--- a/src/core/features/course/lang.json
+++ b/src/core/features/course/lang.json
@@ -1,10 +1,8 @@
{
"activitydisabled": "Your organisation has disabled this activity in the mobile app.",
"activitynotyetviewableremoteaddon": "Your organisation installed a plugin that is not yet supported.",
- "activitynotyetviewablesiteupgradeneeded": "Your organisation's Moodle installation needs to be updated.",
"allsections": "All sections",
"aria:sectionprogress": "Section progress:",
- "askadmintosupport": "Contact the site administrator and tell them you want to use this activity with the Moodle Mobile app.",
"availablespace": " You currently have about {{available}} free space.",
"cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.",
"completion_automatic:done": "Done:",
diff --git a/src/core/features/course/pages/module-preview/module-preview.html b/src/core/features/course/pages/module-preview/module-preview.html
index eb42c4b54..010351d87 100644
--- a/src/core/features/course/pages/module-preview/module-preview.html
+++ b/src/core/features/course/pages/module-preview/module-preview.html
@@ -11,8 +11,8 @@
-
+
@@ -24,44 +24,24 @@
+ [componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true">
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
- {{ 'core.course.hiddenfromstudents' | translate }}
-
-
-
-
- {{ 'core.course.hiddenoncoursepage' | translate }}
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {{ 'core.course.hiddenfromstudents' | translate }}
+
+
+ {{ 'core.course.hiddenoncoursepage' | translate }}
+
+
+
+
void; // Update the module after a certain time.
@@ -80,6 +81,8 @@ export class CoreCourseModulePreviewPage implements OnInit {
if (!this.unsupported) {
this.module.handlerData =
await CoreCourseModuleDelegate.getModuleDataFor(this.module.modname, this.module, this.courseId);
+ } else {
+ this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname);
}
this.title = this.module.name;
diff --git a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
index c63576239..39bf2b5e2 100644
--- a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
+++ b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
@@ -62,8 +62,8 @@
-
+
+ name="fam-cloud-done" color="success" role="status" [attr.aria-label]="'core.downloaded' | translate">
-1) {
row.itemtype = 'agg_mean';
- row.image = 'assets/img/grades/agg_mean.svg';
+ row.icon = 'moodle-agg_mean';
row.iconAlt = Translate.instant('core.grades.aggregatemean');
} else if (text.indexOf('/agg_sum') > -1) {
row.itemtype = 'agg_sum';
- row.image = 'assets/img/grades/agg_sum.svg';
+ row.icon = 'moodle-agg_sum';
row.iconAlt = Translate.instant('core.grades.aggregatesum');
} else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1) {
row.itemtype = 'outcome';
diff --git a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
index d4f915a10..94574fcbe 100644
--- a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
+++ b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
@@ -5,6 +5,10 @@
+
+
+
+
diff --git a/src/core/features/siteplugins/pages/module-index/module-index.html b/src/core/features/siteplugins/pages/module-index/module-index.html
index 7bbd67a91..58f3efc7c 100644
--- a/src/core/features/siteplugins/pages/module-index/module-index.html
+++ b/src/core/features/siteplugins/pages/module-index/module-index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/src/core/lang.json b/src/core/lang.json
index 5b58a7e07..2a72d84e7 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -342,7 +342,6 @@
"whatisyourage": "What is your age?",
"wheredoyoulive": "In which country do you live?",
"whoissiteadmin": "\"Site Administrators\" are the people who manage the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.",
- "whoops": "Oops!",
"whyisthishappening": "Why is this happening?",
"whyisthisrequired": "Why is this required?",
"wsfunctionnotavailable": "The web service function is not available.",