+
+
+
+
+
diff --git a/src/core/user/pages/profile/profile.scss b/src/core/user/pages/profile/profile.scss
index a5844f405..ff977a68f 100644
--- a/src/core/user/pages/profile/profile.scss
+++ b/src/core/user/pages/profile/profile.scss
@@ -5,4 +5,26 @@ page-core-user-profile {
bottom: 30px;
font-size: 24px;
}
+ .core-user-communication-handlers {
+ padding: 6px 2px;
+ background: $list-background-color;
+
+ .core-user-profile-handler {
+ background: $list-background-color;
+ border: 0;
+ color: $core-user-profile-communication-icons-color;
+
+ .icon {
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ max-width: 32px;
+ font-size: 22px;
+ line-height: 32px;
+ color: white;
+ background-color: $core-user-profile-communication-icons-color;
+ margin-bottom: 5px;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/core/user/pages/profile/profile.ts b/src/core/user/pages/profile/profile.ts
index 3d7bd28c7..143c5d850 100644
--- a/src/core/user/pages/profile/profile.ts
+++ b/src/core/user/pages/profile/profile.ts
@@ -23,6 +23,7 @@ import { CoreEventsProvider } from '../../../../providers/events';
import { CoreSitesProvider } from '../../../../providers/sites';
import { CoreMimetypeUtilsProvider } from '../../../../providers/utils/mimetype';
import { CoreFileUploaderHelperProvider } from '../../../fileuploader/providers/helper';
+import { CoreUserDelegate } from '../../providers/delegate';
/**
* Page that displays an user profile page.
@@ -44,11 +45,15 @@ export class CoreUserProfilePage {
title: string;
isDeleted: boolean = false;
canChangeProfilePicture: boolean = false;
+ actionHandlers = [];
+ newPageHandlers = [];
+ communicationHandlers = [];
constructor(private navParams: NavParams, private userProvider: CoreUserProvider, private userHelper: CoreUserHelperProvider,
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private eventsProvider: CoreEventsProvider,
private coursesProvider: CoreCoursesProvider, private sitesProvider: CoreSitesProvider,
- private mimetypeUtils: CoreMimetypeUtilsProvider, private fileUploaderHelper: CoreFileUploaderHelperProvider) {
+ private mimetypeUtils: CoreMimetypeUtilsProvider, private fileUploaderHelper: CoreFileUploaderHelperProvider,
+ private userDelegate: CoreUserDelegate) {
this.userId = navParams.get('userId');
this.courseId = navParams.get('courseId');
@@ -97,6 +102,29 @@ export class CoreUserProfilePage {
this.isLoadingHandlers = true;
+ this.userDelegate.getProfileHandlersFor(user, this.courseId).then((handlers) => {
+ console.error(handlers);
+ this.actionHandlers = [];
+ this.newPageHandlers = [];
+ this.communicationHandlers = [];
+ handlers.forEach((handler) => {
+ switch (handler.type) {
+ case CoreUserDelegate.TYPE_COMMUNICATION:
+ this.communicationHandlers.push(handler.data);
+ break;
+ case CoreUserDelegate.TYPE_ACTION:
+ this.actionHandlers.push(handler.data);
+ break;
+ case CoreUserDelegate.TYPE_NEW_PAGE:
+ default:
+ this.newPageHandlers.push(handler.data);
+ break;
+ }
+ });
+ }).finally(() => {
+ this.isLoadingHandlers = false;
+ });
+
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.user.errorloaduser', true);
});
diff --git a/src/core/user/providers/delegate.ts b/src/core/user/providers/delegate.ts
index b2ea852eb..7bb2f7638 100644
--- a/src/core/user/providers/delegate.ts
+++ b/src/core/user/providers/delegate.ts
@@ -13,18 +13,171 @@
// limitations under the License.
import { Injectable } from '@angular/core';
+import { CoreDelegate, CoreDelegateHandler } from '../../../classes/delegate';
+import { CoreCoursesProvider } from '../../../core/courses/providers/courses';
import { CoreLoggerProvider } from '../../../providers/logger';
+import { CoreSitesProvider } from '../../../providers/sites';
+import { CoreEventsProvider } from '../../../providers/events';
+
+export interface CoreUserProfileHandler extends CoreDelegateHandler {
+ /**
+ * The highest priority is displayed first.
+ * @type {number}
+ */
+ priority: number;
+
+ /**
+ * A type should be specified among these:
+ * - TYPE_COMMUNICATION: will be displayed under the user avatar. Should have icon. Spinner not used.
+ * - TYPE_NEW_PAGE: will be displayed as a list of items. Should have icon. Spinner not used.
+ * Default value if none is specified.
+ * - TYPE_ACTION: will be displayed as a button and should not redirect to any state. Spinner use is recommended.
+ * @type {string}
+ */
+ type: string;
+
+ /**
+ * Whether or not the handler is enabled for a user.
+ * @param {any} user User object.
+ * @param {number} courseId Course ID where to show.
+ * @param {any} [navOptions] Navigation options for the course.
+ * @param {any} [admOptions] Admin options for the course.
+ * @return {boolean|Promise} Whether or not the handler is enabled for a user.
+ */
+ isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean|Promise;
+
+ /**
+ * Returns the data needed to render the handler.
+ * @param {any} user User object.
+ * @param {number} courseId Course ID where to show.
+ * @return {CoreUserProfileHandlerData} Data to be shown.
+ */
+ getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData;
+};
+
+export interface CoreUserProfileHandlerData {
+ /**
+ * Title to display.
+ * @type {string}
+ */
+ title: string;
+
+ /**
+ * Name of the icon to display. Mandatory for TYPE_COMMUNICATION.
+ * @type {string}
+ */
+ icon?: string;
+
+ /**
+ * Additional class to add to the HTML.
+ * @type {string}
+ */
+ class: string;
+
+ /**
+ * If enabled, element will be hidden. Only for TYPE_NEW_PAGE and TYPE_ACTION.
+ * @type {boolean}
+ */
+ hidden?: boolean;
+
+ /**
+ * If enabled will show an spinner. Only for TYPE_ACTION.
+ * @type {boolean}
+ */
+ spinner?: boolean;
+
+ /**
+ * Action to do when clicked.
+ * @param {any} $event
+ * @param {any} user User object.
+ * @param {number} courseId Course ID where to show.
+ * @return {any} Action to be done.
+ */
+ action?($event: any, user: any, courseId: number): any;
+};
/**
* Service to interact with plugins to be shown in user profile. Provides functions to register a plugin
* and notify an update in the data.
*/
@Injectable()
-export class CoreUserDelegate {
- protected logger;
+export class CoreUserDelegate extends CoreDelegate {
+ /**
+ * User profile handler type for communication.
+ * @type {string}
+ */
+ public static TYPE_COMMUNICATION = 'communication';
- constructor(logger: CoreLoggerProvider) {
- this.logger = logger.getInstance('CoreUserDelegate');
+ /**
+ * User profile handler type for new page.
+ * @type {string}
+ */
+ public static TYPE_NEW_PAGE = 'newpage';
+ /**
+ * User profile handler type for actions.
+ * @type {string}
+ */
+ public static TYPE_ACTION = 'action';
+
+ protected handlers: {[s: string]: CoreUserProfileHandler} = {};
+ protected enabledHandlers: {[s: string]: CoreUserProfileHandler} = {};
+ protected featurePrefix = '$mmUserDelegate_';
+
+ constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,
+ private coursesProvider: CoreCoursesProvider, protected eventsProvider: CoreEventsProvider) {
+ super('CoreUserDelegate', loggerProvider, sitesProvider, eventsProvider);
}
+ /**
+ * Get the profile handlers for a user.
+ *
+ * @param {any} user The user object.
+ * @param {number} courseId The course ID.
+ * @return {Promise} Resolved with an array of objects containing 'priority', 'data' and 'type'.
+ */
+ getProfileHandlersFor(user: any, courseId): Promise {
+ let handlers = [],
+ promises = [];
+
+ // Retrieve course options forcing cache.
+ return this.coursesProvider.getUserCourses(true).then((courses) => {
+ let courseIds = courses.map((course) => {
+ return course.id;
+ });
+
+ return this.coursesProvider.getCoursesOptions(courseIds).then((options) => {
+ // For backwards compatibility we don't modify the courseId.
+ let courseIdForOptions = courseId || this.sitesProvider.getSiteHomeId(),
+ navOptions = options.navOptions[courseIdForOptions],
+ admOptions = options.admOptions[courseIdForOptions];
+
+ for (let name in this.enabledHandlers) {
+ // Checks if the handler is enabled for the user.
+ let handler = this.handlers[name],
+ isEnabledForUser = handler.isEnabledForUser(user, courseId, navOptions, admOptions),
+ promise = Promise.resolve(isEnabledForUser).then((enabled) => {
+ if (enabled) {
+ handlers.push({
+ data: handler.getDisplayData(user, courseId),
+ priority: handler.priority,
+ type: handler.type || CoreUserDelegate.TYPE_NEW_PAGE
+ });
+ } else {
+ return Promise.reject(null);
+ }
+ }).catch(function() {
+ // Nothing to do here, it is not enabled for this user.
+ });
+ promises.push(promise);
+ }
+
+ return Promise.all(promises).then(() => {
+ return handlers;
+ });
+ });
+ }).catch(function() {
+ // Never fails.
+ return handlers;
+ });
+ }
}
diff --git a/src/core/user/providers/user-handler.ts b/src/core/user/providers/user-handler.ts
new file mode 100644
index 000000000..3a37255e1
--- /dev/null
+++ b/src/core/user/providers/user-handler.ts
@@ -0,0 +1,70 @@
+// (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 { Injectable } from '@angular/core';
+import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from './delegate';
+import { CoreSitesProvider } from '../../../providers/sites';
+
+/**
+ * Profile links email handler.
+ */
+@Injectable()
+export class CoreUserProfileMailHandler implements CoreUserProfileHandler {
+ name = 'mmUser';
+ priority = 700;
+ type = CoreUserDelegate.TYPE_COMMUNICATION;
+
+ constructor(protected sitesProvider: CoreSitesProvider) {}
+
+ /**
+ * Check if handler is enabled.
+ *
+ * @return {boolean} Always enabled.
+ */
+ isEnabled(): boolean {
+ return true;
+ }
+
+ /**
+ * Check if handler is enabled for this user in this context.
+ *
+ * @param {any} user User to check.
+ * @param {number} courseId Course ID.
+ * @param {any} [navOptions] Course navigation options for current user. See $mmCourses#getUserNavigationOptions.
+ * @param {any} [admOptions] Course admin options for current user. See $mmCourses#getUserAdministrationOptions.
+ * @return {boolean|Promise} Promise resolved with true if enabled, resolved with false otherwise.
+ */
+ isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean|Promise {
+ // Not current user required.
+ return user.id != this.sitesProvider.getCurrentSite().getUserId() && user.email;
+ };
+
+ /**
+ * Returns the data needed to render the handler.
+ *
+ * @return {CoreUserProfileHandlerData} Data needed to render the handler.
+ */
+ getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData {
+ return {
+ icon: 'mail',
+ title: 'core.user.sendemail',
+ class: 'core-user-profile-mail',
+ action: ($event, user, courseId) => {
+ $event.preventDefault();
+ $event.stopPropagation();
+ window.location.href = "mailto:" + user.email;
+ }
+ };
+ }
+}
diff --git a/src/core/user/user.module.ts b/src/core/user/user.module.ts
index 2aeae9a9e..3f052aa1d 100644
--- a/src/core/user/user.module.ts
+++ b/src/core/user/user.module.ts
@@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { CoreUserDelegate } from './providers/delegate';
import { CoreUserProvider } from './providers/user';
import { CoreUserHelperProvider } from './providers/helper';
+import { CoreUserProfileMailHandler } from './providers/user-handler';
@NgModule({
declarations: [
@@ -24,8 +25,13 @@ import { CoreUserHelperProvider } from './providers/helper';
],
providers: [
CoreUserDelegate,
+ CoreUserProfileMailHandler,
CoreUserProvider,
CoreUserHelperProvider
]
})
-export class CoreUserModule {}
+export class CoreUserModule {
+ constructor(userDelegate: CoreUserDelegate, userProfileMailHandler: CoreUserProfileMailHandler) {
+ userDelegate.registerHandler(userProfileMailHandler);
+ }
+}
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index a3e26a43a..ffaec06dd 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -195,3 +195,5 @@ $core-top-tabs-background: $white;
$core-top-tabs-color: $gray-dark;
$core-top-tabs-border: $gray;
$core-top-tabs-color-active: $core-color;
+
+$core-user-profile-communication-icons-color: $core-color;
From ba1267410e83d52f3bb4445a34e158f7ff4ac09e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Tue, 16 Jan 2018 15:52:07 +0100
Subject: [PATCH 05/13] MOBILE-2317 user: Add profile field delegate
---
.../checkbox/checkbox.module.ts | 46 +++++
.../checkbox/component/checkbox.html | 16 ++
.../checkbox/component/checkbox.scss | 0
.../checkbox/component/checkbox.ts | 47 +++++
.../checkbox/providers/handler.ts | 70 ++++++++
.../datetime/component/datetime.html | 10 ++
.../datetime/component/datetime.scss | 0
.../datetime/component/datetime.ts | 63 +++++++
.../datetime/datetime.module.ts | 48 +++++
.../datetime/providers/handler.ts | 85 +++++++++
.../userprofilefield/menu/component/menu.html | 13 ++
.../userprofilefield/menu/component/menu.scss | 0
.../userprofilefield/menu/component/menu.ts | 55 ++++++
.../userprofilefield/menu/menu.module.ts | 48 +++++
.../menu/providers/handler.ts | 70 ++++++++
.../userprofilefield/text/component/text.html | 10 ++
.../userprofilefield/text/component/text.scss | 0
.../userprofilefield/text/component/text.ts | 55 ++++++
.../text/providers/handler.ts | 69 ++++++++
.../userprofilefield/text/text.module.ts | 48 +++++
.../textarea/component/textarea.html | 10 ++
.../textarea/component/textarea.scss | 0
.../textarea/component/textarea.ts | 50 ++++++
.../textarea/providers/handler.ts | 83 +++++++++
.../textarea/textarea.module.ts | 48 +++++
.../userprofilefield.module.ts | 35 ++++
src/app/app.module.ts | 5 +-
src/core/user/components/components.module.ts | 36 ++++
.../user-profile-field.html | 2 +
.../user-profile-field.scss | 5 +
.../user-profile-field/user-profile-field.ts | 98 +++++++++++
src/core/user/pages/about/about.html | 1 +
src/core/user/pages/about/about.module.ts | 2 +
src/core/user/pages/profile/profile.ts | 3 +-
.../{delegate.ts => user-delegate.ts} | 2 +-
src/core/user/providers/user-handler.ts | 2 +-
.../providers/user-profile-field-delegate.ts | 164 ++++++++++++++++++
src/core/user/user.module.ts | 4 +-
38 files changed, 1297 insertions(+), 6 deletions(-)
create mode 100644 src/addon/userprofilefield/checkbox/checkbox.module.ts
create mode 100644 src/addon/userprofilefield/checkbox/component/checkbox.html
create mode 100644 src/addon/userprofilefield/checkbox/component/checkbox.scss
create mode 100644 src/addon/userprofilefield/checkbox/component/checkbox.ts
create mode 100644 src/addon/userprofilefield/checkbox/providers/handler.ts
create mode 100644 src/addon/userprofilefield/datetime/component/datetime.html
create mode 100644 src/addon/userprofilefield/datetime/component/datetime.scss
create mode 100644 src/addon/userprofilefield/datetime/component/datetime.ts
create mode 100644 src/addon/userprofilefield/datetime/datetime.module.ts
create mode 100644 src/addon/userprofilefield/datetime/providers/handler.ts
create mode 100644 src/addon/userprofilefield/menu/component/menu.html
create mode 100644 src/addon/userprofilefield/menu/component/menu.scss
create mode 100644 src/addon/userprofilefield/menu/component/menu.ts
create mode 100644 src/addon/userprofilefield/menu/menu.module.ts
create mode 100644 src/addon/userprofilefield/menu/providers/handler.ts
create mode 100644 src/addon/userprofilefield/text/component/text.html
create mode 100644 src/addon/userprofilefield/text/component/text.scss
create mode 100644 src/addon/userprofilefield/text/component/text.ts
create mode 100644 src/addon/userprofilefield/text/providers/handler.ts
create mode 100644 src/addon/userprofilefield/text/text.module.ts
create mode 100644 src/addon/userprofilefield/textarea/component/textarea.html
create mode 100644 src/addon/userprofilefield/textarea/component/textarea.scss
create mode 100644 src/addon/userprofilefield/textarea/component/textarea.ts
create mode 100644 src/addon/userprofilefield/textarea/providers/handler.ts
create mode 100644 src/addon/userprofilefield/textarea/textarea.module.ts
create mode 100644 src/addon/userprofilefield/userprofilefield.module.ts
create mode 100644 src/core/user/components/components.module.ts
create mode 100644 src/core/user/components/user-profile-field/user-profile-field.html
create mode 100644 src/core/user/components/user-profile-field/user-profile-field.scss
create mode 100644 src/core/user/components/user-profile-field/user-profile-field.ts
rename src/core/user/providers/{delegate.ts => user-delegate.ts} (98%)
create mode 100644 src/core/user/providers/user-profile-field-delegate.ts
diff --git a/src/addon/userprofilefield/checkbox/checkbox.module.ts b/src/addon/userprofilefield/checkbox/checkbox.module.ts
new file mode 100644
index 000000000..1d85b6890
--- /dev/null
+++ b/src/addon/userprofilefield/checkbox/checkbox.module.ts
@@ -0,0 +1,46 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonUserProfileFieldCheckboxHandler } from './providers/handler';
+import { CoreUserProfileFieldDelegate } from '../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldCheckboxComponent } from './component/checkbox';
+import { CoreComponentsModule } from '../../../components/components.module';
+
+@NgModule({
+ declarations: [
+ AddonUserProfileFieldCheckboxComponent
+ ],
+ imports: [
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule
+ ],
+ providers: [
+ AddonUserProfileFieldCheckboxHandler
+ ],
+ exports: [
+ AddonUserProfileFieldCheckboxComponent
+ ],
+ entryComponents: [
+ AddonUserProfileFieldCheckboxComponent
+ ]
+})
+export class AddonUserProfileFieldCheckboxModule {
+ constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldCheckboxHandler) {
+ userProfileFieldDelegate.registerHandler(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.html b/src/addon/userprofilefield/checkbox/component/checkbox.html
new file mode 100644
index 000000000..8ab6fe45f
--- /dev/null
+++ b/src/addon/userprofilefield/checkbox/component/checkbox.html
@@ -0,0 +1,16 @@
+
+
+
{{ field.name }}
+
+ {{ 'core.yes' | translate }}
+
+
+ {{ 'core.no' | translate }}
+
+
+
+
+ {{ field.name }}
+
+
+
\ No newline at end of file
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.scss b/src/addon/userprofilefield/checkbox/component/checkbox.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.ts b/src/addon/userprofilefield/checkbox/component/checkbox.ts
new file mode 100644
index 000000000..28f11a4bf
--- /dev/null
+++ b/src/addon/userprofilefield/checkbox/component/checkbox.ts
@@ -0,0 +1,47 @@
+// (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, Input, OnInit } from '@angular/core';
+
+/**
+ * Directive to render a checkbox user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field-checkbox',
+ templateUrl: 'checkbox.html'
+})
+export class AddonUserProfileFieldCheckboxComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+
+ constructor() {}
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ let field = this.field;
+
+ if (field && this.edit && this.model) {
+ field.modelName = 'profile_field_' + field.shortname;
+
+ // Initialize the value.
+ if (typeof field.defaultdata != 'undefined' && typeof this.model[field.modelName] == 'undefined') {
+ this.model[field.modelName] = field.defaultdata && field.defaultdata !== '0' && field.defaultdata !== 'false';
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/checkbox/providers/handler.ts b/src/addon/userprofilefield/checkbox/providers/handler.ts
new file mode 100644
index 000000000..69c7ac051
--- /dev/null
+++ b/src/addon/userprofilefield/checkbox/providers/handler.ts
@@ -0,0 +1,70 @@
+
+// (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 { Injectable } from '@angular/core';
+import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldCheckboxComponent } from '../component/checkbox';
+
+/**
+ * Checkbox user profile field handlers.
+ */
+@Injectable()
+export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFieldHandler {
+ name = 'checkbox';
+
+ constructor() {}
+
+ /**
+ * Whether or not the handler is enabled on a site level.
+ *
+ * @return {boolean|Promise} True or promise resolved with true if enabled.
+ */
+ isEnabled() : boolean|Promise {
+ return true;
+ }
+
+ /**
+ * Get the data to send for the field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
+ */
+ getData(field: any, signup: boolean, registerAuth: string, model: any): CoreUserProfileFieldHandlerData {
+ let name = 'profile_field_' + field.shortname;
+
+ if (typeof model[name] != 'undefined') {
+ return {
+ type: 'checkbox',
+ name: name,
+ value: model[name] ? 1 : 0
+ };
+ }
+ }
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) {
+ return AddonUserProfileFieldCheckboxComponent;
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/datetime/component/datetime.html b/src/addon/userprofilefield/datetime/component/datetime.html
new file mode 100644
index 000000000..cd19d4595
--- /dev/null
+++ b/src/addon/userprofilefield/datetime/component/datetime.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ {{ field.name }}
+
+
\ No newline at end of file
diff --git a/src/addon/userprofilefield/datetime/component/datetime.scss b/src/addon/userprofilefield/datetime/component/datetime.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/addon/userprofilefield/datetime/component/datetime.ts b/src/addon/userprofilefield/datetime/component/datetime.ts
new file mode 100644
index 000000000..4526abc5e
--- /dev/null
+++ b/src/addon/userprofilefield/datetime/component/datetime.ts
@@ -0,0 +1,63 @@
+// (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, Input, OnInit } from '@angular/core';
+
+/**
+ * Directive to render a datetime user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field-datetime',
+ templateUrl: 'datetime.html'
+})
+export class AddonUserProfileFieldDatetimeComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+
+
+ constructor() {}
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ let field = this.field,
+ year;
+ if (field && this.edit && this.model) {
+ field.modelName = 'profile_field_' + field.shortname;
+
+ // Check if it's only date or it has time too.
+ field.hasTime = field.param3 && field.param3 !== '0' && field.param3 !== 'false';
+ field.format = field.hasTime ? 'core.dffulldate' : 'core.dfdaymonthyear';
+
+ // Check min value.
+ if (field.param1) {
+ year = parseInt(field.param1, 10);
+ if (year) {
+ field.min = year;
+ }
+ }
+
+ // Check max value.
+ if (field.param2) {
+ year = parseInt(field.param2, 10);
+ if (year) {
+ field.max = year;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/datetime/datetime.module.ts b/src/addon/userprofilefield/datetime/datetime.module.ts
new file mode 100644
index 000000000..1697b0e9f
--- /dev/null
+++ b/src/addon/userprofilefield/datetime/datetime.module.ts
@@ -0,0 +1,48 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonUserProfileFieldDatetimeHandler } from './providers/handler';
+import { CoreUserProfileFieldDelegate } from '../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldDatetimeComponent } from './component/datetime';
+import { CoreComponentsModule } from '../../../components/components.module';
+import { CorePipesModule } from '../../../pipes/pipes.module';
+
+@NgModule({
+ declarations: [
+ AddonUserProfileFieldDatetimeComponent
+ ],
+ imports: [
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CorePipesModule
+ ],
+ providers: [
+ AddonUserProfileFieldDatetimeHandler
+ ],
+ exports: [
+ AddonUserProfileFieldDatetimeComponent
+ ],
+ entryComponents: [
+ AddonUserProfileFieldDatetimeComponent
+ ]
+})
+export class AddonUserProfileFieldDatetimeModule {
+ constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldDatetimeHandler) {
+ userProfileFieldDelegate.registerHandler(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/datetime/providers/handler.ts b/src/addon/userprofilefield/datetime/providers/handler.ts
new file mode 100644
index 000000000..ad3c95414
--- /dev/null
+++ b/src/addon/userprofilefield/datetime/providers/handler.ts
@@ -0,0 +1,85 @@
+
+// (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 { Injectable } from '@angular/core';
+import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldDatetimeComponent } from '../component/datetime';
+import { Platform } from 'ionic-angular';
+
+/**
+ * Datetime user profile field handlers.
+ */
+@Injectable()
+export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFieldHandler {
+ name = 'datetime';
+
+ constructor(private platform: Platform) {}
+
+ /**
+ * Whether or not the handler is enabled on a site level.
+ *
+ * @return {boolean|Promise} True or promise resolved with true if enabled.
+ */
+ isEnabled() : boolean|Promise {
+ return true;
+ }
+
+ /**
+ * Get the data to send for the field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
+ */
+ getData(field: any, signup: boolean, registerAuth: string, model: any): CoreUserProfileFieldHandlerData {
+ let hasTime = field.param3 && field.param3 !== '0' && field.param3 !== 'false',
+ modelName = 'profile_field_' + field.shortname,
+ date = JSON.parse(JSON.stringify(model[modelName + '_date'])),
+ time;
+
+ if (date) {
+ if (hasTime && this.platform.is('ios')) {
+ // In iOS the time is in a different input. Add it to the date.
+ time = model[modelName + '_time'];
+ if (!time) {
+ return;
+ }
+
+ date.setHours(time.getHours());
+ date.setMinutes(time.getMinutes());
+ }
+
+ return {
+ type: 'datetime',
+ name: 'profile_field_' + field.shortname,
+ value: Math.round(date.getTime() / 1000)
+ };
+ }
+ }
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) {
+ return AddonUserProfileFieldDatetimeComponent;
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/menu/component/menu.html b/src/addon/userprofilefield/menu/component/menu.html
new file mode 100644
index 000000000..23ee49f9b
--- /dev/null
+++ b/src/addon/userprofilefield/menu/component/menu.html
@@ -0,0 +1,13 @@
+
+
+
{{ field.name }}
+
+
+
+
+ {{ field.name }}
+
+ {{ 'core.choosedots' | translate }}
+ {{option}}
+
+
diff --git a/src/addon/userprofilefield/menu/component/menu.scss b/src/addon/userprofilefield/menu/component/menu.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/addon/userprofilefield/menu/component/menu.ts b/src/addon/userprofilefield/menu/component/menu.ts
new file mode 100644
index 000000000..98bf2f43b
--- /dev/null
+++ b/src/addon/userprofilefield/menu/component/menu.ts
@@ -0,0 +1,55 @@
+// (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, Input, OnInit } from '@angular/core';
+
+/**
+ * Directive to render a menu user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field-menu',
+ templateUrl: 'menu.html'
+})
+export class AddonUserProfileFieldMenuComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+
+ constructor() {}
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ let field = this.field;
+
+ if (field && this.edit && this.model) {
+ field.modelName = 'profile_field_' + field.shortname;
+
+ // Parse options.
+ if (field.param1) {
+ field.options = field.param1.split(/\r\n|\r|\n/g);
+ } else {
+ field.options = [];
+ }
+
+ // Initialize the value using default data.
+ if (typeof field.defaultdata != 'undefined' && typeof this.model[field.modelName] == 'undefined') {
+ this.model[field.modelName] = field.defaultdata;
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/menu/menu.module.ts b/src/addon/userprofilefield/menu/menu.module.ts
new file mode 100644
index 000000000..dfaa1c0ea
--- /dev/null
+++ b/src/addon/userprofilefield/menu/menu.module.ts
@@ -0,0 +1,48 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonUserProfileFieldMenuHandler } from './providers/handler';
+import { CoreUserProfileFieldDelegate } from '../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldMenuComponent } from './component/menu';
+import { CoreComponentsModule } from '../../../components/components.module';
+import { CoreDirectivesModule } from '../../../directives/directives.module';
+
+@NgModule({
+ declarations: [
+ AddonUserProfileFieldMenuComponent
+ ],
+ imports: [
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CoreDirectivesModule
+ ],
+ providers: [
+ AddonUserProfileFieldMenuHandler
+ ],
+ exports: [
+ AddonUserProfileFieldMenuComponent
+ ],
+ entryComponents: [
+ AddonUserProfileFieldMenuComponent
+ ]
+})
+export class AddonUserProfileFieldMenuModule {
+ constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldMenuHandler) {
+ userProfileFieldDelegate.registerHandler(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/menu/providers/handler.ts b/src/addon/userprofilefield/menu/providers/handler.ts
new file mode 100644
index 000000000..470fd1d3d
--- /dev/null
+++ b/src/addon/userprofilefield/menu/providers/handler.ts
@@ -0,0 +1,70 @@
+
+// (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 { Injectable } from '@angular/core';
+import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldMenuComponent } from '../component/menu';
+
+/**
+ * Menu user profile field handlers.
+ */
+@Injectable()
+export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHandler {
+ name = 'menu';
+
+ constructor() {}
+
+ /**
+ * Whether or not the handler is enabled on a site level.
+ *
+ * @return {boolean|Promise} True or promise resolved with true if enabled.
+ */
+ isEnabled() : boolean|Promise {
+ return true;
+ }
+
+ /**
+ * Get the data to send for the field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
+ */
+ getData(field: any, signup: boolean, registerAuth: string, model: any): CoreUserProfileFieldHandlerData {
+ let name = 'profile_field_' + field.shortname;
+
+ if (model[name]) {
+ return {
+ type: 'menu',
+ name: name,
+ value: model[name]
+ };
+ }
+ }
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) {
+ return AddonUserProfileFieldMenuComponent;
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/text/component/text.html b/src/addon/userprofilefield/text/component/text.html
new file mode 100644
index 000000000..c27315320
--- /dev/null
+++ b/src/addon/userprofilefield/text/component/text.html
@@ -0,0 +1,10 @@
+
+
+
{{ field.name }}
+
+
+
+
+ {{ field.name }}
+
+
diff --git a/src/addon/userprofilefield/text/component/text.scss b/src/addon/userprofilefield/text/component/text.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/addon/userprofilefield/text/component/text.ts b/src/addon/userprofilefield/text/component/text.ts
new file mode 100644
index 000000000..f8fd5b72e
--- /dev/null
+++ b/src/addon/userprofilefield/text/component/text.ts
@@ -0,0 +1,55 @@
+// (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, Input, OnInit } from '@angular/core';
+
+/**
+ * Directive to render a text user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field-text',
+ templateUrl: 'text.html'
+})
+export class AddonUserProfileFieldTextComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+
+ constructor() {}
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ let field = this.field;
+
+ if (field && this.edit && this.model) {
+ field.modelName = 'profile_field_' + field.shortname;
+
+ // Check max length.
+ if (field.param2) {
+ field.maxlength = parseInt(field.param2, 10) || '';
+ }
+
+ // Check if it's a password or text.
+ field.inputType = field.param3 && field.param3 !== '0' && field.param3 !== 'false' ? 'password' : 'text';
+
+ // Initialize the value using default data.
+ if (typeof field.defaultdata != 'undefined' && typeof this.model[field.modelName] == 'undefined') {
+ this.model[field.modelName] = field.defaultdata;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/text/providers/handler.ts b/src/addon/userprofilefield/text/providers/handler.ts
new file mode 100644
index 000000000..30949f452
--- /dev/null
+++ b/src/addon/userprofilefield/text/providers/handler.ts
@@ -0,0 +1,69 @@
+
+// (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 { Injectable } from '@angular/core';
+import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldTextComponent } from '../component/text';
+import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
+
+/**
+ * Text user profile field handlers.
+ */
+@Injectable()
+export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHandler {
+ name = 'text';
+
+ constructor(private textUtils: CoreTextUtilsProvider) {}
+
+ /**
+ * Whether or not the handler is enabled on a site level.
+ *
+ * @return {boolean|Promise} True or promise resolved with true if enabled.
+ */
+ isEnabled() : boolean|Promise {
+ return true;
+ }
+
+ /**
+ * Get the data to send for the field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
+ */
+ getData(field: any, signup: boolean, registerAuth: string, model: any): CoreUserProfileFieldHandlerData {
+ let name = 'profile_field_' + field.shortname;
+
+ return {
+ type: 'text',
+ name: name,
+ value: this.textUtils.cleanTags(model[name])
+ };
+ }
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) {
+ return AddonUserProfileFieldTextComponent;
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/text/text.module.ts b/src/addon/userprofilefield/text/text.module.ts
new file mode 100644
index 000000000..4264c858c
--- /dev/null
+++ b/src/addon/userprofilefield/text/text.module.ts
@@ -0,0 +1,48 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonUserProfileFieldTextHandler } from './providers/handler';
+import { CoreUserProfileFieldDelegate } from '../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldTextComponent } from './component/text';
+import { CoreComponentsModule } from '../../../components/components.module';
+import { CoreDirectivesModule } from '../../../directives/directives.module';
+
+@NgModule({
+ declarations: [
+ AddonUserProfileFieldTextComponent
+ ],
+ imports: [
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CoreDirectivesModule
+ ],
+ providers: [
+ AddonUserProfileFieldTextHandler
+ ],
+ exports: [
+ AddonUserProfileFieldTextComponent
+ ],
+ entryComponents: [
+ AddonUserProfileFieldTextComponent
+ ]
+})
+export class AddonUserProfileFieldTextModule {
+ constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextHandler) {
+ userProfileFieldDelegate.registerHandler(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/textarea/component/textarea.html b/src/addon/userprofilefield/textarea/component/textarea.html
new file mode 100644
index 000000000..003d70ef2
--- /dev/null
+++ b/src/addon/userprofilefield/textarea/component/textarea.html
@@ -0,0 +1,10 @@
+
+
+
{{ field.name }}
+
+
+
+
+ {{ field.name }}
+
+
diff --git a/src/addon/userprofilefield/textarea/component/textarea.scss b/src/addon/userprofilefield/textarea/component/textarea.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/addon/userprofilefield/textarea/component/textarea.ts b/src/addon/userprofilefield/textarea/component/textarea.ts
new file mode 100644
index 000000000..967c4081c
--- /dev/null
+++ b/src/addon/userprofilefield/textarea/component/textarea.ts
@@ -0,0 +1,50 @@
+// (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, Input, OnInit } from '@angular/core';
+
+/**
+ * Directive to render a textarea user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field-textarea',
+ templateUrl: 'textarea.html'
+})
+export class AddonUserProfileFieldTextareaComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+
+ constructor() {}
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ let field = this.field;
+
+ if (field && this.edit && this.model) {
+ field.modelName = 'profile_field_' + field.shortname;
+ this.model[field.modelName] = {
+ format: 1
+ };
+
+ // Initialize the value using default data.
+ if (typeof field.defaultdata != 'undefined' && typeof this.model[field.modelName].text == 'undefined') {
+ this.model[field.modelName].text = field.defaultdata;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/textarea/providers/handler.ts b/src/addon/userprofilefield/textarea/providers/handler.ts
new file mode 100644
index 000000000..544c2ad3e
--- /dev/null
+++ b/src/addon/userprofilefield/textarea/providers/handler.ts
@@ -0,0 +1,83 @@
+
+// (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 { Injectable } from '@angular/core';
+import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldTextareaComponent } from '../component/textarea';
+import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
+import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
+
+/**
+ * Textarea user profile field handlers.
+ */
+@Injectable()
+export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFieldHandler {
+ name = 'textarea';
+
+ constructor(private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider) {}
+
+ /**
+ * Whether or not the handler is enabled on a site level.
+ *
+ * @return {boolean|Promise} True or promise resolved with true if enabled.
+ */
+ isEnabled() : boolean|Promise {
+ return true;
+ }
+
+ /**
+ * Get the data to send for the field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {Promise} Data to send for the field.
+ */
+ getData(field: any, signup: boolean, registerAuth: string, model: any): Promise {
+ let name = 'profile_field_' + field.shortname;
+
+ if (model[name]) {
+ return this.domUtils.isRichTextEditorEnabled().then((enabled) => {
+ let text = model[name].text || '';
+ if (!enabled) {
+ // Rich text editor not enabled, add some HTML to the message if needed.
+ text = this.textUtils.formatHtmlLines(text);
+ }
+
+ return {
+ type: 'textarea',
+ name: name,
+ value: JSON.stringify({
+ text: text,
+ format: model[name].format || 1
+ })
+ };
+ });
+ }
+ }
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) {
+ return AddonUserProfileFieldTextareaComponent;
+ }
+
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/textarea/textarea.module.ts b/src/addon/userprofilefield/textarea/textarea.module.ts
new file mode 100644
index 000000000..b1c2212d3
--- /dev/null
+++ b/src/addon/userprofilefield/textarea/textarea.module.ts
@@ -0,0 +1,48 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonUserProfileFieldTextareaHandler } from './providers/handler';
+import { CoreUserProfileFieldDelegate } from '../../../core/user/providers/user-profile-field-delegate';
+import { AddonUserProfileFieldTextareaComponent } from './component/textarea';
+import { CoreComponentsModule } from '../../../components/components.module';
+import { CoreDirectivesModule } from '../../../directives/directives.module';
+
+@NgModule({
+ declarations: [
+ AddonUserProfileFieldTextareaComponent
+ ],
+ imports: [
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CoreDirectivesModule
+ ],
+ providers: [
+ AddonUserProfileFieldTextareaHandler
+ ],
+ exports: [
+ AddonUserProfileFieldTextareaComponent
+ ],
+ entryComponents: [
+ AddonUserProfileFieldTextareaComponent
+ ]
+})
+export class AddonUserProfileFieldTextareaModule {
+ constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextareaHandler) {
+ userProfileFieldDelegate.registerHandler(handler);
+ }
+}
\ No newline at end of file
diff --git a/src/addon/userprofilefield/userprofilefield.module.ts b/src/addon/userprofilefield/userprofilefield.module.ts
new file mode 100644
index 000000000..a946dab74
--- /dev/null
+++ b/src/addon/userprofilefield/userprofilefield.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 { AddonUserProfileFieldCheckboxModule } from './checkbox/checkbox.module';
+import { AddonUserProfileFieldDatetimeModule } from './datetime/datetime.module';
+import { AddonUserProfileFieldMenuModule } from './menu/menu.module';
+import { AddonUserProfileFieldTextModule } from './text/text.module';
+import { AddonUserProfileFieldTextareaModule } from './textarea/textarea.module';
+
+
+@NgModule({
+ declarations: [],
+ imports: [
+ AddonUserProfileFieldCheckboxModule,
+ AddonUserProfileFieldDatetimeModule,
+ AddonUserProfileFieldMenuModule,
+ AddonUserProfileFieldTextModule,
+ AddonUserProfileFieldTextareaModule
+ ],
+ providers: [
+ ],
+ exports: []
+})
+export class AddonUserProfileFieldModule {}
\ No newline at end of file
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 2a824241a..c68d1071b 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -64,6 +64,8 @@ import { CoreUserModule } from '../core/user/user.module';
// Addon modules.
import { AddonCalendarModule } from '../addon/calendar/calendar.module';
+import { AddonUserProfileFieldModule } from '../addon/userprofilefield/userprofilefield.module';
+
// For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) {
@@ -99,7 +101,8 @@ export function createTranslateLoader(http: HttpClient) {
CoreSiteHomeModule,
CoreContentLinksModule,
CoreUserModule,
- AddonCalendarModule
+ AddonCalendarModule,
+ AddonUserProfileFieldModule
],
bootstrap: [IonicApp],
entryComponents: [
diff --git a/src/core/user/components/components.module.ts b/src/core/user/components/components.module.ts
new file mode 100644
index 000000000..13682bbe7
--- /dev/null
+++ b/src/core/user/components/components.module.ts
@@ -0,0 +1,36 @@
+// (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 { CommonModule } from '@angular/common';
+import { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field';
+
+@NgModule({
+ declarations: [
+ CoreUserProfileFieldComponent
+ ],
+ imports: [
+ CommonModule,
+ IonicModule,
+ TranslateModule.forChild(),
+ ],
+ providers: [
+ ],
+ exports: [
+ CoreUserProfileFieldComponent
+ ]
+})
+export class CoreUserComponentsModule {}
diff --git a/src/core/user/components/user-profile-field/user-profile-field.html b/src/core/user/components/user-profile-field/user-profile-field.html
new file mode 100644
index 000000000..825590342
--- /dev/null
+++ b/src/core/user/components/user-profile-field/user-profile-field.html
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/src/core/user/components/user-profile-field/user-profile-field.scss b/src/core/user/components/user-profile-field/user-profile-field.scss
new file mode 100644
index 000000000..a5595aa37
--- /dev/null
+++ b/src/core/user/components/user-profile-field/user-profile-field.scss
@@ -0,0 +1,5 @@
+core-user-profile-field {
+
+}
+
+
diff --git a/src/core/user/components/user-profile-field/user-profile-field.ts b/src/core/user/components/user-profile-field/user-profile-field.ts
new file mode 100644
index 000000000..1b3dcfee0
--- /dev/null
+++ b/src/core/user/components/user-profile-field/user-profile-field.ts
@@ -0,0 +1,98 @@
+// (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, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentRef, OnInit } from '@angular/core';
+import { CoreLoggerProvider } from '../../../../providers/logger';
+import { CoreUserProfileFieldDelegate } from '../../providers/user-profile-field-delegate';
+
+/**
+ * Directive to render user profile field.
+ */
+@Component({
+ selector: 'core-user-profile-field',
+ templateUrl: 'user-profile-field.html'
+})
+export class CoreUserProfileFieldComponent implements OnInit {
+ @Input() field: any; // The profile field to be rendered.
+ @Input() signup?: boolean = false; // True if editing the field in signup. Defaults to false.
+ @Input() edit?: boolean = false; // True if editing the field. Defaults to false.
+ @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+ @Input() registerAuth?: string; // Register auth method. E.g. 'email'.
+
+ // Get the containers where to inject dynamic components. We use a setter because they might be inside a *ngIf.
+ @ViewChild('userProfileField', { read: ViewContainerRef }) set userProfileField (el: ViewContainerRef) {
+ if (this.field) {
+ this.createComponent(this.ufDelegate.getComponent(this.field, this.signup, this.registerAuth), el);
+ } else {
+ // The component hasn't been initialized yet. Store the container.
+ this.fieldContainer = el;
+ }
+ };
+
+ protected logger;
+
+ // Instances and containers of all the components that the handler could define.
+ protected fieldContainer: ViewContainerRef;
+ protected fieldInstance: any;
+
+ constructor(logger: CoreLoggerProvider, private factoryResolver: ComponentFactoryResolver,
+ private ufDelegate: CoreUserProfileFieldDelegate) {
+ this.logger = logger.getInstance('CoreUserProfileFieldComponent');
+ }
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit() {
+ this.createComponent(this.ufDelegate.getComponent(this.field, this.signup, this.registerAuth), this.fieldContainer);
+ }
+
+ /**
+ * Create a component, add it to a container and set the input data.
+ *
+ * @param {any} componentClass The class of the component to create.
+ * @param {ViewContainerRef} container The container to add the component to.
+ * @return {boolean} Whether the component was successfully created.
+ */
+ protected createComponent(componentClass: any, container: ViewContainerRef) : boolean {
+ if (!componentClass || !container) {
+ // No component to instantiate or container doesn't exist right now.
+ return false;
+ }
+
+ if (this.fieldInstance && container === this.fieldContainer) {
+ // Component already instantiated and the component hasn't been destroyed, nothing to do.
+ return true;
+ }
+
+ try {
+ // Create the component and add it to the container.
+ const factory = this.factoryResolver.resolveComponentFactory(componentClass),
+ componentRef = container.createComponent(factory);
+
+ this.fieldContainer = container;
+ this.fieldInstance = componentRef.instance;
+
+ // Set the Input data.
+ this.fieldInstance.field = this.field;
+ this.fieldInstance.edit = this.edit;
+ this.fieldInstance.model = this.model;
+
+ return true;
+ } catch(ex) {
+ this.logger.error('Error creating user field component', ex, componentClass);
+ return false;
+ }
+ }
+}
diff --git a/src/core/user/pages/about/about.html b/src/core/user/pages/about/about.html
index 6556144a4..f95e6f7c3 100644
--- a/src/core/user/pages/about/about.html
+++ b/src/core/user/pages/about/about.html
@@ -59,6 +59,7 @@
{{ 'core.user.interests' | translate}}
+ {{ 'core.user.description' | translate}}
diff --git a/src/core/user/pages/about/about.module.ts b/src/core/user/pages/about/about.module.ts
index 07f43dbba..22ea2afdd 100644
--- a/src/core/user/pages/about/about.module.ts
+++ b/src/core/user/pages/about/about.module.ts
@@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { CoreUserAboutPage } from './about';
import { CoreDirectivesModule } from '../../../../directives/directives.module';
import { CoreComponentsModule } from '../../../../components/components.module';
+import { CoreUserComponentsModule } from '../../components/components.module';
@NgModule({
declarations: [
@@ -26,6 +27,7 @@ import { CoreComponentsModule } from '../../../../components/components.module';
imports: [
CoreComponentsModule,
CoreDirectivesModule,
+ CoreUserComponentsModule,
IonicPageModule.forChild(CoreUserAboutPage),
TranslateModule.forChild()
],
diff --git a/src/core/user/pages/profile/profile.ts b/src/core/user/pages/profile/profile.ts
index 143c5d850..75f9456a1 100644
--- a/src/core/user/pages/profile/profile.ts
+++ b/src/core/user/pages/profile/profile.ts
@@ -23,7 +23,7 @@ import { CoreEventsProvider } from '../../../../providers/events';
import { CoreSitesProvider } from '../../../../providers/sites';
import { CoreMimetypeUtilsProvider } from '../../../../providers/utils/mimetype';
import { CoreFileUploaderHelperProvider } from '../../../fileuploader/providers/helper';
-import { CoreUserDelegate } from '../../providers/delegate';
+import { CoreUserDelegate } from '../../providers/user-delegate';
/**
* Page that displays an user profile page.
@@ -103,7 +103,6 @@ export class CoreUserProfilePage {
this.isLoadingHandlers = true;
this.userDelegate.getProfileHandlersFor(user, this.courseId).then((handlers) => {
- console.error(handlers);
this.actionHandlers = [];
this.newPageHandlers = [];
this.communicationHandlers = [];
diff --git a/src/core/user/providers/delegate.ts b/src/core/user/providers/user-delegate.ts
similarity index 98%
rename from src/core/user/providers/delegate.ts
rename to src/core/user/providers/user-delegate.ts
index 7bb2f7638..659d09ba8 100644
--- a/src/core/user/providers/delegate.ts
+++ b/src/core/user/providers/user-delegate.ts
@@ -124,7 +124,7 @@ export class CoreUserDelegate extends CoreDelegate {
protected featurePrefix = '$mmUserDelegate_';
constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,
- private coursesProvider: CoreCoursesProvider, protected eventsProvider: CoreEventsProvider) {
+ private coursesProvider: CoreCoursesProvider, protected eventsProvider: CoreEventsProvider) {
super('CoreUserDelegate', loggerProvider, sitesProvider, eventsProvider);
}
diff --git a/src/core/user/providers/user-handler.ts b/src/core/user/providers/user-handler.ts
index 3a37255e1..48f439250 100644
--- a/src/core/user/providers/user-handler.ts
+++ b/src/core/user/providers/user-handler.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import { Injectable } from '@angular/core';
-import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from './delegate';
+import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from './user-delegate';
import { CoreSitesProvider } from '../../../providers/sites';
/**
diff --git a/src/core/user/providers/user-profile-field-delegate.ts b/src/core/user/providers/user-profile-field-delegate.ts
new file mode 100644
index 000000000..b3b8784dd
--- /dev/null
+++ b/src/core/user/providers/user-profile-field-delegate.ts
@@ -0,0 +1,164 @@
+// (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 { Injectable } from '@angular/core';
+import { CoreDelegate, CoreDelegateHandler } from '../../../classes/delegate';
+import { CoreLoggerProvider } from '../../../providers/logger';
+import { CoreSitesProvider } from '../../../providers/sites';
+import { CoreEventsProvider } from '../../../providers/events';
+
+export interface CoreUserProfileFieldHandler extends CoreDelegateHandler {
+
+ /**
+ * Return the Component to use to display the user profile field.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} [signup] True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string): any;
+
+ /**
+ * Get the data to send for the field based on the input data.
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {Promise|CoreUserProfileFieldHandlerData} Data to send for the field.
+ */
+ getData?(field: any, signup: boolean, registerAuth: string, model: any):
+ Promise | CoreUserProfileFieldHandlerData;
+};
+
+export interface CoreUserProfileFieldHandlerData {
+ /**
+ * Name to display.
+ * @type {string}
+ */
+ name: string;
+
+ /**
+ * Field type.
+ * @type {string}
+ */
+ type?: string;
+
+ /**
+ * Value of the field.
+ * @type {any}
+ */
+ value: any;
+};
+
+/**
+ * Service to interact with user profile fields. Provides functions to register a plugin.
+ */
+@Injectable()
+export class CoreUserProfileFieldDelegate extends CoreDelegate {
+ protected handlers: {[s: string]: CoreUserProfileFieldHandler} = {};
+ protected enabledHandlers: {[s: string]: CoreUserProfileFieldHandler} = {};
+
+ constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,
+ protected eventsProvider: CoreEventsProvider) {
+ super('CoreUserProfileFieldDelegate', loggerProvider, sitesProvider, eventsProvider);
+ }
+
+ /**
+ * Get the component to use to display an user field.
+ *
+ * @param {any} field User field to get the directive for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} registerAuth Register auth method. E.g. 'email'
+ * @return {any} The component to use, undefined if not found.
+ */
+ getComponent(field: any, signup: boolean, registerAuth: string) : any {
+ let type = field.type || field.datatype;
+ return this.executeFunction(type, 'getComponent', [field, signup, registerAuth]);
+ }
+
+ /**
+ * Get the data to send for a certain field based on the input data.
+ *
+ * @param {any} field User field to get the data for.
+ * @param {boolean} signup True if user is in signup page.
+ * @param {string} registerAuth Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {Promise} Data to send for the field.
+ */
+ getDataForField(field: any, signup: boolean, registerAuth: string, model: any): Promise {
+ let handler = this.getHandler(field, signup);
+
+ if (handler) {
+ let name = 'profile_field_' + field.shortname;
+ if (handler.getData) {
+ return Promise.resolve(handler.getData(field, signup, registerAuth, model));
+ } else if (field.shortname && typeof model[name] != 'undefined') {
+ // Handler doesn't implement the function, but the model has data for the field.
+ return Promise.resolve({
+ type: field.type || field.datatype,
+ name: name,
+ value: model[name]
+ });
+ }
+ }
+ return Promise.reject(null);
+ }
+
+ /**
+ * Get the data to send for a list of fields based on the input data.
+ *
+ * @param {any[]} fields User fields to get the data for.
+ * @param {boolean} [signup] True if user is in signup page.
+ * @param {string} [registerAuth] Register auth method. E.g. 'email'.
+ * @param {any} model Model with the input data.
+ * @return {Promise} Data to send.
+ */
+ getDataForFields(fields: any[], signup = false, registerAuth = "", model: any): Promise {
+ let result = [],
+ promises = [];
+
+ fields.forEach((field) => {
+ this.getDataForField(field, signup, registerAuth, model).then((data) => {
+ result.push(data);
+ }).catch(() => {
+ // Ignore errors.
+ });
+ });
+
+ return Promise.all(promises).then(() => {
+ return result;
+ });
+ }
+
+ /**
+ * Get a handler.
+ *
+ * @param {any} field User field to get the directive for.
+ * @param {boolean} signup True if user is in signup page.
+ * @return {any} Handler.
+ */
+ protected getHandler(field: any, signup: boolean): any {
+ let type = field.type || field.datatype;
+
+ if (signup) {
+ if (this.handlers[type]) {
+ return this.handlers[type];
+ }
+ return false;
+ }
+
+ return this.enabledHandlers[type];
+ }
+}
diff --git a/src/core/user/user.module.ts b/src/core/user/user.module.ts
index 3f052aa1d..474d9171a 100644
--- a/src/core/user/user.module.ts
+++ b/src/core/user/user.module.ts
@@ -13,7 +13,8 @@
// limitations under the License.
import { NgModule } from '@angular/core';
-import { CoreUserDelegate } from './providers/delegate';
+import { CoreUserDelegate } from './providers/user-delegate';
+import { CoreUserProfileFieldDelegate } from './providers/user-profile-field-delegate';
import { CoreUserProvider } from './providers/user';
import { CoreUserHelperProvider } from './providers/helper';
import { CoreUserProfileMailHandler } from './providers/user-handler';
@@ -25,6 +26,7 @@ import { CoreUserProfileMailHandler } from './providers/user-handler';
],
providers: [
CoreUserDelegate,
+ CoreUserProfileFieldDelegate,
CoreUserProfileMailHandler,
CoreUserProvider,
CoreUserHelperProvider
From 61a477afd516bbf72b4221fb08b8f5cbc9230bf9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Thu, 18 Jan 2018 16:38:11 +0100
Subject: [PATCH 06/13] MOBILE-2317 rte: Initial implementation
---
src/components/components.module.ts | 7 +-
.../mark-required/mark-required.scss | 14 +-
.../rich-text-editor/rich-text-editor.html | 29 +++
.../rich-text-editor/rich-text-editor.scss | 71 +++++++
.../rich-text-editor/rich-text-editor.ts | 194 ++++++++++++++++++
5 files changed, 305 insertions(+), 10 deletions(-)
create mode 100644 src/components/rich-text-editor/rich-text-editor.html
create mode 100644 src/components/rich-text-editor/rich-text-editor.scss
create mode 100644 src/components/rich-text-editor/rich-text-editor.ts
diff --git a/src/components/components.module.ts b/src/components/components.module.ts
index a9b03da57..7d62c20f4 100644
--- a/src/components/components.module.ts
+++ b/src/components/components.module.ts
@@ -36,6 +36,7 @@ import { CoreLocalFileComponent } from './local-file/local-file';
import { CoreSitePickerComponent } from './site-picker/site-picker';
import { CoreTabsComponent } from './tabs/tabs';
import { CoreTabComponent } from './tabs/tab';
+import { CoreRichTextEditorComponent } from './rich-text-editor/rich-text-editor';
@NgModule({
declarations: [
@@ -57,7 +58,8 @@ import { CoreTabComponent } from './tabs/tab';
CoreLocalFileComponent,
CoreSitePickerComponent,
CoreTabsComponent,
- CoreTabComponent
+ CoreTabComponent,
+ CoreRichTextEditorComponent
],
entryComponents: [
CoreContextMenuPopoverComponent,
@@ -86,7 +88,8 @@ import { CoreTabComponent } from './tabs/tab';
CoreLocalFileComponent,
CoreSitePickerComponent,
CoreTabsComponent,
- CoreTabComponent
+ CoreTabComponent,
+ CoreRichTextEditorComponent
]
})
export class CoreComponentsModule {}
diff --git a/src/components/mark-required/mark-required.scss b/src/components/mark-required/mark-required.scss
index deacc173b..1e6bebc83 100644
--- a/src/components/mark-required/mark-required.scss
+++ b/src/components/mark-required/mark-required.scss
@@ -1,9 +1,7 @@
-*[core-mark-required] {
- .core-input-required-asterisk, .icon.core-input-required-asterisk {
- color: $red !important;
- font-size: 8px;
- padding-left: 4px;
- line-height: 100%;
- vertical-align: top;
- }
+.core-input-required-asterisk, .icon.core-input-required-asterisk {
+ color: $red !important;
+ font-size: 8px;
+ padding-left: 4px;
+ line-height: 100%;
+ vertical-align: top;
}
diff --git a/src/components/rich-text-editor/rich-text-editor.html b/src/components/rich-text-editor/rich-text-editor.html
new file mode 100644
index 000000000..72eee6e14
--- /dev/null
+++ b/src/components/rich-text-editor/rich-text-editor.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/rich-text-editor/rich-text-editor.scss b/src/components/rich-text-editor/rich-text-editor.scss
new file mode 100644
index 000000000..096779bb8
--- /dev/null
+++ b/src/components/rich-text-editor/rich-text-editor.scss
@@ -0,0 +1,71 @@
+core-rich-text-editor {
+ height: 40vh;
+ overflow: hidden;
+ min-height: 30vh;
+
+ > div {
+ height: 100%;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .core-rte-editor, .core-textarea {
+ padding: 2px;
+ margin: 2px;
+ width: 100%;
+ resize: none;
+ background-color: $white;
+ flex-grow: 1;
+ * {
+ overflow: hidden;
+ }
+ }
+
+ .core-rte-editor {
+ -webkit-user-select: auto !important;
+ word-wrap: break-word;
+ overflow-x: hidden;
+ overflow-y: auto;
+ cursor: text;
+ img {
+ padding-left: 2px;
+ max-width: 95%;
+ }
+ &:empty:before {
+ content: attr(data-placeholder-text);
+ display: block;
+ color: $gray-light;
+ font-weight: bold;
+ }
+ }
+
+ .core-textarea textarea {
+ margin: 0 !important;
+ padding: 0;
+ height: 100% !important;
+ width: 100% !important;
+ resize: none;
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+
+ div.formatOptions {
+ background: $gray-dark;
+ margin: 5px 1px 15px 1px;
+ text-align: center;
+ flex-grow: 0;
+ width: 100%;
+ z-index: 1;
+ button {
+ background: $gray-dark;
+ color: $white;
+ font-size: 1.1em;
+ height: 35px;
+ min-width: 30px;
+ padding-left: 1px;
+ padding-right: 1px;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/rich-text-editor/rich-text-editor.ts b/src/components/rich-text-editor/rich-text-editor.ts
new file mode 100644
index 000000000..3f41f9f49
--- /dev/null
+++ b/src/components/rich-text-editor/rich-text-editor.ts
@@ -0,0 +1,194 @@
+// (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, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
+import { TextInput } from 'ionic-angular';
+import { CoreDomUtilsProvider } from '../../providers/utils/dom';
+import { FormControl } from '@angular/forms';
+import { Keyboard } from '@ionic-native/keyboard';
+
+/**
+ * Directive to display a rich text editor if enabled.
+ *
+* If enabled, this directive will show a rich text editor. Otherwise it'll show a regular textarea.
+ *
+ * This directive requires an OBJECT model. The text written in the editor or textarea will be stored inside
+ * a "text" property in that object. This is to ensure 2-way data-binding, since using a string as a model
+ * could be easily broken.
+ *
+ * Example:
+ *
+ *
+ *
+ * In the example above, the text written in the editor will be stored in newpost.text.
+ */
+@Component({
+ selector: 'core-rich-text-editor',
+ templateUrl: 'rich-text-editor.html'
+})
+export class CoreRichTextEditorComponent {
+ // Based on: https://github.com/judgewest2000/Ionic3RichText/
+ // @todo: Resize, images, anchor button, fullscreen...
+
+ @Input() placeholder?: string = ""; // Placeholder to set in textarea.
+ @Input() control: FormControl; // Form control.
+ @Output() public contentChanged: EventEmitter;
+
+ @ViewChild('editor') editor: ElementRef; // WYSIWYG editor.
+ @ViewChild('textarea') textarea: TextInput; // Textarea editor.
+ @ViewChild('decorate') decorate: ElementRef; // Buttons.
+
+ rteEnabled: boolean = false;
+ uniqueId = `rte{Math.floor(Math.random() * 1000000)}`;
+ editorElement: HTMLDivElement;
+
+ constructor(private domUtils: CoreDomUtilsProvider, private keyboard: Keyboard) {
+ this.contentChanged = new EventEmitter();
+ }
+
+ /**
+ * Init editor
+ */
+ ngAfterContentInit() {
+ this.domUtils.isRichTextEditorEnabled().then((enabled) => {
+ this.rteEnabled = !!enabled;
+ });
+
+ // Setup the editor.
+ this.editorElement = this.editor.nativeElement as HTMLDivElement;
+ this.editorElement.innerHTML = this.control.value;
+ this.textarea.value = this.control.value;
+ this.control.setValue(this.control.value);
+
+ this.editorElement.onchange = this.onChange.bind(this);
+ this.editorElement.onkeyup = this.onChange.bind(this);
+ this.editorElement.onpaste = this.onChange.bind(this);
+ this.editorElement.oninput = this.onChange.bind(this);
+
+ // Setup button actions.
+ let buttons = (this.decorate.nativeElement as HTMLDivElement).getElementsByTagName('button');
+ for (let i = 0; i < buttons.length; i++) {
+ let button = buttons[i],
+ command = button.getAttribute('data-command');
+
+ if (command) {
+ if (command.includes('|')) {
+ let parameter = command.split('|')[1];
+ command = command.split('|')[0];
+
+ button.addEventListener('click', ($event) => {
+ this.buttonAction($event, command, parameter);
+ });
+ } else {
+ button.addEventListener('click', ($event) => {
+ this.buttonAction($event, command);
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * On change function to sync with form data.
+ */
+ onChange($event) {
+ if (this.rteEnabled) {
+ if (this.isNullOrWhiteSpace(this.editorElement.innerText)) {
+ this.clearText();
+ } else {
+ this.control.setValue(this.editorElement.innerHTML);
+ }
+ } else {
+ if (this.isNullOrWhiteSpace(this.textarea.value)) {
+ this.clearText();
+ } else {
+ this.control.setValue(this.textarea.value);
+ }
+ }
+ this.contentChanged.emit(this.control.value);
+ }
+
+ /**
+ * Toggle from rte editor to textarea syncing values.
+ */
+ toggleEditor($event) {
+ $event.preventDefault();
+ $event.stopPropagation();
+
+ if (this.isNullOrWhiteSpace(this.control.value)) {
+ this.clearText();
+ } else {
+ this.editorElement.innerHTML = this.control.value;
+ this.textarea.value = this.control.value;
+ }
+
+ this.rteEnabled = !this.rteEnabled;
+
+ // Set focus and cursor at the end.
+ setTimeout(() => {
+ if (this.rteEnabled) {
+ this.editorElement.focus();
+ let range = document.createRange();
+ range.selectNodeContents(this.editorElement);
+ range.collapse(false);
+ let sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);
+ } else {
+ this.textarea.setFocus();
+ }
+ setTimeout(() => {
+ this.keyboard.show();
+ }, 1);
+ }, 1);
+ }
+
+ /**
+ * Check if text is empty.
+ * @param {string} value text
+ */
+ private isNullOrWhiteSpace(value: string) {
+ if (value == null || typeof value == "undefined") {
+ return true;
+ }
+ value = value.replace(/[\n\r]/g, '');
+ value = value.split(' ').join('');
+
+ return value.length === 0;
+ }
+
+ /**
+ * Clear the text.
+ */
+ clearText() {
+ this.editorElement.innerHTML = '';
+ this.textarea.value = '';
+ this.control.setValue(null);
+ }
+
+ /**
+ * Execute an action over the selected text.
+ * API docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
+ *
+ * @param {any} $event Event data
+ * @param {string} command Command to execute.
+ * @param {any} [parameters] Parameters of the command.
+ */
+ private buttonAction($event: any, command: string, parameters: any = null) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ document.execCommand(command, false, parameters);
+ }
+}
\ No newline at end of file
From d7c6c25292b8de809017d08c1206e22c1fb3a26f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Thu, 18 Jan 2018 16:38:41 +0100
Subject: [PATCH 07/13] MOBILE-2317 login: Add custom user profile field to
sign-up
---
.../checkbox/component/checkbox.html | 5 +-
.../checkbox/component/checkbox.scss | 0
.../checkbox/component/checkbox.ts | 18 +++--
.../checkbox/providers/handler.ts | 13 ++--
.../datetime/component/datetime.html | 6 +-
.../datetime/component/datetime.scss | 0
.../datetime/component/datetime.ts | 22 ++++--
.../datetime/providers/handler.ts | 30 ++------
.../userprofilefield/menu/component/menu.html | 6 +-
.../userprofilefield/menu/component/menu.scss | 0
.../userprofilefield/menu/component/menu.ts | 18 +++--
.../menu/providers/handler.ts | 13 ++--
.../userprofilefield/text/component/text.html | 6 +-
.../userprofilefield/text/component/text.scss | 0
.../userprofilefield/text/component/text.ts | 18 +++--
.../text/providers/handler.ts | 11 ++-
.../textarea/component/textarea.html | 8 +--
.../textarea/component/textarea.scss | 0
.../textarea/component/textarea.ts | 26 ++++---
.../textarea/providers/handler.ts | 42 +++++-------
src/classes/delegate.ts | 46 +++++++++++--
src/core/login/login.module.ts | 2 +-
.../pages/email-signup/email-signup.html | 20 +++---
.../pages/email-signup/email-signup.module.ts | 2 +
.../login/pages/email-signup/email-signup.ts | 33 ++++-----
src/core/login/providers/helper.ts | 10 +--
.../user-profile-field/user-profile-field.ts | 17 +++--
src/core/user/lang/en.json | 3 +
src/core/user/providers/helper.ts | 1 -
.../providers/user-profile-field-delegate.ts | 68 +++++++------------
src/providers/utils/dom.ts | 14 ++++
src/providers/utils/time.ts | 8 +++
32 files changed, 253 insertions(+), 213 deletions(-)
delete mode 100644 src/addon/userprofilefield/checkbox/component/checkbox.scss
delete mode 100644 src/addon/userprofilefield/datetime/component/datetime.scss
delete mode 100644 src/addon/userprofilefield/menu/component/menu.scss
delete mode 100644 src/addon/userprofilefield/text/component/text.scss
delete mode 100644 src/addon/userprofilefield/textarea/component/textarea.scss
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.html b/src/addon/userprofilefield/checkbox/component/checkbox.html
index 8ab6fe45f..41170009e 100644
--- a/src/addon/userprofilefield/checkbox/component/checkbox.html
+++ b/src/addon/userprofilefield/checkbox/component/checkbox.html
@@ -9,8 +9,9 @@
-
+{{ field.name }}
-
+
+
\ No newline at end of file
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.scss b/src/addon/userprofilefield/checkbox/component/checkbox.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/addon/userprofilefield/checkbox/component/checkbox.ts b/src/addon/userprofilefield/checkbox/component/checkbox.ts
index 28f11a4bf..b909bfac7 100644
--- a/src/addon/userprofilefield/checkbox/component/checkbox.ts
+++ b/src/addon/userprofilefield/checkbox/component/checkbox.ts
@@ -13,20 +13,22 @@
// limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
/**
* Directive to render a checkbox user profile field.
*/
@Component({
- selector: 'core-user-profile-field-checkbox',
+ selector: 'addon-user-profile-field-checkbox',
templateUrl: 'checkbox.html'
})
export class AddonUserProfileFieldCheckboxComponent implements OnInit {
@Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false.
- @Input() model?: any; // Model where to store the data. Required if edit=true or signup=true.
+ @Input() disabled?: boolean = false; // True if disabled. Defaults to false.
+ @Input() form?: FormGroup; // Form where to add the form control.
- constructor() {}
+ constructor(private fb: FormBuilder) {}
/**
* Component being initialized.
@@ -34,13 +36,15 @@ export class AddonUserProfileFieldCheckboxComponent implements OnInit {
ngOnInit() {
let field = this.field;
- if (field && this.edit && this.model) {
+ if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname;
// Initialize the value.
- if (typeof field.defaultdata != 'undefined' && typeof this.model[field.modelName] == 'undefined') {
- this.model[field.modelName] = field.defaultdata && field.defaultdata !== '0' && field.defaultdata !== 'false';
- }
+ let formData = {
+ value: field.defaultdata && field.defaultdata !== '0' && field.defaultdata !== 'false',
+ disabled: this.disabled
+ };
+ this.form.addControl(field.modelName, this.fb.control(formData, field.required && !field.locked ? Validators.requiredTrue : null));
}
}
diff --git a/src/addon/userprofilefield/checkbox/providers/handler.ts b/src/addon/userprofilefield/checkbox/providers/handler.ts
index 69c7ac051..ee5147f99 100644
--- a/src/addon/userprofilefield/checkbox/providers/handler.ts
+++ b/src/addon/userprofilefield/checkbox/providers/handler.ts
@@ -40,17 +40,17 @@ export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFiel
* @param {any} field User field to get the data for.
* @param {boolean} signup True if user is in signup page.
* @param {string} [registerAuth] Register auth method. E.g. 'email'.
- * @param {any} model Model with the input data.
+ * @param {any} formValues Form Values.
* @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/
- getData(field: any, signup: boolean, registerAuth: string, model: any): CoreUserProfileFieldHandlerData {
+ getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname;
- if (typeof model[name] != 'undefined') {
+ if (typeof formValues[name] != 'undefined') {
return {
type: 'checkbox',
name: name,
- value: model[name] ? 1 : 0
+ value: formValues[name] ? 1 : 0
};
}
}
@@ -58,12 +58,9 @@ export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFiel
/**
* Return the Component to use to display the user profile field.
*
- * @param {any} field User field to get the data for.
- * @param {boolean} signup True if user is in signup page.
- * @param {string} [registerAuth] Register auth method. E.g. 'email'.
* @return {any} The component to use, undefined if not found.
*/
- getComponent(field: any, signup: boolean, registerAuth: string) {
+ getComponent() {
return AddonUserProfileFieldCheckboxComponent;
}
diff --git a/src/addon/userprofilefield/datetime/component/datetime.html b/src/addon/userprofilefield/datetime/component/datetime.html
index cd19d4595..57b8e7ca2 100644
--- a/src/addon/userprofilefield/datetime/component/datetime.html
+++ b/src/addon/userprofilefield/datetime/component/datetime.html
@@ -4,7 +4,7 @@