commit
9ff408d3b9
|
@ -176,6 +176,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
super.ngOnChanges(changes);
|
||||||
|
|
||||||
if (this.loaded && changes.block) {
|
if (this.loaded && changes.block) {
|
||||||
// Block was re-fetched, load content.
|
// Block was re-fetched, load content.
|
||||||
this.reloadContent();
|
this.reloadContent();
|
||||||
|
|
|
@ -1089,11 +1089,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
// Only show outcomes with info on it, outcomeid could be null if outcomes are disabled on site.
|
// Only show outcomes with info on it, outcomeid could be null if outcomes are disabled on site.
|
||||||
gradeInfo.outcomes?.forEach((outcome) => {
|
gradeInfo.outcomes?.forEach((outcome) => {
|
||||||
if (outcome.id == String(grade.outcomeid)) {
|
if (outcome.id == String(grade.outcomeid)) {
|
||||||
outcome.selected = grade.gradeformatted;
|
// Clean HTML tags, grade can contain an icon.
|
||||||
|
outcome.selected = CoreTextUtils.cleanTags(grade.gradeformatted || '');
|
||||||
outcome.modified = grade.gradedategraded;
|
outcome.modified = grade.gradedategraded;
|
||||||
if (outcome.options) {
|
if (outcome.options) {
|
||||||
outcome.selectedId =
|
outcome.selectedId = CoreGradesHelper.getGradeValueFromLabel(outcome.options, outcome.selected);
|
||||||
CoreGradesHelper.getGradeValueFromLabel(outcome.options, outcome.selected || '');
|
|
||||||
this.originalGrades.outcomes[outcome.id] = outcome.selectedId;
|
this.originalGrades.outcomes[outcome.id] = outcome.selectedId;
|
||||||
outcome.itemNumber = grade.itemnumber;
|
outcome.itemNumber = grade.itemnumber;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
<ion-item *ngIf="model[modelValueName]">
|
<ion-item *ngIf="model[modelValueName]">
|
||||||
<ion-label color="success">{{ 'core.answered' | translate }}</ion-label>
|
<ion-label color="success">{{ 'core.answered' | translate }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="expired" class="ion-text-wrap">
|
<ion-item *ngIf="expired" class="ion-text-wrap core-input-error">
|
||||||
<ion-label color="danger">{{ 'core.login.recaptchaexpired' | translate }}</ion-label>
|
<ion-label color="danger">{{ 'core.login.recaptchaexpired' | translate }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ion-item *ngIf="showRequiredError && !model[modelValueName] && !expired" class="ion-text-wrap core-input-error">
|
||||||
|
<ion-label color="danger">{{ 'core.required' | translate }}</ion-label>
|
||||||
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,6 +32,7 @@ export class CoreRecaptchaComponent implements OnInit {
|
||||||
@Input() publicKey?: string; // The site public key.
|
@Input() publicKey?: string; // The site public key.
|
||||||
@Input() modelValueName = 'recaptcharesponse'; // Name of the model property where to store the response.
|
@Input() modelValueName = 'recaptcharesponse'; // Name of the model property where to store the response.
|
||||||
@Input() siteUrl = ''; // The site URL. If not defined, current site.
|
@Input() siteUrl = ''; // The site URL. If not defined, current site.
|
||||||
|
@Input() showRequiredError = false; // Whether to display the required error if recaptcha hasn't been answered.
|
||||||
|
|
||||||
expired = false;
|
expired = false;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { OnInit, Input, Component, Optional, Inject } from '@angular/core';
|
import { OnInit, Input, Component, Optional, Inject, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
@ -30,7 +30,7 @@ import { CorePromisedValue } from '@classes/promised-value';
|
||||||
@Component({
|
@Component({
|
||||||
template: '',
|
template: '',
|
||||||
})
|
})
|
||||||
export abstract class CoreBlockBaseComponent implements OnInit, ICoreBlockComponent, AsyncDirective {
|
export abstract class CoreBlockBaseComponent implements OnInit, OnChanges, ICoreBlockComponent, AsyncDirective {
|
||||||
|
|
||||||
@Input() title!: string; // The block title.
|
@Input() title!: string; // The block title.
|
||||||
@Input() block!: CoreCourseBlock; // The block to render.
|
@Input() block!: CoreCourseBlock; // The block to render.
|
||||||
|
@ -54,15 +54,31 @@ export abstract class CoreBlockBaseComponent implements OnInit, ICoreBlockCompon
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
if (this.block.configs && this.block.configs.length > 0) {
|
await this.loadContent();
|
||||||
this.block.configs.forEach((config) => {
|
}
|
||||||
config.value = CoreTextUtils.parseJSON(config.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.block.configsRecord = CoreUtils.arrayToObject(this.block.configs, 'name');
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.block) {
|
||||||
|
this.parseConfigs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse configs if needed.
|
||||||
|
*/
|
||||||
|
protected parseConfigs(): void {
|
||||||
|
if (!this.block.configs?.length || this.block.configsRecord) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.loadContent();
|
this.block.configs.forEach((config) => {
|
||||||
|
config.value = CoreTextUtils.parseJSON(config.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.block.configsRecord = CoreUtils.arrayToObject(this.block.configs, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -193,7 +193,6 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.siteName = this.siteConfig.sitename;
|
this.siteName = this.siteConfig.sitename;
|
||||||
this.logoUrl = CoreLoginHelper.getLogoUrl(this.siteConfig);
|
this.logoUrl = CoreLoginHelper.getLogoUrl(this.siteConfig);
|
||||||
this.authInstructions = this.siteConfig.authinstructions || Translate.instant('core.login.loginsteps');
|
|
||||||
this.showScanQR = await CoreLoginHelper.displayQRInCredentialsScreen(this.siteConfig.tool_mobile_qrcodetype);
|
this.showScanQR = await CoreLoginHelper.displayQRInCredentialsScreen(this.siteConfig.tool_mobile_qrcodetype);
|
||||||
|
|
||||||
const disabledFeatures = CoreLoginHelper.getDisabledFeatures(this.siteConfig);
|
const disabledFeatures = CoreLoginHelper.getDisabledFeatures(this.siteConfig);
|
||||||
|
@ -204,6 +203,8 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
|
||||||
!!this.supportConfig?.canContactSupport(),
|
!!this.supportConfig?.canContactSupport(),
|
||||||
this.showForgottenPassword,
|
this.showForgottenPassword,
|
||||||
);
|
);
|
||||||
|
this.authInstructions = this.siteConfig.authinstructions ||
|
||||||
|
(this.canSignup ? Translate.instant('core.login.loginsteps') : '');
|
||||||
|
|
||||||
if (!this.eventThrown && !this.viewLeft) {
|
if (!this.eventThrown && !this.viewLeft) {
|
||||||
this.eventThrown = true;
|
this.eventThrown = true;
|
||||||
|
|
|
@ -21,13 +21,13 @@
|
||||||
<div class="list-item-limited-width">
|
<div class="list-item-limited-width">
|
||||||
|
|
||||||
<!-- Site has an unsupported required field. -->
|
<!-- Site has an unsupported required field. -->
|
||||||
<ion-list *ngIf="!allRequiredSupported">
|
<ion-list *ngIf="!allRequiredSupported" class="ion-padding">
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
{{ 'core.login.signuprequiredfieldnotsupported' | translate }}
|
{{ 'core.login.signuprequiredfieldnotsupported' | translate }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-button expand="block" class="ion-margin" [href]="signupUrl" core-link autoLogin="no" [showBrowserWarning]="false">
|
<ion-button expand="block" [href]="signupUrl" core-link autoLogin="no" [showBrowserWarning]="false">
|
||||||
{{ 'core.openinbrowser' | translate }}
|
{{ 'core.openinbrowser' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
@ -62,9 +62,11 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Submit button. -->
|
<!-- Submit button. -->
|
||||||
<ion-button expand="block" class="ion-margin" type="submit" [disabled]="!ageVerificationForm.valid">
|
<div class="ion-padding">
|
||||||
{{ 'core.proceed' | translate }}
|
<ion-button expand="block" type="submit" [disabled]="!ageVerificationForm.valid">
|
||||||
</ion-button>
|
{{ 'core.proceed' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
@ -186,7 +188,8 @@
|
||||||
<h2><span [core-mark-required]="true">{{ 'core.login.security_question' | translate }}</span></h2>
|
<h2><span [core-mark-required]="true">{{ 'core.login.security_question' | translate }}</span></h2>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<core-recaptcha [publicKey]="settings.recaptchapublickey" [model]="captcha" [siteUrl]="siteUrl"></core-recaptcha>
|
<core-recaptcha [publicKey]="settings.recaptchapublickey" [model]="captcha" [siteUrl]="siteUrl"
|
||||||
|
[showRequiredError]="formSubmitClicked"></core-recaptcha>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Site policy (if any). -->
|
<!-- Site policy (if any). -->
|
||||||
|
@ -213,10 +216,12 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Submit button. -->
|
<div class="ion-padding">
|
||||||
<ion-button expand="block" class="ion-margin" type="submit">{{ 'core.login.createaccount' | translate }}</ion-button>
|
<!-- Submit button. -->
|
||||||
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
|
<ion-button expand="block" type="submit">{{ 'core.login.createaccount' | translate }}</ion-button>
|
||||||
<input type="submit" class="core-submit-hidden-enter" />
|
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
|
||||||
|
<input type="submit" class="core-submit-hidden-enter" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
@ -239,9 +244,11 @@
|
||||||
<p *ngIf="supportEmail">{{ supportEmail }}</p>
|
<p *ngIf="supportEmail">{{ supportEmail }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-button *ngIf="!supportName && !supportEmail" expand="block" class="ion-margin" (click)="showContactOnSite()">
|
<div class="ion-padding">
|
||||||
{{ 'core.openinbrowser' | translate }}
|
<ion-button *ngIf="!supportName && !supportEmail" expand="block" (click)="showContactOnSite()">
|
||||||
</ion-button>
|
{{ 'core.openinbrowser' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</div>
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -60,6 +60,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
||||||
settingsLoaded = false;
|
settingsLoaded = false;
|
||||||
allRequiredSupported = true;
|
allRequiredSupported = true;
|
||||||
signupUrl?: string;
|
signupUrl?: string;
|
||||||
|
formSubmitClicked = false;
|
||||||
captcha = {
|
captcha = {
|
||||||
recaptcharesponse: '',
|
recaptcharesponse: '',
|
||||||
};
|
};
|
||||||
|
@ -265,6 +266,8 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.formSubmitClicked = true;
|
||||||
|
|
||||||
if (!this.signupForm.valid || (this.settings?.recaptchapublickey && !this.captcha.recaptcharesponse)) {
|
if (!this.signupForm.valid || (this.settings?.recaptchapublickey && !this.captcha.recaptcharesponse)) {
|
||||||
// Form not valid. Mark all controls as dirty to display errors.
|
// Form not valid. Mark all controls as dirty to display errors.
|
||||||
for (const name in this.signupForm.controls) {
|
for (const name in this.signupForm.controls) {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 30 KiB |
|
@ -75,7 +75,7 @@
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-toggle [(ngModel)]="debugDisplay" (ionChange)="debugDisplayChanged($event)" slot="end"></ion-toggle>
|
<ion-toggle [(ngModel)]="debugDisplay" (ionChange)="debugDisplayChanged($event)" slot="end"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap" *ngIf="analyticsSupported">
|
<ion-item class="ion-text-wrap" *ngIf="analyticsAvailable">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<p class="item-heading">{{ 'core.settings.enableanalytics' | translate }}</p>
|
<p class="item-heading">{{ 'core.settings.enableanalytics' | translate }}</p>
|
||||||
<p>{{ 'core.settings.enableanalyticsdescription' | translate }}</p>
|
<p>{{ 'core.settings.enableanalyticsdescription' | translate }}</p>
|
||||||
|
|
|
@ -44,7 +44,7 @@ export class CoreSettingsGeneralPage {
|
||||||
selectedZoomLevel = CoreZoomLevel.NONE;
|
selectedZoomLevel = CoreZoomLevel.NONE;
|
||||||
richTextEditor = true;
|
richTextEditor = true;
|
||||||
debugDisplay = false;
|
debugDisplay = false;
|
||||||
analyticsSupported = false;
|
analyticsAvailable = false;
|
||||||
analyticsEnabled = false;
|
analyticsEnabled = false;
|
||||||
colorSchemes: CoreColorScheme[] = [];
|
colorSchemes: CoreColorScheme[] = [];
|
||||||
selectedScheme: CoreColorScheme = CoreColorScheme.LIGHT;
|
selectedScheme: CoreColorScheme = CoreColorScheme.LIGHT;
|
||||||
|
@ -101,8 +101,8 @@ export class CoreSettingsGeneralPage {
|
||||||
|
|
||||||
this.debugDisplay = await CoreConfig.get(CoreConstants.SETTINGS_DEBUG_DISPLAY, false);
|
this.debugDisplay = await CoreConfig.get(CoreConstants.SETTINGS_DEBUG_DISPLAY, false);
|
||||||
|
|
||||||
this.analyticsSupported = CoreAnalytics.hasHandlers();
|
this.analyticsAvailable = await CoreAnalytics.isAnalyticsAvailable();
|
||||||
if (this.analyticsSupported) {
|
if (this.analyticsAvailable) {
|
||||||
this.analyticsEnabled = await CoreConfig.get(CoreConstants.SETTINGS_ANALYTICS_ENABLED, true);
|
this.analyticsEnabled = await CoreConfig.get(CoreConstants.SETTINGS_ANALYTICS_ENABLED, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,29 @@ export class CoreAnalyticsService extends CoreDelegate<CoreAnalyticsHandler> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if analytics is available for the app/site.
|
||||||
|
*
|
||||||
|
* @returns True if available, false otherwise.
|
||||||
|
*/
|
||||||
|
async isAnalyticsAvailable(): Promise<boolean> {
|
||||||
|
if (Object.keys(this.enabledHandlers).length > 0) {
|
||||||
|
// There is an enabled handler, analytics is available.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there is a handler that is enabled at app level (enabled handlers are only set when logged in).
|
||||||
|
const enabledList = await Promise.all(Object.values(this.handlers).map(handler => {
|
||||||
|
if (!handler.appLevelEnabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.isEnabled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
return enabledList.includes(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an event for the current site.
|
* Log an event for the current site.
|
||||||
*
|
*
|
||||||
|
@ -108,6 +131,11 @@ export const CoreAnalytics = makeSingleton(CoreAnalyticsService);
|
||||||
*/
|
*/
|
||||||
export interface CoreAnalyticsHandler extends CoreDelegateHandler {
|
export interface CoreAnalyticsHandler extends CoreDelegateHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true it means that the handler is enabled or not for the whole app, it doesn't depend on the site.
|
||||||
|
*/
|
||||||
|
appLevelEnabled?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an event.
|
* Log an event.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue