From 96c862260851852dc02b44f17177b75681bcc4a0 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 28 Jan 2020 12:52:56 +0100 Subject: [PATCH 1/9] MOBILE-3323 editor: Create new folder editor in src/core --- .../pages/edit-event/edit-event.module.ts | 2 + .../calendar/pages/edit-event/edit-event.ts | 4 +- .../feedback/comments/comments.module.ts | 4 +- .../onlinetext/onlinetext.module.ts | 4 +- src/addon/mod/data/fields/field.module.ts | 2 +- .../data/fields/textarea/textarea.module.ts | 4 +- .../mod/forum/components/components.module.ts | 4 +- .../forum/pages/edit-post/edit-post.module.ts | 2 + .../new-discussion/new-discussion.module.ts | 2 + .../pages/new-discussion/new-discussion.ts | 4 +- .../mod/glossary/pages/edit/edit.module.ts | 2 + .../mod/lesson/pages/player/player.module.ts | 2 + src/addon/mod/wiki/pages/edit/edit.module.ts | 2 + .../workshop/components/components.module.ts | 4 +- .../pages/assessment/assessment.module.ts | 2 + .../edit-submission/edit-submission.module.ts | 2 + .../pages/submission/submission.module.ts | 2 + src/addon/qtype/essay/essay.module.ts | 4 +- .../textarea/textarea.module.ts | 4 +- src/app/app.module.ts | 2 + src/components/components.module.ts | 3 -- .../editor/components/components.module.ts | 41 +++++++++++++++++++ .../core-editor-rich-text-editor.html} | 0 .../rich-text-editor/rich-text-editor.scss | 0 .../rich-text-editor/rich-text-editor.ts | 14 ++----- src/core/editor/editor.module.ts | 27 ++++++++++++ 26 files changed, 118 insertions(+), 25 deletions(-) create mode 100644 src/core/editor/components/components.module.ts rename src/{components/rich-text-editor/core-rich-text-editor.html => core/editor/components/rich-text-editor/core-editor-rich-text-editor.html} (100%) rename src/{ => core/editor}/components/rich-text-editor/rich-text-editor.scss (100%) rename src/{ => core/editor}/components/rich-text-editor/rich-text-editor.ts (97%) create mode 100644 src/core/editor/editor.module.ts diff --git a/src/addon/calendar/pages/edit-event/edit-event.module.ts b/src/addon/calendar/pages/edit-event/edit-event.module.ts index 32a1ac65a..5821de8a0 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.module.ts +++ b/src/addon/calendar/pages/edit-event/edit-event.module.ts @@ -17,6 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; import { AddonCalendarEditEventPage } from './edit-event'; @NgModule({ @@ -26,6 +27,7 @@ import { AddonCalendarEditEventPage } from './edit-event'; imports: [ CoreComponentsModule, CoreDirectivesModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonCalendarEditEventPage), TranslateModule.forChild() ], diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts index 293d1fd62..16f9350ce 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.ts +++ b/src/addon/calendar/pages/edit-event/edit-event.ts @@ -25,7 +25,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts'; +import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts'; import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar'; import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; import { AddonCalendarHelperProvider } from '../../providers/helper'; @@ -43,7 +43,7 @@ import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; }) export class AddonCalendarEditEventPage implements OnInit, OnDestroy { - @ViewChild(CoreRichTextEditorComponent) descriptionEditor: CoreRichTextEditorComponent; + @ViewChild(CoreEditorRichTextEditorComponent) descriptionEditor: CoreEditorRichTextEditorComponent; title: string; dateFormat: string; diff --git a/src/addon/mod/assign/feedback/comments/comments.module.ts b/src/addon/mod/assign/feedback/comments/comments.module.ts index 491a88fde..cb23c6983 100644 --- a/src/addon/mod/assign/feedback/comments/comments.module.ts +++ b/src/addon/mod/assign/feedback/comments/comments.module.ts @@ -21,6 +21,7 @@ import { AddonModAssignFeedbackCommentsComponent } from './component/comments'; import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -31,7 +32,8 @@ import { CoreDirectivesModule } from '@directives/directives.module'; IonicModule, TranslateModule.forChild(), CoreComponentsModule, - CoreDirectivesModule + CoreDirectivesModule, + CoreEditorComponentsModule, ], providers: [ AddonModAssignFeedbackCommentsHandler diff --git a/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts b/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts index 88de4de1d..5ac1e16f8 100644 --- a/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts +++ b/src/addon/mod/assign/submission/onlinetext/onlinetext.module.ts @@ -21,6 +21,7 @@ import { AddonModAssignSubmissionOnlineTextComponent } from './component/onlinet import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -31,7 +32,8 @@ import { CoreDirectivesModule } from '@directives/directives.module'; IonicModule, TranslateModule.forChild(), CoreComponentsModule, - CoreDirectivesModule + CoreDirectivesModule, + CoreEditorComponentsModule, ], providers: [ AddonModAssignSubmissionOnlineTextHandler diff --git a/src/addon/mod/data/fields/field.module.ts b/src/addon/mod/data/fields/field.module.ts index 0e0467799..9a850d4f6 100644 --- a/src/addon/mod/data/fields/field.module.ts +++ b/src/addon/mod/data/fields/field.module.ts @@ -40,7 +40,7 @@ import { AddonModDataFieldUrlModule } from './url/url.module'; AddonModDataFieldRadiobuttonModule, AddonModDataFieldTextModule, AddonModDataFieldTextareaModule, - AddonModDataFieldUrlModule + AddonModDataFieldUrlModule, ], providers: [ ], diff --git a/src/addon/mod/data/fields/textarea/textarea.module.ts b/src/addon/mod/data/fields/textarea/textarea.module.ts index 3f7070ef7..ead1184b6 100644 --- a/src/addon/mod/data/fields/textarea/textarea.module.ts +++ b/src/addon/mod/data/fields/textarea/textarea.module.ts @@ -20,6 +20,7 @@ import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; import { AddonModDataFieldTextareaComponent } from './component/textarea'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -30,7 +31,8 @@ import { CoreDirectivesModule } from '@directives/directives.module'; IonicModule, TranslateModule.forChild(), CoreComponentsModule, - CoreDirectivesModule + CoreDirectivesModule, + CoreEditorComponentsModule, ], providers: [ AddonModDataFieldTextareaHandler diff --git a/src/addon/mod/forum/components/components.module.ts b/src/addon/mod/forum/components/components.module.ts index 5a3180af3..e9cdcba14 100644 --- a/src/addon/mod/forum/components/components.module.ts +++ b/src/addon/mod/forum/components/components.module.ts @@ -26,6 +26,7 @@ import { AddonModForumIndexComponent } from './index/index'; import { AddonModForumPostComponent } from './post/post'; import { AddonForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu'; import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-options-menu'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -43,7 +44,8 @@ import { AddonForumPostOptionsMenuComponent } from './post-options-menu/post-opt CorePipesModule, CoreCourseComponentsModule, CoreRatingComponentsModule, - CoreTagComponentsModule + CoreTagComponentsModule, + CoreEditorComponentsModule, ], providers: [ ], diff --git a/src/addon/mod/forum/pages/edit-post/edit-post.module.ts b/src/addon/mod/forum/pages/edit-post/edit-post.module.ts index 07f409742..89cf9ad4d 100644 --- a/src/addon/mod/forum/pages/edit-post/edit-post.module.ts +++ b/src/addon/mod/forum/pages/edit-post/edit-post.module.ts @@ -19,6 +19,7 @@ import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModForumComponentsModule } from '../../components/components.module'; import { AddonModForumEditPostPage } from './edit-post'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -28,6 +29,7 @@ import { AddonModForumEditPostPage } from './edit-post'; CoreComponentsModule, CoreDirectivesModule, AddonModForumComponentsModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModForumEditPostPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts b/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts index c0add2f4c..0bb9e4038 100644 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts +++ b/src/addon/mod/forum/pages/new-discussion/new-discussion.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModForumNewDiscussionPage } from './new-discussion'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonModForumNewDiscussionPage } from './new-discussion'; imports: [ CoreComponentsModule, CoreDirectivesModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModForumNewDiscussionPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts b/src/addon/mod/forum/pages/new-discussion/new-discussion.ts index 091753865..68b44837b 100644 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts +++ b/src/addon/mod/forum/pages/new-discussion/new-discussion.ts @@ -25,7 +25,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; -import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts'; +import { CoreEditorRichTextEditorComponent } from '@core/editor/components/rich-text-editor/rich-text-editor.ts'; import { AddonModForumProvider } from '../../providers/forum'; import { AddonModForumOfflineProvider } from '../../providers/offline'; import { AddonModForumHelperProvider } from '../../providers/helper'; @@ -41,7 +41,7 @@ import { AddonModForumSyncProvider } from '../../providers/sync'; }) export class AddonModForumNewDiscussionPage implements OnDestroy { - @ViewChild(CoreRichTextEditorComponent) messageEditor: CoreRichTextEditorComponent; + @ViewChild(CoreEditorRichTextEditorComponent) messageEditor: CoreEditorRichTextEditorComponent; component = AddonModForumProvider.COMPONENT; messageControl = new FormControl(); diff --git a/src/addon/mod/glossary/pages/edit/edit.module.ts b/src/addon/mod/glossary/pages/edit/edit.module.ts index 0f606e93e..926f32ad6 100644 --- a/src/addon/mod/glossary/pages/edit/edit.module.ts +++ b/src/addon/mod/glossary/pages/edit/edit.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModGlossaryEditPage } from './edit'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonModGlossaryEditPage } from './edit'; imports: [ CoreComponentsModule, CoreDirectivesModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModGlossaryEditPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/lesson/pages/player/player.module.ts b/src/addon/mod/lesson/pages/player/player.module.ts index 361ae19ab..c6dd35097 100644 --- a/src/addon/mod/lesson/pages/player/player.module.ts +++ b/src/addon/mod/lesson/pages/player/player.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModLessonPlayerPage } from './player'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonModLessonPlayerPage } from './player'; imports: [ CoreComponentsModule, CoreDirectivesModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModLessonPlayerPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/wiki/pages/edit/edit.module.ts b/src/addon/mod/wiki/pages/edit/edit.module.ts index 20b25f6bc..ecf4b0fa4 100644 --- a/src/addon/mod/wiki/pages/edit/edit.module.ts +++ b/src/addon/mod/wiki/pages/edit/edit.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModWikiEditPage } from './edit'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonModWikiEditPage } from './edit'; imports: [ CoreComponentsModule, CoreDirectivesModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModWikiEditPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/workshop/components/components.module.ts b/src/addon/mod/workshop/components/components.module.ts index 775490396..279484ef6 100644 --- a/src/addon/mod/workshop/components/components.module.ts +++ b/src/addon/mod/workshop/components/components.module.ts @@ -24,6 +24,7 @@ import { AddonModWorkshopIndexComponent } from './index/index'; import { AddonModWorkshopSubmissionComponent } from './submission/submission'; import { AddonModWorkshopAssessmentComponent } from './assessment/assessment'; import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strategy/assessment-strategy'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -39,7 +40,8 @@ import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strate CoreComponentsModule, CoreDirectivesModule, CorePipesModule, - CoreCourseComponentsModule + CoreCourseComponentsModule, + CoreEditorComponentsModule, ], providers: [ ], diff --git a/src/addon/mod/workshop/pages/assessment/assessment.module.ts b/src/addon/mod/workshop/pages/assessment/assessment.module.ts index 21415631e..8a2314b34 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.module.ts +++ b/src/addon/mod/workshop/pages/assessment/assessment.module.ts @@ -19,6 +19,7 @@ import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModWorkshopComponentsModule } from '../../components/components.module'; import { AddonModWorkshopAssessmentPage } from './assessment'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -28,6 +29,7 @@ import { AddonModWorkshopAssessmentPage } from './assessment'; CoreDirectivesModule, CoreComponentsModule, AddonModWorkshopComponentsModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModWorkshopAssessmentPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts index 25e5e4073..d3b216c60 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModWorkshopEditSubmissionPage } from './edit-submission'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonModWorkshopEditSubmissionPage } from './edit-submission'; imports: [ CoreDirectivesModule, CoreComponentsModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModWorkshopEditSubmissionPage), TranslateModule.forChild() ], diff --git a/src/addon/mod/workshop/pages/submission/submission.module.ts b/src/addon/mod/workshop/pages/submission/submission.module.ts index b393b059b..6484fdc06 100644 --- a/src/addon/mod/workshop/pages/submission/submission.module.ts +++ b/src/addon/mod/workshop/pages/submission/submission.module.ts @@ -19,6 +19,7 @@ import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModWorkshopComponentsModule } from '../../components/components.module'; import { AddonModWorkshopSubmissionPage } from './submission'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -28,6 +29,7 @@ import { AddonModWorkshopSubmissionPage } from './submission'; CoreDirectivesModule, CoreComponentsModule, AddonModWorkshopComponentsModule, + CoreEditorComponentsModule, IonicPageModule.forChild(AddonModWorkshopSubmissionPage), TranslateModule.forChild() ], diff --git a/src/addon/qtype/essay/essay.module.ts b/src/addon/qtype/essay/essay.module.ts index da95a1363..d259137cc 100644 --- a/src/addon/qtype/essay/essay.module.ts +++ b/src/addon/qtype/essay/essay.module.ts @@ -20,6 +20,7 @@ import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonQtypeEssayHandler } from './providers/handler'; import { AddonQtypeEssayComponent } from './component/essay'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -29,7 +30,8 @@ import { AddonQtypeEssayComponent } from './component/essay'; IonicModule, TranslateModule.forChild(), CoreComponentsModule, - CoreDirectivesModule + CoreDirectivesModule, + CoreEditorComponentsModule, ], providers: [ AddonQtypeEssayHandler diff --git a/src/addon/userprofilefield/textarea/textarea.module.ts b/src/addon/userprofilefield/textarea/textarea.module.ts index f64fc285d..68ad0e678 100644 --- a/src/addon/userprofilefield/textarea/textarea.module.ts +++ b/src/addon/userprofilefield/textarea/textarea.module.ts @@ -20,6 +20,7 @@ import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile- import { AddonUserProfileFieldTextareaComponent } from './component/textarea'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreEditorComponentsModule } from '@core/editor/components/components.module'; @NgModule({ declarations: [ @@ -29,7 +30,8 @@ import { CoreDirectivesModule } from '@directives/directives.module'; IonicModule, TranslateModule.forChild(), CoreComponentsModule, - CoreDirectivesModule + CoreDirectivesModule, + CoreEditorComponentsModule, ], providers: [ AddonUserProfileFieldTextareaHandler diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 38fff7b5d..81fdb84a7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -87,6 +87,7 @@ import { CoreTagModule } from '@core/tag/tag.module'; import { CoreFilterModule } from '@core/filter/filter.module'; import { CoreH5PModule } from '@core/h5p/h5p.module'; import { CoreSearchModule } from '@core/search/search.module'; +import { CoreEditorModule } from '@core/editor/editor.module'; // Addon modules. import { AddonBadgesModule } from '@addon/badges/badges.module'; @@ -235,6 +236,7 @@ export const WP_PROVIDER: any = null; CoreFilterModule, CoreH5PModule, CoreSearchModule, + CoreEditorModule, AddonBadgesModule, AddonBlogModule, AddonCalendarModule, diff --git a/src/components/components.module.ts b/src/components/components.module.ts index bdd2f6d4c..e48f29dd6 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -39,7 +39,6 @@ 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'; import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; import { CoreDynamicComponent } from './dynamic-component/dynamic-component'; import { CoreSendMessageFormComponent } from './send-message-form/send-message-form'; @@ -79,7 +78,6 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip'; CoreSitePickerComponent, CoreTabsComponent, CoreTabComponent, - CoreRichTextEditorComponent, CoreNavBarButtonsComponent, CoreDynamicComponent, CoreSendMessageFormComponent, @@ -128,7 +126,6 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip'; CoreSitePickerComponent, CoreTabsComponent, CoreTabComponent, - CoreRichTextEditorComponent, CoreNavBarButtonsComponent, CoreDynamicComponent, CoreSendMessageFormComponent, diff --git a/src/core/editor/components/components.module.ts b/src/core/editor/components/components.module.ts new file mode 100644 index 000000000..5d8edf3e7 --- /dev/null +++ b/src/core/editor/components/components.module.ts @@ -0,0 +1,41 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreEditorRichTextEditorComponent } from './rich-text-editor/rich-text-editor'; +import { CoreComponentsModule } from '@components/components.module'; + +@NgModule({ + declarations: [ + CoreEditorRichTextEditorComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + ], + providers: [ + ], + exports: [ + CoreEditorRichTextEditorComponent + ], + entryComponents: [ + CoreEditorRichTextEditorComponent + ] +}) +export class CoreEditorComponentsModule {} diff --git a/src/components/rich-text-editor/core-rich-text-editor.html b/src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html similarity index 100% rename from src/components/rich-text-editor/core-rich-text-editor.html rename to src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html diff --git a/src/components/rich-text-editor/rich-text-editor.scss b/src/core/editor/components/rich-text-editor/rich-text-editor.scss similarity index 100% rename from src/components/rich-text-editor/rich-text-editor.scss rename to src/core/editor/components/rich-text-editor/rich-text-editor.scss diff --git a/src/components/rich-text-editor/rich-text-editor.ts b/src/core/editor/components/rich-text-editor/rich-text-editor.ts similarity index 97% rename from src/components/rich-text-editor/rich-text-editor.ts rename to src/core/editor/components/rich-text-editor/rich-text-editor.ts index d4367e509..8520959c5 100644 --- a/src/components/rich-text-editor/rich-text-editor.ts +++ b/src/core/editor/components/rich-text-editor/rich-text-editor.ts @@ -25,24 +25,18 @@ import { FormControl } from '@angular/forms'; import { Subscription } from 'rxjs'; /** - * Directive to display a rich text editor if enabled. + * Component 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. + * If enabled, this component will show a rich text editor. Otherwise it'll show a regular textarea. * * Example: * - * - * In the example above, the text written in the editor will be stored in newpost.text. */ @Component({ selector: 'core-rich-text-editor', - templateUrl: 'core-rich-text-editor.html' + templateUrl: 'core-editor-rich-text-editor.html' }) -export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy { +export class CoreEditorRichTextEditorComponent implements AfterContentInit, OnDestroy { // Based on: https://github.com/judgewest2000/Ionic3RichText/ // @todo: Anchor button, fullscreen... // @todo: Textarea height is not being updated when editor is resized. Height is calculated if any css is changed. diff --git a/src/core/editor/editor.module.ts b/src/core/editor/editor.module.ts new file mode 100644 index 000000000..1052cfb8b --- /dev/null +++ b/src/core/editor/editor.module.ts @@ -0,0 +1,27 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreEditorComponentsModule } from './components/components.module'; + +@NgModule({ + declarations: [ + ], + imports: [ + CoreEditorComponentsModule + ], + providers: [ + ] +}) +export class CoreEditorModule {} From d2f4df452e56cd02db42df3681a52e65aa86cd20 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 28 Jan 2020 17:57:41 +0100 Subject: [PATCH 2/9] MOBILE-3323 editor: Create offline provider for editor --- src/core/editor/editor.module.ts | 17 +- src/core/editor/providers/editor-offline.ts | 254 ++++++++++++++++++++ 2 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 src/core/editor/providers/editor-offline.ts diff --git a/src/core/editor/editor.module.ts b/src/core/editor/editor.module.ts index 1052cfb8b..e55c88a49 100644 --- a/src/core/editor/editor.module.ts +++ b/src/core/editor/editor.module.ts @@ -14,14 +14,25 @@ import { NgModule } from '@angular/core'; import { CoreEditorComponentsModule } from './components/components.module'; +import { CoreEditorOfflineProvider } from './providers/editor-offline'; + +// List of providers (without handlers). +export const CORE_GRADES_PROVIDERS: any[] = [ + CoreEditorOfflineProvider, +]; @NgModule({ declarations: [ ], imports: [ - CoreEditorComponentsModule + CoreEditorComponentsModule, ], providers: [ - ] + CoreEditorOfflineProvider, + ], }) -export class CoreEditorModule {} +export class CoreEditorModule { + constructor(editorOffline: CoreEditorOfflineProvider) { + // Inject the helper even if it isn't used here it's instantiated. + } +} diff --git a/src/core/editor/providers/editor-offline.ts b/src/core/editor/providers/editor-offline.ts new file mode 100644 index 000000000..0f0b5e28c --- /dev/null +++ b/src/core/editor/providers/editor-offline.ts @@ -0,0 +1,254 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { CoreLoggerProvider } from '@providers/logger'; +import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { CoreUtilsProvider } from '@providers/utils/utils'; + +/** + * Service with features regarding rich text editor in offline. + */ +@Injectable() +export class CoreEditorOfflineProvider { + + protected DRAFT_TABLE = 'editor_draft'; + + protected logger; + protected siteSchema: CoreSiteSchema = { + name: 'CoreEditorProvider', + version: 1, + tables: [ + { + name: this.DRAFT_TABLE, + columns: [ + { + name: 'contextlevel', + type: 'TEXT', + }, + { + name: 'contextinstanceid', + type: 'INTEGER', + }, + { + name: 'elementid', + type: 'TEXT', + }, + { + name: 'extraparams', // Moodle web uses a page hash built with URL. App will use some params stringified. + type: 'TEXT', + }, + { + name: 'drafttext', + type: 'TEXT', + notNull: true + }, + { + name: 'pageinstance', + type: 'TEXT', + notNull: true + }, + { + name: 'timecreated', + type: 'INTEGER', + notNull: true + }, + { + name: 'timemodified', + type: 'INTEGER', + notNull: true + }, + ], + primaryKeys: ['contextlevel', 'contextinstanceid', 'elementid', 'extraparams'] + }, + ], + }; + + constructor( + logger: CoreLoggerProvider, + protected sitesProvider: CoreSitesProvider, + protected textUtils: CoreTextUtilsProvider, + protected utils: CoreUtilsProvider) { + this.logger = logger.getInstance('CoreEditorProvider'); + + this.sitesProvider.registerSiteSchema(this.siteSchema); + } + + /** + * Delete a draft from DB. + * + * @param contextLevel Context level. + * @param contextInstanceId The instance ID related to the context. + * @param elementId Element ID. + * @param extraParams Object with extra params to identify the draft. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved when done. + */ + async deleteDraft(contextLevel: string, contextInstanceId: number, elementId: string, extraParams: {[name: string]: any}, + siteId?: string): Promise { + + try { + const db = await this.sitesProvider.getSiteDb(siteId); + + const params = this.fixDraftPrimaryData(contextLevel, contextInstanceId, elementId, extraParams); + + return db.deleteRecords(this.DRAFT_TABLE, params); + } catch (error) { + // Ignore errors, probably no draft stored. + } + } + + /** + * Return an object with the draft primary data converted to the right format. + * + * @param contextLevel Context level. + * @param contextInstanceId The instance ID related to the context. + * @param elementId Element ID. + * @param extraParams Object with extra params to identify the draft. + * @return Object with the fixed primary data. + */ + protected fixDraftPrimaryData(contextLevel: string, contextInstanceId: number, elementId: string, + extraParams: {[name: string]: any}): CoreEditorDraftPrimaryData { + + return { + contextlevel: contextLevel, + contextinstanceid: contextInstanceId, + elementid: elementId, + extraparams: this.utils.sortAndStringify(extraParams || {}), + }; + } + + /** + * Get a draft from DB. + * + * @param contextLevel Context level. + * @param contextInstanceId The instance ID related to the context. + * @param elementId Element ID. + * @param extraParams Object with extra params to identify the draft. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with the draft data. Undefined if no draft stored. + */ + async getDraft(contextLevel: string, contextInstanceId: number, elementId: string, extraParams: {[name: string]: any}, + siteId?: string): Promise { + + const db = await this.sitesProvider.getSiteDb(siteId); + + const params = this.fixDraftPrimaryData(contextLevel, contextInstanceId, elementId, extraParams); + + return db.getRecord(this.DRAFT_TABLE, params); + } + + /** + * Get draft to resume it. + * + * @param contextLevel Context level. + * @param contextInstanceId The instance ID related to the context. + * @param elementId Element ID. + * @param extraParams Object with extra params to identify the draft. + * @param pageInstance Unique identifier to prevent storing data from several sources at the same time. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with the draft text. Undefined if no draft stored. + */ + async resumeDraft(contextLevel: string, contextInstanceId: number, elementId: string, extraParams: {[name: string]: any}, + pageInstance: string, siteId?: string): Promise { + + try { + // Check if there is a draft stored. + const entry = await this.getDraft(contextLevel, contextInstanceId, elementId, extraParams, siteId); + + // There is a draft stored. Update its page instance. + try { + const db = await this.sitesProvider.getSiteDb(siteId); + + entry.pageinstance = pageInstance; + entry.timemodified = Date.now(); + + await db.insertRecord(this.DRAFT_TABLE, entry); + } catch (error) { + // Ignore errors saving the draft. It shouldn't happen. + } + + return entry.drafttext; + } catch (error) { + // No draft stored. Store an empty draft to save the pageinstance. + await this.saveDraft(contextLevel, contextInstanceId, elementId, extraParams, pageInstance, '', siteId); + } + } + + /** + * Save a draft in DB. + * + * @param contextLevel Context level. + * @param contextInstanceId The instance ID related to the context. + * @param elementId Element ID. + * @param extraParams Object with extra params to identify the draft. + * @param pageInstance Unique identifier to prevent storing data from several sources at the same time. + * @param draftText The text to store. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved when done. + */ + async saveDraft(contextLevel: string, contextInstanceId: number, elementId: string, extraParams: {[name: string]: any}, + pageInstance: string, draftText: string, siteId?: string): Promise { + + let timecreated = Date.now(); + let entry: CoreEditorDraft; + + // Check if there is a draft already stored. + try { + entry = await this.getDraft(contextLevel, contextInstanceId, elementId, extraParams, siteId); + + timecreated = entry.timecreated; + } catch (error) { + // No draft already stored. + } + + if (entry && entry.pageinstance != pageInstance) { + this.logger.warning(`Discarding draft because of pageinstance. Context '${contextLevel}' '${contextInstanceId}', ` + + `element '${elementId}'`); + throw null; + } + + const db = await this.sitesProvider.getSiteDb(siteId); + + const data: CoreEditorDraft = this.fixDraftPrimaryData(contextLevel, contextInstanceId, elementId, extraParams); + + data.drafttext = (draftText || '').trim(); + data.pageinstance = pageInstance; + data.timecreated = timecreated; + data.timemodified = Date.now(); + + await db.insertRecord(this.DRAFT_TABLE, data); + } +} + +/** + * Primary data to identify a stored draft. + */ +type CoreEditorDraftPrimaryData = { + contextlevel: string; // Context level. + contextinstanceid: number; // The instance ID related to the context. + elementid: string; // Element ID. + extraparams: string; // Extra params stringified. +}; + +/** + * Draft data stored. + */ +type CoreEditorDraft = CoreEditorDraftPrimaryData & { + drafttext?: string; // Draft text stored. + pageinstance?: string; // Unique identifier to prevent storing data from several sources at the same time. + timecreated?: number; // Time created. + timemodified?: number; // Time modified. +}; From 5a79151b01f1999de6c1f927cbe00c1fa28d303a Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 31 Jan 2020 10:08:24 +0100 Subject: [PATCH 3/9] MOBILE-3323 editor: Save and restore drafts --- scripts/langindex.json | 2 + .../calendar/pages/edit-event/edit-event.html | 2 +- .../addon-mod-assign-feedback-comments.html | 3 +- ...ddon-mod-assign-submission-onlinetext.html | 2 +- .../onlinetext/component/onlinetext.ts | 14 +- .../addon-mod-data-field-textarea.html | 2 +- .../fields/textarea/component/textarea.ts | 6 +- .../components/post/addon-mod-forum-post.html | 2 +- .../edit-post/addon-mod-forum-edit-post.html | 2 +- .../pages/new-discussion/new-discussion.html | 2 +- src/addon/mod/glossary/pages/edit/edit.html | 2 +- src/addon/mod/glossary/pages/edit/edit.ts | 5 + src/addon/mod/lesson/pages/player/player.html | 2 +- src/addon/mod/wiki/pages/edit/edit.html | 2 +- src/addon/mod/wiki/pages/edit/edit.ts | 15 ++ ...ddon-mod-workshop-assessment-strategy.html | 2 +- .../workshop/pages/assessment/assessment.html | 2 +- .../edit-submission/edit-submission.html | 2 +- .../pages/edit-submission/edit-submission.ts | 5 + .../workshop/pages/submission/submission.html | 2 +- .../essay/component/addon-qtype-essay.html | 2 +- .../addon-user-profile-field-textarea.html | 2 +- src/assets/lang/en.json | 2 + .../core-editor-rich-text-editor.html | 13 +- .../rich-text-editor/rich-text-editor.scss | 18 ++- .../rich-text-editor/rich-text-editor.ts | 142 +++++++++++++++++- src/core/editor/lang/en.json | 4 + src/theme/variables.scss | 5 - 28 files changed, 228 insertions(+), 36 deletions(-) create mode 100644 src/core/editor/lang/en.json diff --git a/scripts/langindex.json b/scripts/langindex.json index 344e7460d..339c56c5a 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1482,6 +1482,8 @@ "core.downloaded": "local_moodlemobileapp", "core.downloading": "local_moodlemobileapp", "core.edit": "moodle", + "core.editor.autosavesucceeded": "editor_atto", + "core.editor.textrecovered": "editor_atto", "core.emptysplit": "local_moodlemobileapp", "core.error": "moodle", "core.errorchangecompletion": "local_moodlemobileapp", diff --git a/src/addon/calendar/pages/edit-event/edit-event.html b/src/addon/calendar/pages/edit-event/edit-event.html index 9eae210f8..55a2cd584 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.html +++ b/src/addon/calendar/pages/edit-event/edit-event.html @@ -86,7 +86,7 @@

{{ 'core.description' | translate }}

- +
diff --git a/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html b/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html index 6a2fff370..75892386b 100644 --- a/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html +++ b/src/addon/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html @@ -19,5 +19,6 @@ - + + diff --git a/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html b/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html index a629ab325..a659af7a9 100644 --- a/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html +++ b/src/addon/mod/assign/submission/onlinetext/component/addon-mod-assign-submission-onlinetext.html @@ -15,6 +15,6 @@

{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}

- + diff --git a/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts b/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts index 70ee9d183..a788de667 100644 --- a/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts +++ b/src/addon/mod/assign/submission/onlinetext/component/onlinetext.ts @@ -14,6 +14,7 @@ import { Component, OnInit, ElementRef } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { AddonModAssignProvider } from '../../../providers/assign'; @@ -35,16 +36,23 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS text: string; loaded: boolean; wordLimitEnabled: boolean; + currentUserId: number; protected wordCountTimeout: any; protected element: HTMLElement; - constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, - protected assignProvider: AddonModAssignProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider, - element: ElementRef) { + constructor( + protected fb: FormBuilder, + protected domUtils: CoreDomUtilsProvider, + protected textUtils: CoreTextUtilsProvider, + protected assignProvider: AddonModAssignProvider, + protected assignOfflineProvider: AddonModAssignOfflineProvider, + element: ElementRef, + sitesProvider: CoreSitesProvider) { super(); this.element = element.nativeElement; + this.currentUserId = sitesProvider.getCurrentSiteUserId(); } /** diff --git a/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html b/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html index 02ce6f367..680b92f89 100644 --- a/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html +++ b/src/addon/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html @@ -2,7 +2,7 @@ - + diff --git a/src/addon/mod/data/fields/textarea/component/textarea.ts b/src/addon/mod/data/fields/textarea/component/textarea.ts index ba54212d7..d0d03359a 100644 --- a/src/addon/mod/data/fields/textarea/component/textarea.ts +++ b/src/addon/mod/data/fields/textarea/component/textarea.ts @@ -49,10 +49,10 @@ export class AddonModDataFieldTextareaComponent extends AddonModDataFieldPluginC * Initialize field. */ protected init(): void { - if (this.isShowOrListMode()) { - this.component = AddonModDataProvider.COMPONENT; - this.componentId = this.database.coursemodule; + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database.coursemodule; + if (this.isShowOrListMode()) { return; } diff --git a/src/addon/mod/forum/components/post/addon-mod-forum-post.html b/src/addon/mod/forum/components/post/addon-mod-forum-post.html index 02a3bbe42..cafe72514 100644 --- a/src/addon/mod/forum/components/post/addon-mod-forum-post.html +++ b/src/addon/mod/forum/components/post/addon-mod-forum-post.html @@ -64,7 +64,7 @@ {{ 'addon.mod_forum.message' | translate }} - + {{ 'addon.mod_forum.privatereply' | translate }} diff --git a/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html b/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html index 0a934fa27..3fd8d6e4a 100644 --- a/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html +++ b/src/addon/mod/forum/pages/edit-post/addon-mod-forum-edit-post.html @@ -16,7 +16,7 @@ {{ 'addon.mod_forum.message' | translate }} - + diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.html b/src/addon/mod/forum/pages/new-discussion/new-discussion.html index 95bb6e5ce..6cfeb39f8 100644 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.html +++ b/src/addon/mod/forum/pages/new-discussion/new-discussion.html @@ -19,7 +19,7 @@ {{ 'addon.mod_forum.message' | translate }} - + diff --git a/src/addon/mod/glossary/pages/edit/edit.html b/src/addon/mod/glossary/pages/edit/edit.html index 3c3bfa0d7..0bf02b458 100644 --- a/src/addon/mod/glossary/pages/edit/edit.html +++ b/src/addon/mod/glossary/pages/edit/edit.html @@ -15,7 +15,7 @@ {{ 'addon.mod_glossary.definition' | translate }} - + {{ 'addon.mod_glossary.categories' | translate }} diff --git a/src/addon/mod/glossary/pages/edit/edit.ts b/src/addon/mod/glossary/pages/edit/edit.ts index 7bb9e60eb..9cf8b4355 100644 --- a/src/addon/mod/glossary/pages/edit/edit.ts +++ b/src/addon/mod/glossary/pages/edit/edit.ts @@ -51,6 +51,7 @@ export class AddonModGlossaryEditPage implements OnInit { attachments = []; definitionControl = new FormControl(); categories = []; + editorExtraParams: {[name: string]: any} = {}; protected courseId: number; protected module: any; @@ -113,6 +114,10 @@ export class AddonModGlossaryEditPage implements OnInit { this.originalData.files = files.slice(); }); } + + if (entry.id) { + this.editorExtraParams.id = entry.id; + } } this.definitionControl.setValue(this.entry.definition); diff --git a/src/addon/mod/lesson/pages/player/player.html b/src/addon/mod/lesson/pages/player/player.html index 13c7e7f0b..b8526c401 100644 --- a/src/addon/mod/lesson/pages/player/player.html +++ b/src/addon/mod/lesson/pages/player/player.html @@ -57,7 +57,7 @@ - +

{{ 'addon.mod_lesson.youranswer' | translate }}

diff --git a/src/addon/mod/wiki/pages/edit/edit.html b/src/addon/mod/wiki/pages/edit/edit.html index c9f61f896..2381df6c3 100644 --- a/src/addon/mod/wiki/pages/edit/edit.html +++ b/src/addon/mod/wiki/pages/edit/edit.html @@ -17,7 +17,7 @@
- + diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts index 160bfeeb2..033ec982e 100644 --- a/src/addon/mod/wiki/pages/edit/edit.ts +++ b/src/addon/mod/wiki/pages/edit/edit.ts @@ -45,6 +45,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { component = AddonModWikiProvider.COMPONENT; // Component to link the files to. componentId: number; // Component ID to link the files to. wrongVersionLock: boolean; // Whether the page lock doesn't match the initial one. + editorExtraParams: {[name: string]: any} = {}; protected module: any; // Wiki module instance. protected courseId: number; // Course the wiki belongs to. @@ -101,6 +102,20 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { // Block the wiki so it cannot be synced. this.syncProvider.blockOperation(this.component, this.blockId); + + if (!this.module.id) { + this.editorExtraParams.type = 'wiki'; + } + + if (this.pageId) { + this.editorExtraParams.pageid = this.pageId; + + if (this.section) { + this.editorExtraParams.section = this.section; + } + } else if (pageTitle) { + this.editorExtraParams.pagetitle = pageTitle; + } } /** diff --git a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html index 7f3b2bba7..b79857e1d 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html +++ b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html @@ -16,7 +16,7 @@ {{ 'addon.mod_workshop.feedbackauthor' | translate }} - + {{ 'addon.mod_workshop.feedbackreviewer' | translate }} - + diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html index 71b1e535d..a7f3bad68 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html @@ -18,7 +18,7 @@ {{ 'addon.mod_workshop.submissioncontent' | translate }} - + diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts index 816a29b46..c3fb1edbb 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts @@ -51,6 +51,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { component = AddonModWorkshopProvider.COMPONENT; componentId: number; editForm: FormGroup; // The form group. + editorExtraParams: {[name: string]: any} = {}; protected workshopId: number; protected submissionId: number; @@ -86,6 +87,10 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { this.editForm = new FormGroup({}); this.editForm.addControl('title', this.fb.control('', Validators.required)); this.editForm.addControl('content', this.fb.control('')); + + if (this.submissionId) { + this.editorExtraParams.id = this.submissionId; + } } /** diff --git a/src/addon/mod/workshop/pages/submission/submission.html b/src/addon/mod/workshop/pages/submission/submission.html index 41fbecd1d..0951709ae 100644 --- a/src/addon/mod/workshop/pages/submission/submission.html +++ b/src/addon/mod/workshop/pages/submission/submission.html @@ -87,7 +87,7 @@
{{ 'addon.mod_workshop.feedbackauthor' | translate }} - + diff --git a/src/addon/qtype/essay/component/addon-qtype-essay.html b/src/addon/qtype/essay/component/addon-qtype-essay.html index 5c171fedc..13462423b 100644 --- a/src/addon/qtype/essay/component/addon-qtype-essay.html +++ b/src/addon/qtype/essay/component/addon-qtype-essay.html @@ -11,7 +11,7 @@ - + diff --git a/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html b/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html index 1d149a8ed..8908db521 100644 --- a/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html +++ b/src/addon/userprofilefield/textarea/component/addon-user-profile-field-textarea.html @@ -9,5 +9,5 @@ {{ field.name }} - + \ No newline at end of file diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index e9673f525..d3f4ff056 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -1482,6 +1482,8 @@ "core.downloaded": "Downloaded", "core.downloading": "Downloading", "core.edit": "Edit", + "core.editor.autosavesucceeded": "Draft saved.", + "core.editor.textrecovered": "A draft version of this text was automatically restored.", "core.emptysplit": "This page will appear blank if the left panel is empty or is loading.", "core.error": "Error", "core.errorchangecompletion": "An error occurred while changing the completion status. Please try again.", diff --git a/src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html b/src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html index ba2bd897f..6b3a503ed 100644 --- a/src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html +++ b/src/core/editor/components/rich-text-editor/core-editor-rich-text-editor.html @@ -1,7 +1,14 @@ -
-
+
+
+
- + + +
+ + {{ infoMessage | translate }} +
+
diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts index 41c02baaf..599f8aa86 100644 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts +++ b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; import { @@ -36,10 +38,17 @@ export class AddonModAssignEditFeedbackModalPage { @Input() plugin: AddonModAssignPlugin; // The plugin object. @Input() userId: number; // The user ID of the submission. + @ViewChild('editFeedbackForm') formElement: ElementRef; + protected forceLeave = false; // To allow leaving the page without checking for changes. - constructor(params: NavParams, protected viewCtrl: ViewController, protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, protected feedbackDelegate: AddonModAssignFeedbackDelegate) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + protected domUtils: CoreDomUtilsProvider, + protected translate: TranslateService, + protected feedbackDelegate: AddonModAssignFeedbackDelegate, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.assign = params.get('assign'); this.submission = params.get('submission'); @@ -82,6 +91,11 @@ export class AddonModAssignEditFeedbackModalPage { e.preventDefault(); e.stopPropagation(); + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + // Close the modal, sending the input data. this.forceLeave = true; this.closeModal(this.getInputData()); diff --git a/src/addon/mod/assign/pages/edit/edit.html b/src/addon/mod/assign/pages/edit/edit.html index c85acfcf9..f849d0390 100644 --- a/src/addon/mod/assign/pages/edit/edit.html +++ b/src/addon/mod/assign/pages/edit/edit.html @@ -13,7 +13,7 @@ -
+ diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts index 7eccd3c20..901cc2baa 100644 --- a/src/addon/mod/assign/pages/edit/edit.ts +++ b/src/addon/mod/assign/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; @@ -34,6 +34,9 @@ import { AddonModAssignHelperProvider } from '../../providers/helper'; templateUrl: 'edit.html', }) export class AddonModAssignEditPage implements OnInit, OnDestroy { + + @ViewChild('editSubmissionForm') formElement: ElementRef; + title: string; // Title to display. assign: AddonModAssignAssign; // Assignment. courseId: number; // Course ID the assignment belongs to. @@ -265,69 +268,77 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy { * * @return Promise resolved when done. */ - protected saveSubmission(): Promise { + protected async saveSubmission(): Promise { const inputData = this.getInputData(); if (this.submissionStatement && (!inputData.submissionstatement || inputData.submissionstatement === 'false')) { - return Promise.reject(this.translate.instant('addon.mod_assign.acceptsubmissionstatement')); + throw this.translate.instant('addon.mod_assign.acceptsubmissionstatement'); } let modal = this.domUtils.showModalLoading(); + let size; // Get size to ask for confirmation. - return this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData).catch(() => { + try { + size = await this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData); + } catch (error) { // Error calculating size, return -1. - return -1; - }).then((size) => { - modal.dismiss(); + size = -1; + } + modal.dismiss(); + + try { // Confirm action. - return this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline); - }).then(() => { + await this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline); + modal = this.domUtils.showModalLoading('core.sending', true); - return this.prepareSubmissionData(inputData).then((pluginData) => { - if (!Object.keys(pluginData).length) { - // Nothing to save. - return; - } + const pluginData = await this.prepareSubmissionData(inputData); + if (!Object.keys(pluginData).length) { + // Nothing to save. + return; + } - let promise; + let sent: boolean; - if (this.saveOffline) { - // Save submission in offline. - promise = this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData, - this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId); - } else { - // Try to send it to server. - promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline, - this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId); - } + if (this.saveOffline) { + // Save submission in offline. + sent = false; + await this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData, + this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId); + } else { + // Try to send it to server. + sent = await this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline, + this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId); + } - return promise.then(() => { - // Clear temporary data from plugins. - return this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); - }).then(() => { - // Submission saved, trigger event. - const params = { - assignmentId: this.assign.id, - submissionId: this.userSubmission.id, - userId: this.userId, - }; + // Clear temporary data from plugins. + await this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); - this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params, - this.sitesProvider.getCurrentSiteId()); + // Submission saved, trigger events. + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: sent, + }, this.sitesProvider.getCurrentSiteId()); - if (!this.assign.submissiondrafts) { - // No drafts allowed, so it was submitted. Trigger event. - this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params, - this.sitesProvider.getCurrentSiteId()); - } - }); - }); - }).finally(() => { + const params = { + assignmentId: this.assign.id, + submissionId: this.userSubmission.id, + userId: this.userId, + }; + + this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params, + this.sitesProvider.getCurrentSiteId()); + + if (!this.assign.submissiondrafts) { + // No drafts allowed, so it was submitted. Trigger event. + this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params, + this.sitesProvider.getCurrentSiteId()); + } + } finally { modal.dismiss(); - }); + } } /** diff --git a/src/addon/mod/data/pages/edit/edit.html b/src/addon/mod/data/pages/edit/edit.html index d5865e7e3..7b5418da5 100644 --- a/src/addon/mod/data/pages/edit/edit.html +++ b/src/addon/mod/data/pages/edit/edit.html @@ -21,7 +21,7 @@
- +
diff --git a/src/addon/mod/data/pages/edit/edit.ts b/src/addon/mod/data/pages/edit/edit.ts index 38a1bc2e8..6bc8c66ff 100644 --- a/src/addon/mod/data/pages/edit/edit.ts +++ b/src/addon/mod/data/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { FormGroup } from '@angular/forms'; @@ -40,6 +40,7 @@ import { CoreTagProvider } from '@core/tag/providers/tag'; }) export class AddonModDataEditPage { @ViewChild(Content) content: Content; + @ViewChild('editFormEl') formElement: ElementRef; protected module: any; protected courseId: number; @@ -216,6 +217,12 @@ export class AddonModDataEditPage { // This is done if entry is updated when editing or creating if not. if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: result.sent, + }, this.siteId); + const promises = []; this.entryId = this.entryId || result.newentryid; diff --git a/src/addon/mod/data/pages/search/search.html b/src/addon/mod/data/pages/search/search.html index 000181e40..6dc36d529 100644 --- a/src/addon/mod/data/pages/search/search.html +++ b/src/addon/mod/data/pages/search/search.html @@ -13,7 +13,7 @@ {{ 'addon.mod_data.search' | translate}} {{ 'addon.mod_data.advancedsearch' | translate }}
-
+ diff --git a/src/addon/mod/data/pages/search/search.ts b/src/addon/mod/data/pages/search/search.ts index 5aff12b6b..5834e6ccc 100644 --- a/src/addon/mod/data/pages/search/search.ts +++ b/src/addon/mod/data/pages/search/search.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, ViewController } from 'ionic-angular'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -32,6 +34,8 @@ import { CoreTagProvider } from '@core/tag/providers/tag'; templateUrl: 'search.html', }) export class AddonModDataSearchPage { + @ViewChild('searchFormEl') formElement: ElementRef; + search: any; fields: any; data: any; @@ -41,10 +45,17 @@ export class AddonModDataSearchPage { jsData: any; fieldsArray: any; - constructor(params: NavParams, private viewCtrl: ViewController, fb: FormBuilder, protected utils: CoreUtilsProvider, - protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate, - protected textUtils: CoreTextUtilsProvider, protected dataHelper: AddonModDataHelperProvider, - private tagProvider: CoreTagProvider) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + fb: FormBuilder, + protected utils: CoreUtilsProvider, + protected domUtils: CoreDomUtilsProvider, + protected fieldsDelegate: AddonModDataFieldsDelegate, + protected textUtils: CoreTextUtilsProvider, + protected dataHelper: AddonModDataHelperProvider, + protected tagProvider: CoreTagProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.search = params.get('search'); this.fields = params.get('fields'); this.data = params.get('data'); @@ -209,6 +220,11 @@ export class AddonModDataSearchPage { this.search.sortBy = searchedData.sortBy; this.search.sortDirection = searchedData.sortDirection; + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.closeModal(this.search); } } diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts index 0e70b084c..481896b28 100644 --- a/src/addon/mod/data/providers/data.ts +++ b/src/addon/mod/data/providers/data.ts @@ -126,7 +126,8 @@ export class AddonModDataProvider { .then((entry) => { return { // Return provissional entry Id. - newentryid: entry + newentryid: entry, + sent: false, }; }); }; @@ -142,7 +143,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.addEntryOnline(dataId, contents, groupId, siteId).catch((error) => { + return this.addEntryOnline(dataId, contents, groupId, siteId).then((result) => { + result.sent = true; + + return result; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -194,7 +199,12 @@ export class AddonModDataProvider { const storeOffline = (): Promise => { const action = approve ? 'approve' : 'disapprove'; - return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId); + return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId) + .then(() => { + return { + sent: false, + }; + }); }; // Get if the opposite action is not synced. @@ -210,7 +220,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.approveEntryOnline(entryId, approve, siteId).catch((error) => { + return this.approveEntryOnline(entryId, approve, siteId).then(() => { + return { + sent: true, + }; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -288,7 +302,12 @@ export class AddonModDataProvider { // Convenience function to store a data to be synchronized later. const storeOffline = (): Promise => { - return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId); + return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId) + .then(() => { + return { + sent: false, + }; + }); }; let justAdded = false; @@ -318,7 +337,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.deleteEntryOnline(entryId, siteId).catch((error) => { + return this.deleteEntryOnline(entryId, siteId).then(() => { + return { + sent: true, + }; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -368,7 +391,8 @@ export class AddonModDataProvider { return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId) .then(() => { return { - updated: true + updated: true, + sent: false, }; }); }; @@ -408,6 +432,7 @@ export class AddonModDataProvider { return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline) .then((result) => { result.updated = true; + result.sent = true; return result; }); @@ -418,7 +443,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.editEntryOnline(entryId, contents, siteId).catch((error) => { + return this.editEntryOnline(entryId, contents, siteId).then((result) => { + result.sent = true; + + return result; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); diff --git a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html index 440d7f383..f7ac672e0 100644 --- a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html @@ -34,7 +34,7 @@ - + {{ 'addon.mod_lesson.enterpassword' | translate }} diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts index 17dec12ee..b3b4b95a7 100644 --- a/src/addon/mod/lesson/components/index/index.ts +++ b/src/addon/mod/lesson/components/index/index.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Optional, Injector, Input, ViewChild } from '@angular/core'; +import { Component, Optional, Injector, Input, ViewChild, ElementRef } from '@angular/core'; import { Content, NavController } from 'ionic-angular'; +import { CoreEventsProvider } from '@providers/events'; import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -35,6 +36,7 @@ import { CoreTabsComponent } from '@components/tabs/tabs'; }) export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityComponent { @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; + @ViewChild('passwordForm') formElement: ElementRef; @Input() group: number; // The group to display. @Input() action: string; // The "action" to display first. @@ -584,6 +586,11 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo this.loaded = true; this.refreshIcon = 'refresh'; this.syncIcon = 'sync'; + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }, this.siteId); }); } diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.html b/src/addon/mod/lesson/pages/password-modal/password-modal.html index b673d540c..3c35a3dcc 100644 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.html +++ b/src/addon/mod/lesson/pages/password-modal/password-modal.html @@ -9,7 +9,7 @@ - + {{ 'addon.mod_lesson.enterpassword' | translate }} diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.ts b/src/addon/mod/lesson/pages/password-modal/password-modal.ts index 13fbfcdb1..d72df1ca4 100644 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.ts +++ b/src/addon/mod/lesson/pages/password-modal/password-modal.ts @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController } from 'ionic-angular'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; /** * Modal that asks the password for a lesson. @@ -24,8 +26,11 @@ import { IonicPage, ViewController } from 'ionic-angular'; templateUrl: 'password-modal.html', }) export class AddonModLessonPasswordModalPage { + @ViewChild('passwordForm') formElement: ElementRef; - constructor(protected viewCtrl: ViewController) { } + constructor(protected viewCtrl: ViewController, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { } /** * Send the password back. @@ -37,6 +42,11 @@ export class AddonModLessonPasswordModalPage { e.preventDefault(); e.stopPropagation(); + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.viewCtrl.dismiss(password.value); } diff --git a/src/addon/mod/lesson/pages/player/player.html b/src/addon/mod/lesson/pages/player/player.html index b8526c401..dfe4c3e86 100644 --- a/src/addon/mod/lesson/pages/player/player.html +++ b/src/addon/mod/lesson/pages/player/player.html @@ -38,7 +38,7 @@ - + diff --git a/src/addon/mod/lesson/pages/player/player.ts b/src/addon/mod/lesson/pages/player/player.ts index c29ad99b5..7ab23e96f 100644 --- a/src/addon/mod/lesson/pages/player/player.ts +++ b/src/addon/mod/lesson/pages/player/player.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ElementRef } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; @@ -41,6 +41,7 @@ import { AddonModLessonHelperProvider } from '../../providers/helper'; }) export class AddonModLessonPlayerPage implements OnInit, OnDestroy { @ViewChild(Content) content: Content; + @ViewChild('questionFormEl') formElement: ElementRef; component = AddonModLessonProvider.COMPONENT; LESSON_EOL = AddonModLessonProvider.LESSON_EOL; @@ -540,15 +541,23 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { * Process a page, sending some data. * * @param data The data to send. + * @param formSubmitted Whether a form was submitted. * @return Promise resolved when done. */ - protected processPage(data: any): Promise { + protected processPage(data: any, formSubmitted?: boolean): Promise { this.loaded = false; const args = [this.lesson, this.courseId, this.pageData, data, this.password, this.review, this.offline, this.accessInfo, this.jumps]; return this.callFunction(this.lessonProvider.processPage.bind(this.lessonProvider), args, 6, 8).then((result) => { + if (formSubmitted) { + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: result.sent, + }, this.sitesProvider.getCurrentSiteId()); + } + if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson)) { // Lesson allows offline and the user changed some data in server. Update cached data. const retake = this.accessInfo.attemptscount; @@ -637,7 +646,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { // Use getRawValue to include disabled values. const data = this.lessonHelper.prepareQuestionData(this.question, this.questionForm.getRawValue()); - this.processPage(data).finally(() => { + this.processPage(data, true).finally(() => { this.loaded = true; }); } diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts index b7fabe1f1..a4db30c6f 100644 --- a/src/addon/mod/lesson/providers/lesson.ts +++ b/src/addon/mod/lesson/providers/lesson.ts @@ -3089,6 +3089,7 @@ export class AddonModLessonProvider { result.warnings = []; result.displaymenu = pageData.displaymenu; // Keep the same value since we can't calculate it in offline. result.messages = this.getPageProcessMessages(lesson, accessInfo, result, review, jumps); + result.sent = false; Object.assign(result, calculatedData); return result; @@ -3104,6 +3105,8 @@ export class AddonModLessonProvider { review: review }, this.sitesProvider.getCurrentSiteId()); + response.sent = true; + return response; }); } diff --git a/src/addon/mod/quiz/pages/player/player.html b/src/addon/mod/quiz/pages/player/player.html index e632829c1..81d271d7d 100644 --- a/src/addon/mod/quiz/pages/player/player.html +++ b/src/addon/mod/quiz/pages/player/player.html @@ -45,7 +45,7 @@ - +
diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts index 81f7989d5..31acddaa4 100644 --- a/src/addon/mod/quiz/pages/player/player.ts +++ b/src/addon/mod/quiz/pages/player/player.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ViewChildren, QueryList } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef } from '@angular/core'; import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; @@ -41,6 +41,7 @@ import { Subscription } from 'rxjs'; export class AddonModQuizPlayerPage implements OnInit, OnDestroy { @ViewChild(Content) content: Content; @ViewChildren(CoreQuestionComponent) questionComponents: QueryList; + @ViewChild('quizForm') formElement: ElementRef; quiz: any; // The quiz the attempt belongs to. attempt: any; // The attempt being attempted. @@ -585,6 +586,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { // Answers saved, cancel auto save. this.autoSave.cancelAutoSave(); this.autoSave.hideAutoSaveError(); + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !this.offline, + }, this.sitesProvider.getCurrentSiteId()); }); } diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html index c00c08277..69d32d7ab 100644 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html +++ b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html @@ -10,7 +10,7 @@ - + diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts index 590d03137..28681c8a5 100644 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts +++ b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, Injector, ViewChild } from '@angular/core'; +import { Component, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams, Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; @@ -31,6 +32,7 @@ import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-del export class AddonModQuizPreflightModalPage implements OnInit { @ViewChild(Content) content: Content; + @ViewChild('preflightFormEl') formElement: ElementRef; preflightForm: FormGroup; title: string; @@ -43,9 +45,15 @@ export class AddonModQuizPreflightModalPage implements OnInit { protected siteId: string; protected rules: string[]; - constructor(params: NavParams, fb: FormBuilder, translate: TranslateService, sitesProvider: CoreSitesProvider, - protected viewCtrl: ViewController, protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, - protected injector: Injector, protected domUtils: CoreDomUtilsProvider) { + constructor(params: NavParams, + fb: FormBuilder, + translate: TranslateService, + sitesProvider: CoreSitesProvider, + protected viewCtrl: ViewController, + protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, + protected injector: Injector, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { this.title = params.get('title') || translate.instant('addon.mod_quiz.startattempt'); this.quiz = params.get('quiz'); @@ -112,6 +120,11 @@ export class AddonModQuizPreflightModalPage implements OnInit { this.domUtils.showErrorModal('core.errorinvalidform', true); } } else { + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.siteId); + this.viewCtrl.dismiss(this.preflightForm.value); } } diff --git a/src/addon/mod/wiki/pages/edit/edit.html b/src/addon/mod/wiki/pages/edit/edit.html index 2381df6c3..aa2491c90 100644 --- a/src/addon/mod/wiki/pages/edit/edit.html +++ b/src/addon/mod/wiki/pages/edit/edit.html @@ -11,7 +11,7 @@ - + diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts index 033ec982e..7df9ad26a 100644 --- a/src/addon/mod/wiki/pages/edit/edit.ts +++ b/src/addon/mod/wiki/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -37,6 +37,8 @@ import { AddonModWikiSyncProvider, AddonModWikiSyncSubwikiResult } from '../../p }) export class AddonModWikiEditPage implements OnInit, OnDestroy { + @ViewChild('editPageForm') formElement: ElementRef; + title: string; // Title to display. pageForm: FormGroup; // The form group. contentControl: FormControl; // The FormControl for the page content. @@ -423,6 +425,12 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { if (this.editing) { // Edit existing page. promise = this.wikiProvider.editPage(this.pageId, text, this.section).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }, this.sitesProvider.getCurrentSiteId()); + // Invalidate page since it changed. return this.wikiProvider.invalidatePage(this.pageId).then(() => { return this.gotoPage(title); @@ -456,6 +464,12 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { let wikiId = this.wikiId || (this.module && this.module.instance); return this.wikiProvider.newPage(title, text, this.subwikiId, wikiId, this.userId, this.groupId).then((id) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: id > 0, + }, this.sitesProvider.getCurrentSiteId()); + if (id > 0) { // Page was created, get its data and go to the page. this.pageId = id; diff --git a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html index b79857e1d..c3bb1fd41 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html +++ b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html @@ -1,6 +1,6 @@

{{ 'addon.mod_workshop.assessmentform' | translate }}

- + diff --git a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts index e92c1e998..9b239ddac 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit, Injector } from '@angular/core'; +import { Component, Input, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; import { FormControl } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { CoreSyncProvider } from '@providers/sync'; @@ -44,6 +44,8 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { @Input() strategy: string; @Input() edit?: boolean; + @ViewChild('assessmentForm') formElement: ElementRef; + componentClass: any; data = { workshopId: 0, @@ -292,7 +294,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { // Save assessment in offline. return this.workshopOffline.saveAssessment(this.workshop.id, this.assessmentId, this.workshop.course, assessmentData).then(() => { - // Don't return anything. + return false; }); } @@ -301,6 +303,12 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { return this.workshopProvider.updateAssessment(this.workshop.id, this.assessmentId, this.workshop.course, assessmentData, false, allowOffline); }).then((grade) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!grade, + }, this.sitesProvider.getCurrentSiteId()); + const promises = []; // If sent to the server, invalidate and clean. diff --git a/src/addon/mod/workshop/pages/assessment/assessment.html b/src/addon/mod/workshop/pages/assessment/assessment.html index 6d093a0c4..cfb72fa00 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.html +++ b/src/addon/mod/workshop/pages/assessment/assessment.html @@ -38,7 +38,7 @@ - +

{{ 'addon.mod_workshop.assessmentsettings' | translate }}

diff --git a/src/addon/mod/workshop/pages/assessment/assessment.ts b/src/addon/mod/workshop/pages/assessment/assessment.ts index 1fe9fe5aa..ae9a53d62 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.ts +++ b/src/addon/mod/workshop/pages/assessment/assessment.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -39,6 +39,8 @@ import { AddonModWorkshopSyncProvider } from '../../providers/sync'; }) export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { + @ViewChild('evaluateFormEl') formElement: ElementRef; + assessment: any; submission: any; profile: any; @@ -340,7 +342,13 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { // Try to send it to server. return this.workshopProvider.evaluateAssessment(this.workshopId, this.assessmentId, this.courseId, inputData.text, - inputData.weight, inputData.grade).then(() => { + inputData.weight, inputData.grade).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!result, + }, this.siteId); + const data = { workshopId: this.workshopId, assessmentId: this.assessmentId, diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html index a7f3bad68..99e4d0bf3 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html @@ -10,7 +10,7 @@ - + {{ 'addon.mod_workshop.submissiontitle' | translate }} diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts index c3fb1edbb..121d11ed8 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -37,6 +37,8 @@ import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; }) export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { + @ViewChild('editFormEl') formElement: ElementRef; + module: any; courseId: number; access: any; @@ -352,7 +354,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { // Save submission in offline. return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, attachmentsId, submissionId, 'update').then(() => { - // Don't return anything. + return false; }); } @@ -365,8 +367,8 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { if (saveOffline) { // Save submission in offline. return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, - attachmentsId, submissionId, 'add').then(() => { - // Don't return anything. + attachmentsId, submissionId, 'add').then(() => { + return false; }); } @@ -375,6 +377,12 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { return this.workshopProvider.addSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, attachmentsId, undefined, submissionId, allowOffline); }).then((newSubmissionId) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!newSubmissionId, + }, this.siteId); + const data = { workshopId: this.workshopId, cmId: this.module.cmid diff --git a/src/addon/mod/workshop/pages/submission/submission.html b/src/addon/mod/workshop/pages/submission/submission.html index 0951709ae..5bff46089 100644 --- a/src/addon/mod/workshop/pages/submission/submission.html +++ b/src/addon/mod/workshop/pages/submission/submission.html @@ -65,7 +65,7 @@ - +

{{ 'addon.mod_workshop.feedbackauthor' | translate }}

diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts index 3fb884e5f..a1c3b6d50 100644 --- a/src/addon/mod/workshop/pages/submission/submission.ts +++ b/src/addon/mod/workshop/pages/submission/submission.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, Optional, ViewChild } from '@angular/core'; +import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -41,6 +41,7 @@ import { AddonModWorkshopSyncProvider } from '../../providers/sync'; export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { @ViewChild(AddonModWorkshopAssessmentStrategyComponent) assessmentStrategy: AddonModWorkshopAssessmentStrategyComponent; + @ViewChild('feedbackFormEl') formElement: ElementRef; module: any; workshop: any; @@ -444,7 +445,13 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { // Try to send it to server. return this.workshopProvider.evaluateSubmission(this.workshopId, this.submissionId, this.courseId, inputData.text, - inputData.published, inputData.grade).then(() => { + inputData.published, inputData.grade).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!result, + }, this.siteId); + const data = { workshopId: this.workshopId, cmId: this.module.cmid, diff --git a/src/addon/notes/pages/add/add.html b/src/addon/notes/pages/add/add.html index b6df1d660..93f2a04a4 100644 --- a/src/addon/notes/pages/add/add.html +++ b/src/addon/notes/pages/add/add.html @@ -9,7 +9,7 @@ - + {{ 'addon.notes.publishstate' | translate }} diff --git a/src/addon/notes/pages/add/add.ts b/src/addon/notes/pages/add/add.ts index 15fd9da0a..d00e95aa2 100644 --- a/src/addon/notes/pages/add/add.ts +++ b/src/addon/notes/pages/add/add.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonNotesProvider } from '../../providers/notes'; @@ -27,14 +29,22 @@ import { AddonNotesProvider } from '../../providers/notes'; templateUrl: 'add.html', }) export class AddonNotesAddPage { + + @ViewChild('itemEdit') formElement: ElementRef; + userId: number; courseId: number; type = 'personal'; text = ''; processing = false; - constructor(params: NavParams, private viewCtrl: ViewController, private appProvider: CoreAppProvider, - private domUtils: CoreDomUtilsProvider, private notesProvider: AddonNotesProvider) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + protected appProvider: CoreAppProvider, + protected domUtils: CoreDomUtilsProvider, + protected notesProvider: AddonNotesProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.userId = params.get('userId'); this.courseId = params.get('courseId'); this.type = params.get('type') || 'personal'; @@ -54,6 +64,12 @@ export class AddonNotesAddPage { // Freeze the add note button. this.processing = true; this.notesProvider.addNote(this.userId, this.courseId, this.type, this.text).then((sent) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: sent, + }, this.sitesProvider.getCurrentSiteId()); + this.viewCtrl.dismiss({type: this.type, sent: true}).finally(() => { this.domUtils.showToast(sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline', true, 3000); }); diff --git a/src/components/local-file/core-local-file.html b/src/components/local-file/core-local-file.html index a988397a9..ffec36361 100644 --- a/src/components/local-file/core-local-file.html +++ b/src/components/local-file/core-local-file.html @@ -1,4 +1,4 @@ - + {{fileExtension}} diff --git a/src/components/local-file/local-file.ts b/src/components/local-file/local-file.ts index e1c46b1c2..31fd41bf0 100644 --- a/src/components/local-file/local-file.ts +++ b/src/components/local-file/local-file.ts @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'; +import { Component, Input, Output, OnInit, EventEmitter, ViewChild, ElementRef } from '@angular/core'; +import { CoreEventsProvider } from '@providers/events'; import { CoreFileProvider } from '@providers/file'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -38,6 +40,8 @@ export class CoreLocalFileComponent implements OnInit { @Output() onRename?: EventEmitter; // Will notify when the file is renamed. Receives the FileEntry as the param. @Output() onClick?: EventEmitter; // Will notify when the file is clicked. Only if overrideClick is true. + @ViewChild('nameForm') formElement: ElementRef; + fileName: string; fileIcon: string; fileExtension: string; @@ -47,12 +51,14 @@ export class CoreLocalFileComponent implements OnInit { editMode: boolean; relativePath: string; - constructor(private mimeUtils: CoreMimetypeUtilsProvider, - private utils: CoreUtilsProvider, - private textUtils: CoreTextUtilsProvider, - private fileProvider: CoreFileProvider, - private domUtils: CoreDomUtilsProvider, - private timeUtils: CoreTimeUtilsProvider) { + constructor(protected mimeUtils: CoreMimetypeUtilsProvider, + protected utils: CoreUtilsProvider, + protected textUtils: CoreTextUtilsProvider, + protected fileProvider: CoreFileProvider, + protected domUtils: CoreDomUtilsProvider, + protected timeUtils: CoreTimeUtilsProvider, + protected sitesProvider: CoreSitesProvider, + protected eventsProvider: CoreEventsProvider) { this.onDelete = new EventEmitter(); this.onRename = new EventEmitter(); this.onClick = new EventEmitter(); @@ -152,6 +158,12 @@ export class CoreLocalFileComponent implements OnInit { }).catch(() => { // File doesn't exist, move it. return this.fileProvider.moveFile(this.relativePath, newPath).then((fileEntry) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.editMode = false; this.file = fileEntry; this.loadFileBasicData(); diff --git a/src/components/search-box/search-box.ts b/src/components/search-box/search-box.ts new file mode 100644 index 000000000..4698e4584 --- /dev/null +++ b/src/components/search-box/search-box.ts @@ -0,0 +1,100 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUtilsProvider } from '@providers/utils/utils'; + +/** + * Component to display a "search box". + * + * @description + * This component will display a standalone search box with its search button in order to have a better UX. + * + * Example usage: + * + */ +@Component({ + selector: 'core-search-box', + templateUrl: 'core-search-box.html' +}) +export class CoreSearchBoxComponent implements OnInit { + @Input() searchLabel?: string; // Label to be used on action button. + @Input() placeholder?: string; // Placeholder text for search text input. + @Input() autocorrect = 'on'; // Enables/disable Autocorrection on search text input. + @Input() spellcheck?: string | boolean = true; // Enables/disable Spellchecker on search text input. + @Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view. + @Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted. + @Input() showClear = true; // Show/hide clear button. + @Input() disabled = false; // Disables the input text. + @Input() initialSearch: string; // Initial search text. + @Output() onSubmit: EventEmitter; // Send data when submitting the search form. + @Output() onClear: EventEmitter; // Send event when clearing the search form. + + @ViewChild('searchForm') formElement: ElementRef; + + searched = false; + searchText = ''; + + constructor(protected translate: TranslateService, + protected utils: CoreUtilsProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { + this.onSubmit = new EventEmitter(); + this.onClear = new EventEmitter(); + } + + ngOnInit(): void { + this.searchLabel = this.searchLabel || this.translate.instant('core.search'); + this.placeholder = this.placeholder || this.translate.instant('core.search'); + this.spellcheck = this.utils.isTrueOrOne(this.spellcheck); + this.showClear = this.utils.isTrueOrOne(this.showClear); + this.searchText = this.initialSearch || ''; + } + + /** + * Form submitted. + * + * @param e Event. + */ + submitForm(e: Event): void { + e.preventDefault(); + e.stopPropagation(); + + if (this.searchText.length < this.lengthCheck) { + // The view should handle this case, but we check it here too just in case. + return; + } + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + + this.searched = true; + this.onSubmit.emit(this.searchText); + } + + /** + * Form submitted. + */ + clearForm(): void { + this.searched = false; + this.searchText = ''; + this.onClear.emit(); + } +} diff --git a/src/components/send-message-form/core-send-message-form.html b/src/components/send-message-form/core-send-message-form.html index 8caa886c3..bed8756ce 100644 --- a/src/components/send-message-form/core-send-message-form.html +++ b/src/components/send-message-form/core-send-message-form.html @@ -1,4 +1,4 @@ - +
- + diff --git a/src/core/login/pages/credentials/credentials.ts b/src/core/login/pages/credentials/credentials.ts index 13b81d12c..3d3f6f5e4 100644 --- a/src/core/login/pages/credentials/credentials.ts +++ b/src/core/login/pages/credentials/credentials.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; @@ -32,6 +32,9 @@ import { CoreConfigConstants } from '../../../../configconstants'; templateUrl: 'credentials.html', }) export class CoreLoginCredentialsPage { + + @ViewChild('credentialsForm') formElement: ElementRef; + credForm: FormGroup; siteUrl: string; siteChecked = false; @@ -242,6 +245,11 @@ export class CoreLoginCredentialsPage { } }).finally(() => { modal.dismiss(); + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); }); } diff --git a/src/core/login/pages/email-signup/email-signup.html b/src/core/login/pages/email-signup/email-signup.html index 2e49ea822..3e52b4b6c 100644 --- a/src/core/login/pages/email-signup/email-signup.html +++ b/src/core/login/pages/email-signup/email-signup.html @@ -17,7 +17,7 @@ - +

{{ 'core.agelocationverification' | translate }}

@@ -47,7 +47,7 @@ -
+

{{siteUrl}}

diff --git a/src/core/login/pages/email-signup/email-signup.ts b/src/core/login/pages/email-signup/email-signup.ts index ffb8a6f42..e9563da59 100644 --- a/src/core/login/pages/email-signup/email-signup.ts +++ b/src/core/login/pages/email-signup/email-signup.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams, Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -35,6 +36,8 @@ import { CoreConfigConstants } from '../../../../configconstants'; }) export class CoreLoginEmailSignupPage { @ViewChild(Content) content: Content; + @ViewChild('ageForm') ageFormElement: ElementRef; + @ViewChild('signupFormEl') signupFormElement: ElementRef; signupForm: FormGroup; siteUrl: string; @@ -66,10 +69,18 @@ export class CoreLoginEmailSignupPage { policyErrors: any; namefieldsErrors: any; - constructor(private navCtrl: NavController, navParams: NavParams, private fb: FormBuilder, private wsProvider: CoreWSProvider, - private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, - private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider, - private textUtils: CoreTextUtilsProvider, private userProfileFieldDelegate: CoreUserProfileFieldDelegate) { + constructor(protected navCtrl: NavController, + navParams: NavParams, + protected fb: FormBuilder, + protected wsProvider: CoreWSProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected translate: TranslateService, + protected utils: CoreUtilsProvider, + protected textUtils: CoreTextUtilsProvider, + protected userProfileFieldDelegate: CoreUserProfileFieldDelegate, + protected eventsProvider: CoreEventsProvider) { this.siteUrl = navParams.get('siteUrl'); @@ -265,6 +276,12 @@ export class CoreLoginEmailSignupPage { return this.wsProvider.callAjax('auth_email_signup_user', params, { siteUrl: this.siteUrl }); }).then((result) => { if (result.success) { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.signupFormElement.nativeElement, + online: true, + }); + // Show alert and ho back. const message = this.translate.instant('core.login.emailconfirmsent', { $a: params.email }); this.domUtils.showAlert(this.translate.instant('core.success'), message); @@ -334,6 +351,12 @@ export class CoreLoginEmailSignupPage { params.age = parseInt(params.age, 10); // Use just the integer part. this.wsProvider.callAjax('core_auth_is_minor', params, {siteUrl: this.siteUrl}).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.ageFormElement.nativeElement, + online: true, + }); + if (!result.status) { if (this.countryControl.value) { this.signUpCountryControl.setValue(this.countryControl.value); diff --git a/src/core/login/pages/forgotten-password/forgotten-password.html b/src/core/login/pages/forgotten-password/forgotten-password.html index 182ef2312..81905e4d4 100644 --- a/src/core/login/pages/forgotten-password/forgotten-password.html +++ b/src/core/login/pages/forgotten-password/forgotten-password.html @@ -10,7 +10,7 @@
- + {{ 'core.login.searchby' | translate }} diff --git a/src/core/login/pages/forgotten-password/forgotten-password.ts b/src/core/login/pages/forgotten-password/forgotten-password.ts index b1d5d6785..1187d903a 100644 --- a/src/core/login/pages/forgotten-password/forgotten-password.ts +++ b/src/core/login/pages/forgotten-password/forgotten-password.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreLoginHelperProvider } from '../../providers/helper'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @@ -28,11 +30,20 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; templateUrl: 'forgotten-password.html', }) export class CoreLoginForgottenPasswordPage { + + @ViewChild('resetPasswordForm') formElement: ElementRef; + myForm: FormGroup; siteUrl: string; - constructor(private navCtrl: NavController, navParams: NavParams, fb: FormBuilder, private translate: TranslateService, - private loginHelper: CoreLoginHelperProvider, private domUtils: CoreDomUtilsProvider) { + constructor(protected navCtrl: NavController, + navParams: NavParams, + fb: FormBuilder, + protected translate: TranslateService, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.siteUrl = navParams.get('siteUrl'); this.myForm = fb.group({ @@ -71,6 +82,11 @@ export class CoreLoginForgottenPasswordPage { this.domUtils.showErrorModal(response.notice); } else { // Success. + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + this.domUtils.showAlert(this.translate.instant('core.success'), response.notice); this.navCtrl.pop(); } diff --git a/src/core/login/pages/reconnect/reconnect.html b/src/core/login/pages/reconnect/reconnect.html index bfb460784..1b87186ee 100644 --- a/src/core/login/pages/reconnect/reconnect.html +++ b/src/core/login/pages/reconnect/reconnect.html @@ -29,7 +29,7 @@ {{ 'core.login.reconnectdescription' | translate }}

- +

{{username}}

diff --git a/src/core/login/pages/reconnect/reconnect.ts b/src/core/login/pages/reconnect/reconnect.ts index 9372ed2b7..73b551574 100644 --- a/src/core/login/pages/reconnect/reconnect.ts +++ b/src/core/login/pages/reconnect/reconnect.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreLoginHelperProvider } from '../../providers/helper'; @@ -29,6 +30,9 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; templateUrl: 'reconnect.html', }) export class CoreLoginReconnectPage { + + @ViewChild('reconnectForm') formElement: ElementRef; + credForm: FormGroup; siteUrl: string; username: string; @@ -47,13 +51,14 @@ export class CoreLoginReconnectPage { protected isLoggedOut: boolean; protected siteId: string; - constructor(private navCtrl: NavController, + constructor(protected navCtrl: NavController, navParams: NavParams, fb: FormBuilder, - private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, - private loginHelper: CoreLoginHelperProvider, - private domUtils: CoreDomUtilsProvider) { + protected appProvider: CoreAppProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { const currentSite = this.sitesProvider.getCurrentSite(); @@ -175,6 +180,12 @@ export class CoreLoginReconnectPage { // Start the authentication process. this.sitesProvider.getUserToken(siteUrl, username, password).then((data) => { return this.sitesProvider.updateSiteToken(this.infoSiteUrl, username, data.token, data.privateToken).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + // Update site info too because functions might have changed (e.g. unisntall local_mobile). return this.sitesProvider.updateSiteInfoByUrl(this.infoSiteUrl, username).then(() => { // Reset fields so the data is not in the view anymore. diff --git a/src/core/login/pages/site/site.html b/src/core/login/pages/site/site.html index 6a4b40f36..8da75c237 100644 --- a/src/core/login/pages/site/site.html +++ b/src/core/login/pages/site/site.html @@ -17,7 +17,7 @@
- +

{{ 'core.login.newsitedescription' | translate }}

diff --git a/src/core/login/pages/site/site.ts b/src/core/login/pages/site/site.ts index 5a231d27c..d20b510f7 100644 --- a/src/core/login/pages/site/site.ts +++ b/src/core/login/pages/site/site.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, ModalController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider, CoreSiteCheckResponse } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreConfigConstants } from '../../../../configconstants'; @@ -31,6 +32,9 @@ import { CoreUrl } from '@classes/utils/url'; templateUrl: 'site.html', }) export class CoreLoginSitePage { + + @ViewChild('siteFormEl') formElement: ElementRef; + siteForm: FormGroup; fixedSites: any[]; filteredSites: any[]; @@ -38,9 +42,15 @@ export class CoreLoginSitePage { showKeyboard = false; filter = ''; - constructor(navParams: NavParams, private navCtrl: NavController, fb: FormBuilder, private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, - private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider) { + constructor(navParams: NavParams, + protected navCtrl: NavController, + fb: FormBuilder, + protected appProvider: CoreAppProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected modalCtrl: ModalController, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { this.showKeyboard = !!navParams.get('showKeyboard'); @@ -96,6 +106,12 @@ export class CoreLoginSitePage { // It's a demo site. this.sitesProvider.getUserToken(siteData.url, siteData.username, siteData.password).then((data) => { return this.sitesProvider.newSite(data.siteUrl, data.token, data.privateToken).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + return this.loginHelper.goToSiteInitialPage(); }, (error) => { this.loginHelper.treatUserTokenError(siteData.url, error, siteData.username, siteData.password); @@ -175,6 +191,12 @@ export class CoreLoginSitePage { */ protected async login(response: CoreSiteCheckResponse): Promise { return this.sitesProvider.checkRequiredMinimumVersion(response.config).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + if (response.warning) { this.domUtils.showErrorModal(response.warning, true, 4000); } diff --git a/src/core/search/components/search-box/core-search-box.html b/src/core/search/components/search-box/core-search-box.html index adc00c50b..be186a40b 100644 --- a/src/core/search/components/search-box/core-search-box.html +++ b/src/core/search/components/search-box/core-search-box.html @@ -1,5 +1,5 @@ - +