diff --git a/src/core/login/pages/email-signup/email-signup.html b/src/core/login/pages/email-signup/email-signup.html
new file mode 100644
index 000000000..77eaa0385
--- /dev/null
+++ b/src/core/login/pages/email-signup/email-signup.html
@@ -0,0 +1,122 @@
+
+
+ {{ 'mm.login.newaccount' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/login/pages/email-signup/email-signup.module.ts b/src/core/login/pages/email-signup/email-signup.module.ts
new file mode 100644
index 000000000..37b1fd5b7
--- /dev/null
+++ b/src/core/login/pages/email-signup/email-signup.module.ts
@@ -0,0 +1,35 @@
+// (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 { NgModule } from '@angular/core';
+import { IonicPageModule } from 'ionic-angular';
+import { CoreLoginEmailSignupPage } from './email-signup';
+import { CoreLoginModule } from '../../login.module';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreComponentsModule } from '../../../../components/components.module';
+import { CoreDirectivesModule } from '../../../../directives/directives.module';
+
+@NgModule({
+ declarations: [
+ CoreLoginEmailSignupPage
+ ],
+ imports: [
+ CoreComponentsModule,
+ CoreDirectivesModule,
+ CoreLoginModule,
+ IonicPageModule.forChild(CoreLoginEmailSignupPage),
+ TranslateModule.forChild()
+ ]
+})
+export class CoreLoginCredentialsPageModule {}
diff --git a/src/core/login/pages/email-signup/email-signup.scss b/src/core/login/pages/email-signup/email-signup.scss
new file mode 100644
index 000000000..cf74cbcf4
--- /dev/null
+++ b/src/core/login/pages/email-signup/email-signup.scss
@@ -0,0 +1,2 @@
+page-core-login-email-signup {
+}
diff --git a/src/core/login/pages/email-signup/email-signup.ts b/src/core/login/pages/email-signup/email-signup.ts
new file mode 100644
index 000000000..93cd7489e
--- /dev/null
+++ b/src/core/login/pages/email-signup/email-signup.ts
@@ -0,0 +1,268 @@
+// (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, ViewChild } from '@angular/core';
+import { IonicPage, NavController, NavParams, Content } from 'ionic-angular';
+import { TranslateService } from '@ngx-translate/core';
+import { CoreSitesProvider } from '../../../../providers/sites';
+import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
+import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
+import { CoreUtilsProvider } from '../../../../providers/utils/utils';
+import { CoreWSProvider } from '../../../../providers/ws';
+import { CoreLoginHelperProvider } from '../../providers/helper';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+/**
+ * Page to signup using email.
+ */
+@IonicPage()
+@Component({
+ selector: 'page-core-login-email-signup',
+ templateUrl: 'email-signup.html',
+})
+export class CoreLoginEmailSignupPage {
+ @ViewChild(Content) content: Content;
+ signupForm: FormGroup;
+ siteUrl: string;
+ siteConfig: any;
+ siteName: string;
+ authInstructions: string;
+ settings: any;
+ countries: any;
+ countriesKeys: any[];
+ categories: any[];
+ settingsLoaded: boolean = false;
+
+ // Validation errors.
+ usernameErrors: any;
+ passwordErrors: any;
+ emailErrors: any;
+ email2Errors: any;
+ policyErrors: any;
+ namefieldsErrors: any;
+
+ constructor(private navCtrl: NavController, navParams: NavParams, private fb: FormBuilder, private wsProvider: CoreWSProvider,
+ private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
+ private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
+ private textUtils: CoreTextUtilsProvider) {
+
+ this.siteUrl = navParams.get('siteUrl');
+
+ // Create the signupForm with the basic controls. More controls will be added later.
+ this.signupForm = this.fb.group({
+ 'username': ['', Validators.required],
+ 'password': ['', Validators.required],
+ 'email': ['', Validators.compose([Validators.required, Validators.email])],
+ 'email2': ['', Validators.compose([Validators.required, Validators.email])]
+ });
+
+ // Setup validation errors.
+ this.usernameErrors = this.loginHelper.getErrorMessages('mm.login.usernamerequired');
+ this.passwordErrors = this.loginHelper.getErrorMessages('mm.login.passwordrequired');
+ this.emailErrors = this.loginHelper.getErrorMessages('mm.login.missingemail');
+ this.email2Errors = this.loginHelper.getErrorMessages('mm.login.missingemail', null, 'mm.login.emailnotmatch');
+ this.policyErrors = this.loginHelper.getErrorMessages('mm.login.policyagree');
+ }
+
+ /**
+ * View loaded.
+ */
+ ionViewDidLoad() {
+ // Fetch the data.
+ this.fetchData().finally(() => {
+ this.settingsLoaded = true;
+ });
+ }
+
+ /**
+ * Complete the FormGroup using the settings received from server.
+ */
+ protected completeFormGroup() {
+ this.signupForm.addControl('city', this.fb.control(this.settings.defaultcity || ''));
+ this.signupForm.addControl('country', this.fb.control(this.settings.country || ''));
+
+ // Add the name fields.
+ for (let i in this.settings.namefields) {
+ this.signupForm.addControl(this.settings.namefields[i], this.fb.control('', Validators.required));
+ }
+
+ if (this.settings.recaptchachallengehash && this.settings.recaptchachallengeimage) {
+ this.signupForm.addControl('recaptcharesponse', this.fb.control('', Validators.required));
+ }
+
+ if (this.settings.sitepolicy) {
+ this.signupForm.addControl('policyagreed', this.fb.control(false, Validators.requiredTrue));
+ }
+ }
+
+ /**
+ * Fetch the required data from the server-
+ */
+ protected fetchData() : Promise {
+ // Get site config.
+ return this.sitesProvider.getSitePublicConfig(this.siteUrl).then((config) => {
+ this.siteConfig = config;
+
+ if (this.treatSiteConfig(config)) {
+ return this.getSignupSettings();
+ }
+ }).then(() => {
+ this.completeFormGroup();
+ }).catch((err) => {
+ this.domUtils.showErrorModal(err);
+ });
+ }
+
+ /**
+ * Get signup settings from server.
+ */
+ protected getSignupSettings() : Promise {
+ return this.wsProvider.callAjax('auth_email_get_signup_settings', {}, {siteUrl: this.siteUrl}).then((settings) => {
+ this.settings = settings;
+ this.categories = this.loginHelper.formatProfileFieldsForSignup(settings.profilefields);
+
+ if (this.signupForm && this.signupForm.controls['recaptcharesponse']) {
+ this.signupForm.controls['recaptcharesponse'].reset(); // Reset captcha.
+ }
+
+ this.namefieldsErrors = {};
+ if (settings.namefields) {
+ settings.namefields.forEach((field) => {
+ this.namefieldsErrors[field] = this.loginHelper.getErrorMessages('mm.login.missing' + field);
+ });
+ }
+
+ return this.utils.getCountryList().then((countries) => {
+ this.countries = countries;
+ this.countriesKeys = Object.keys(countries);
+ });
+ });
+ }
+
+ /**
+ * Treat the site config, checking if it's valid and extracting the data we're interested in.
+ *
+ * @param {any} siteConfig Site config to treat.
+ */
+ protected treatSiteConfig(siteConfig) {
+ if (siteConfig && siteConfig.registerauth == 'email' && !this.loginHelper.isEmailSignupDisabled(siteConfig)) {
+ this.siteName = siteConfig.sitename;
+ this.authInstructions = siteConfig.authinstructions;
+ return true;
+ } else {
+ this.domUtils.showErrorModal(
+ this.translate.instant('mm.login.signupplugindisabled', {$a: this.translate.instant('mm.login.auth_email')}));
+ this.navCtrl.pop();
+ return false;
+ }
+ }
+
+ /**
+ * Pull to refresh.
+ *
+ * @param {any} refresher Refresher.
+ */
+ refreshSettings(refresher: any) : void {
+ this.fetchData().finally(() => {
+ refresher.complete();
+ });
+ }
+
+ /**
+ * Request another captcha.
+ *
+ * @param {boolean} ignoreError Whether to ignore errors.
+ */
+ requestCaptcha(ignoreError?: boolean) : void {
+ let modal = this.domUtils.showModalLoading();
+ this.getSignupSettings().catch((err) => {
+ if (!ignoreError && err) {
+ this.domUtils.showErrorModal(err);
+ }
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+
+ /**
+ * Create account.
+ */
+ create() : void {
+ if (!this.signupForm.valid) {
+ // Form not valid. Scroll to the first element with errors.
+ if (!this.domUtils.scrollToInputError(this.content, document.body)) {
+ // Input not found, show an error modal.
+ this.domUtils.showErrorModal('mm.core.errorinvalidform', true);
+ }
+ } else {
+ let params: any = {
+ username: this.signupForm.value.username.trim().toLowerCase(),
+ password: this.signupForm.value.password,
+ firstname: this.textUtils.cleanTags(this.signupForm.value.firstname),
+ lastname: this.textUtils.cleanTags(this.signupForm.value.lastname),
+ email: this.signupForm.value.email.trim(),
+ city: this.textUtils.cleanTags(this.signupForm.value.city),
+ country: this.signupForm.value.country
+ },
+ modal = this.domUtils.showModalLoading('mm.core.sending', true);
+
+ if (this.siteConfig.launchurl) {
+ let service = this.sitesProvider.determineService(this.siteUrl);
+ params.redirect = this.loginHelper.prepareForSSOLogin(this.siteUrl, service, this.siteConfig.launchurl);
+ }
+
+ if (this.settings.recaptchachallengehash && this.settings.recaptchachallengeimage) {
+ params.recaptchachallengehash = this.settings.recaptchachallengehash;
+ params.recaptcharesponse = this.signupForm.value.recaptcharesponse;
+ }
+
+ // Get the data for the custom profile fields.
+ // @todo: Implement it once profile fields are implemented.
+ // $mmUserProfileFieldsDelegate.getDataForFields(fields, true, 'email', $scope.data).then(function(fieldsData) {
+ // params.customprofilefields = fieldsData;
+
+ this.wsProvider.callAjax('auth_email_signup_user', params, {siteUrl: this.siteUrl}).then((result) => {
+ if (result.success) {
+ // Show alert and ho back.
+ let message = this.translate.instant('mm.login.emailconfirmsent', {$a: params.email});
+ this.domUtils.showAlert('mm.core.success', message);
+ this.navCtrl.pop();
+ } else {
+ if (result.warnings && result.warnings.length) {
+ this.domUtils.showErrorModal(result.warnings[0].message);
+ } else {
+ this.domUtils.showErrorModal('mm.login.usernotaddederror', true);
+ }
+
+ // Error sending, request another capctha since the current one is probably invalid now.
+ this.requestCaptcha(true);
+ }
+ }).catch((error) => {
+ this.domUtils.showErrorModalDefault(error && error.error, 'mm.login.usernotaddederror', true);
+
+ // Error sending, request another capctha since the current one is probably invalid now.
+ this.requestCaptcha(true);
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+ }
+
+ /**
+ * Show authentication instructions.
+ */
+ protected showAuthInstructions() {
+ this.textUtils.expandText(this.translate.instant('mm.login.instructions'), this.authInstructions, true);
+ }
+}
diff --git a/src/core/login/pages/site-error/site-error.html b/src/core/login/pages/site-error/site-error.html
index 960233c44..071683560 100644
--- a/src/core/login/pages/site-error/site-error.html
+++ b/src/core/login/pages/site-error/site-error.html
@@ -21,7 +21,7 @@
{{ 'mm.login.contactyouradministratorissue' | translate:{$a: ''} }}
- {{issue}}
+
diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts
index 7f5aecf20..5d3faa201 100644
--- a/src/core/login/providers/helper.ts
+++ b/src/core/login/providers/helper.ts
@@ -122,6 +122,10 @@ export class CoreLoginHelperProvider {
formatProfileFieldsForSignup(profileFields: any[]) : any {
let categories = {};
+ if (!profileFields) {
+ return categories;
+ }
+
profileFields.forEach((field) => {
if (!field.signup) {
// Not a signup field, ignore it.
@@ -161,7 +165,7 @@ export class CoreLoginHelperProvider {
var errors: any = {};
if (requiredMsg) {
- errors.required = this.translate.instant(requiredMsg);
+ errors.required = errors.requiredTrue = this.translate.instant(requiredMsg);
}
if (emailMsg) {
errors.email = this.translate.instant(emailMsg);
diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts
index b6ec92d47..f6f2ea770 100644
--- a/src/providers/utils/dom.ts
+++ b/src/providers/utils/dom.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import { Injectable } from '@angular/core';
-import { LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform } from 'ionic-angular';
+import { LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreTextUtilsProvider } from './text';
import { CoreAppProvider } from '../app';
@@ -365,7 +365,7 @@ export class CoreDomUtilsProvider {
}
// Finally, check again.
- if (element.className.indexOf(positionParentClass) != -1) {
+ if (element && element.className.indexOf(positionParentClass) != -1) {
element = null;
}
}
@@ -564,13 +564,14 @@ export class CoreDomUtilsProvider {
/**
* Scroll to a certain element inside another element.
*
- * @param {HTMLElement} scrollEl The element that must be scrolled.
+ * @param {Content|HTMLElement} scrollEl The content that must be scrolled.
* @param {HTMLElement} container Element to search in.
* @param {string} [selector] Selector to find the element to scroll to. If not defined, scroll to the container.
* @param {string} [scrollParentClass] Parent class where to stop calculating the position. Default scroll-content.
* @return {boolean} True if the element is found, false otherwise.
*/
- scrollToElement(scrollEl: HTMLElement, container: HTMLElement, selector?: string, scrollParentClass?: string) : boolean {
+ scrollToElement(scrollEl: Content|HTMLElement, container: HTMLElement, selector?: string, scrollParentClass?: string)
+ : boolean {
let position = this.getElementXY(container, selector, scrollParentClass);
if (!position) {
return false;
@@ -583,23 +584,17 @@ export class CoreDomUtilsProvider {
/**
* Search for an input with error (mm-input-error directive) and scrolls to it if found.
*
- * @param {HTMLElement} scrollEl The element that must be scrolled.
+ * @param {Content|HTMLElement} scrollEl The element that must be scrolled.
* @param {HTMLElement} container Element to search in.
* @param [scrollParentClass] Parent class where to stop calculating the position. Default scroll-content.
* @return {boolean} True if the element is found, false otherwise.
*/
- scrollToInputError(scrollEl: HTMLElement, container: HTMLElement, scrollParentClass?: string) : boolean {
- // @todo
- return true;
- // Wait an instant to make sure errors are shown and scroll to the element.
- // return $timeout(function() {
- // if (!scrollDelegate) {
- // scrollDelegate = $ionicScrollDelegate;
- // }
+ scrollToInputError(scrollEl: Content|HTMLElement, container: HTMLElement, scrollParentClass?: string) : boolean {
+ if (!scrollEl) {
+ return false;
+ }
- // scrollDelegate.resize();
- // return self.scrollToElement(container, '.mm-input-has-errors', scrollDelegate, scrollParentClass);
- // }, 100);
+ return this.scrollToElement(scrollEl, container, '.core-input-error', scrollParentClass);
}
/**
diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts
index 3a642ab79..ea2423c27 100644
--- a/src/providers/utils/utils.ts
+++ b/src/providers/utils/utils.ts
@@ -553,8 +553,8 @@ export class CoreUtilsProvider {
for (let name in table) {
if (name.indexOf('mm.core.country-') === 0) {
- name = name.replace('mm.core.country-', '');
- countries[name] = table[name];
+ let code = name.replace('mm.core.country-', '');
+ countries[code] = table[name];
}
}