diff --git a/src/addon/notes/components/components.module.ts b/src/addon/notes/components/components.module.ts
new file mode 100644
index 000000000..4064545e6
--- /dev/null
+++ b/src/addon/notes/components/components.module.ts
@@ -0,0 +1,41 @@
+// (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 { CoreComponentsModule } from '@components/components.module';
+import { AddonNotesTypesComponent } from './types/types';
+
+@NgModule({
+ declarations: [
+ AddonNotesTypesComponent
+ ],
+ imports: [
+ CommonModule,
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ ],
+ providers: [
+ ],
+ exports: [
+ AddonNotesTypesComponent
+ ],
+ entryComponents: [
+ AddonNotesTypesComponent
+ ]
+})
+export class AddonNotesComponentsModule {}
diff --git a/src/addon/notes/components/types/types.html b/src/addon/notes/components/types/types.html
new file mode 100644
index 000000000..87c04392c
--- /dev/null
+++ b/src/addon/notes/components/types/types.html
@@ -0,0 +1,15 @@
+
+
+
+
+ {{ 'addon.notes.sitenotes' | translate }}
+
+
+ {{ 'addon.notes.coursenotes' | translate }}
+
+
+ {{ 'addon.notes.personalnotes' | translate }}
+
+
+
+
diff --git a/src/addon/notes/components/types/types.ts b/src/addon/notes/components/types/types.ts
new file mode 100644
index 000000000..813546b6d
--- /dev/null
+++ b/src/addon/notes/components/types/types.ts
@@ -0,0 +1,62 @@
+// (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 { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
+import { CoreSplitViewComponent } from '@components/split-view/split-view';
+import { CoreSitesProvider } from '@providers/sites';
+
+/**
+ * Component that displays the competencies of a course.
+ */
+@Component({
+ selector: 'addon-notes-types',
+ templateUrl: 'types.html',
+})
+export class AddonNotesTypesComponent implements AfterViewInit, OnInit {
+ @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
+
+ @Input() courseId: number;
+
+ protected type: string;
+
+ constructor(private sitesProvider: CoreSitesProvider) {
+ }
+
+ /**
+ * Properties initialized.
+ */
+ ngOnInit(): void {
+ const siteHomeId = this.sitesProvider.getCurrentSite().getSiteHomeId();
+ this.type = (this.courseId == siteHomeId ? 'site' : 'course');
+ }
+
+ /**
+ * View loaded.
+ */
+ ngAfterViewInit(): void {
+ if (this.splitviewCtrl.isOn()) {
+ this.openList(this.type);
+ }
+ }
+
+ /**
+ * Opens a list of notes.
+ *
+ * @param {string} type
+ */
+ openList(type: string): void {
+ this.type = type;
+ this.splitviewCtrl.push('AddonNotesListPage', {courseId: this.courseId, type: type});
+ }
+}
diff --git a/src/addon/notes/lang/en.json b/src/addon/notes/lang/en.json
new file mode 100644
index 000000000..761417b79
--- /dev/null
+++ b/src/addon/notes/lang/en.json
@@ -0,0 +1,13 @@
+{
+ "addnewnote": "Add a new note",
+ "coursenotes": "Course notes",
+ "eventnotecreated": "Note created",
+ "nonotes": "There are no notes of this type yet.",
+ "note": "Note",
+ "notes": "Notes",
+ "personalnotes": "Personal notes",
+ "publishstate": "Context",
+ "sitenotes": "Site notes",
+ "userwithid": "User with ID {{id}}",
+ "warningnotenotsent": "Couldn't add note(s) to course {{course}}. {{error}}"
+}
\ No newline at end of file
diff --git a/src/addon/notes/notes.module.ts b/src/addon/notes/notes.module.ts
new file mode 100644
index 000000000..4ea5dd0a8
--- /dev/null
+++ b/src/addon/notes/notes.module.ts
@@ -0,0 +1,67 @@
+// (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 { AddonNotesProvider } from './providers/notes';
+import { AddonNotesOfflineProvider } from './providers/notes-offline';
+import { AddonNotesSyncProvider } from './providers/notes-sync';
+import { AddonNotesCourseOptionHandler } from './providers/course-option-handler';
+import { AddonNotesSyncCronHandler } from './providers/sync-cron-handler';
+import { AddonNotesUserHandler } from './providers/user-handler';
+import { AddonNotesComponentsModule } from './components/components.module';
+import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
+import { CoreCronDelegate } from '@providers/cron';
+import { CoreCoursesProvider } from '@core/courses/providers/courses';
+import { CoreEventsProvider } from '@providers/events';
+import { CoreSitesProvider } from '@providers/sites';
+import { CoreUserDelegate } from '@core/user/providers/user-delegate';
+import { CoreUserProvider } from '@core/user/providers/user';
+
+@NgModule({
+ declarations: [
+ ],
+ imports: [
+ AddonNotesComponentsModule
+ ],
+ providers: [
+ AddonNotesProvider,
+ AddonNotesOfflineProvider,
+ AddonNotesSyncProvider,
+ AddonNotesCourseOptionHandler,
+ AddonNotesSyncCronHandler,
+ AddonNotesUserHandler
+ ]
+})
+export class AddonNotesModule {
+ constructor(courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonNotesCourseOptionHandler,
+ userDelegate: CoreUserDelegate, userHandler: AddonNotesUserHandler, cronDelegate: CoreCronDelegate,
+ syncHandler: AddonNotesSyncCronHandler, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
+ // Register handlers.
+ courseOptionsDelegate.registerHandler(courseOptionHandler);
+ userDelegate.registerHandler(userHandler);
+ cronDelegate.register(syncHandler);
+
+ eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
+ courseOptionHandler.clearCoursesNavCache();
+ }, sitesProvider.getCurrentSiteId());
+
+ eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED, () => {
+ courseOptionHandler.clearCoursesNavCache();
+ }, sitesProvider.getCurrentSiteId());
+
+ eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, () => {
+ userHandler.clearAddNoteCache();
+ }, sitesProvider.getCurrentSiteId());
+ }
+}
diff --git a/src/addon/notes/pages/add/add.html b/src/addon/notes/pages/add/add.html
new file mode 100644
index 000000000..59b0bb32d
--- /dev/null
+++ b/src/addon/notes/pages/add/add.html
@@ -0,0 +1,28 @@
+
+
+ {{ 'addon.notes.addnewnote' | translate }}
+
+
+
+
+
+
+
+
diff --git a/src/addon/notes/pages/add/add.module.ts b/src/addon/notes/pages/add/add.module.ts
new file mode 100644
index 000000000..404012014
--- /dev/null
+++ b/src/addon/notes/pages/add/add.module.ts
@@ -0,0 +1,31 @@
+// (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 { AddonNotesAddPage } from './add';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreDirectivesModule } from '@directives/directives.module';
+
+@NgModule({
+ declarations: [
+ AddonNotesAddPage
+ ],
+ imports: [
+ CoreDirectivesModule,
+ IonicPageModule.forChild(AddonNotesAddPage),
+ TranslateModule.forChild()
+ ]
+})
+export class AddonNotesAddPageModule {}
diff --git a/src/addon/notes/pages/add/add.ts b/src/addon/notes/pages/add/add.ts
new file mode 100644
index 000000000..f31f44528
--- /dev/null
+++ b/src/addon/notes/pages/add/add.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 { Component } from '@angular/core';
+import { IonicPage, ViewController, NavParams } from 'ionic-angular';
+import { CoreAppProvider } from '@providers/app';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { AddonNotesProvider } from '../../providers/notes';
+
+/**
+ * Component that displays a text area for composing a note.
+ */
+@IonicPage({ segment: 'addon-notes-add' })
+@Component({
+ selector: 'page-addon-notes-add',
+ templateUrl: 'add.html',
+})
+export class AddonNotesAddPage {
+ userId: number;
+ courseId: number;
+ publishState = 'personal';
+ text = '';
+ processing = false;
+
+ constructor(params: NavParams, private viewCtrl: ViewController, private appProvider: CoreAppProvider,
+ private domUtils: CoreDomUtilsProvider, private notesProvider: AddonNotesProvider) {
+ this.userId = params.get('userId');
+ this.courseId = params.get('courseId');
+ }
+
+ /**
+ * Send the note or store it offline.
+ */
+ addNote(): void {
+ this.appProvider.closeKeyboard();
+ const loadingModal = this.domUtils.showModalLoading('core.sending', true);
+ // Freeze the add note button.
+ this.processing = true;
+ this.notesProvider.addNote(this.userId, this.courseId, this.publishState, this.text).then((sent) => {
+ this.viewCtrl.dismiss().finally(() => {
+ const message = sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline';
+ this.domUtils.showAlertTranslated('core.success', message);
+ });
+ }).catch((error) => {
+ this.domUtils.showErrorModal(error);
+ this.processing = false;
+ }).finally(() => {
+ loadingModal.dismiss();
+ });
+ }
+
+ /**
+ * Close modal.
+ */
+ closeModal(): void {
+ this.viewCtrl.dismiss();
+ }
+}
diff --git a/src/addon/notes/pages/list/list.html b/src/addon/notes/pages/list/list.html
new file mode 100644
index 000000000..02d5f6084
--- /dev/null
+++ b/src/addon/notes/pages/list/list.html
@@ -0,0 +1,42 @@
+
+
+ {{ 'addon.notes.notes' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+