diff --git a/local_moodleappbehat/tests/behat/behat_app.php b/local_moodleappbehat/tests/behat/behat_app.php
index 0f3b92d5e..59ece39db 100644
--- a/local_moodleappbehat/tests/behat/behat_app.php
+++ b/local_moodleappbehat/tests/behat/behat_app.php
@@ -91,8 +91,6 @@ class behat_app extends behat_app_helper {
* @throws ExpectationException Problem with resizing window
*/
public function i_launch_the_app(string $runtime = '') {
- $this->check_tags();
-
// Go to page and prepare browser for app.
$this->prepare_browser(['skiponboarding' => empty($runtime)]);
}
@@ -101,18 +99,27 @@ class behat_app extends behat_app_helper {
* @Then I wait the app to restart
*/
public function i_wait_the_app_to_restart() {
- // Wait window to reload.
- $this->spin(function() {
- if ($this->runtime_js('hasInitialized()')) {
- // Behat runtime shouldn't be initialized after reload.
- throw new DriverException('Window is not reloading properly.');
- }
-
- return true;
- });
-
// Prepare testing runtime again.
- $this->prepare_browser(['restart' => false]);
+ $this->prepare_browser();
+ }
+
+ /**
+ * @Then I log out in the app
+ *
+ * @param bool $force If force logout or not.
+ */
+ public function i_log_out_in_app($force = true) {
+ $options = json_encode([
+ 'forceLogout' => $force,
+ ]);
+
+ $result = $this->zone_js("sites.logout($options)");
+
+ if ($result !== 'OK') {
+ throw new DriverException('Error on log out - ' . $result);
+ }
+
+ $this->i_wait_the_app_to_restart();
}
/**
@@ -567,7 +574,7 @@ class behat_app extends behat_app_helper {
/**
* Performs a pull to refresh gesture.
*
- * @When /^I pull to refresh in the app$/
+ * @When I pull to refresh in the app
* @throws DriverException If the gesture is not available
*/
public function i_pull_to_refresh_in_the_app() {
@@ -841,9 +848,31 @@ class behat_app extends behat_app_helper {
* @Given /^I switch offline mode to "(true|false)"$/
* @param string $offline New value for navigator online mode
* @throws DriverException If the navigator.online mode is not available
+ * @deprecated since 4.1 use i_switch_network_connection instead.
*/
public function i_switch_offline_mode(string $offline) {
- $this->runtime_js("network.setForceOffline($offline)");
+ $this->i_switch_network_connection($offline == 'true' ? 'offline' : 'wifi');
+ }
+
+ /**
+ * Switch network connection.
+ *
+ * @When /^I switch network connection to (wifi|cellular|offline)$/
+ * @param string $more New network mode.
+ * @throws DriverException If the navigator.online mode is not available
+ */
+ public function i_switch_network_connection(string $mode) {
+ switch ($mode) {
+ case 'wifi':
+ $this->runtime_js("network.setForceConnectionMode('$mode');");
+ break;
+ case 'cellular':
+ $this->runtime_js("network.setForceConnectionMode('$mode');");
+ break;
+ case 'offline':
+ $this->runtime_js("network.setForceConnectionMode('none');");
+ break;
+ }
}
}
diff --git a/local_moodleappbehat/tests/behat/behat_app_helper.php b/local_moodleappbehat/tests/behat/behat_app_helper.php
index 9f8236183..3142ce5f9 100644
--- a/local_moodleappbehat/tests/behat/behat_app_helper.php
+++ b/local_moodleappbehat/tests/behat/behat_app_helper.php
@@ -95,6 +95,12 @@ class behat_app_helper extends behat_base {
public function start_scenario() {
$this->check_behat_setup();
$this->fix_moodle_setup();
+
+ if ($this->apprunning) {
+ $this->notify_unload();
+ $this->apprunning = false;
+ }
+
$this->ionicurl = $this->start_or_reuse_ionic();
}
@@ -274,17 +280,20 @@ class behat_app_helper extends behat_base {
* @throws DriverException If the app fails to load properly
*/
protected function prepare_browser(array $options = []) {
- $restart = $options['restart'] ?? true;
+ if ($this->evaluate_script('window.behat') && $this->runtime_js('hasInitialized()')) {
+ // Already initialized.
+ return;
+ }
- if ($restart) {
- if ($this->apprunning) {
- $this->notify_unload();
- }
+ $restart = false;
- // Restart the browser and set its size.
- $this->getSession()->restart();
+ if (!$this->apprunning) {
+ $this->check_tags();
+
+ $restart = true;
+
+ // Reset its size.
$this->resize_window($this->windowsize, true);
-
if (empty($this->ionicurl)) {
$this->ionicurl = $this->start_or_reuse_ionic();
}
@@ -506,6 +515,8 @@ class behat_app_helper extends behat_base {
$successXPath = '//page-core-mainmenu';
}
+ $this->i_log_out_in_app(false);
+
$this->handle_url($url, $successXPath);
}
@@ -536,7 +547,6 @@ class behat_app_helper extends behat_base {
if ($result !== 'OK') {
throw new DriverException('Error handling url - ' . $result);
}
-
if (!empty($successXPath)) {
// Wait until the page appears.
$this->spin(
@@ -550,6 +560,8 @@ class behat_app_helper extends behat_base {
}
$this->wait_for_pending_js();
+
+ $this->i_wait_the_app_to_restart();
}
/**
diff --git a/scripts/create_langindex.sh b/scripts/create_langindex.sh
index 3171c270a..a34fe6f6e 100755
--- a/scripts/create_langindex.sh
+++ b/scripts/create_langindex.sh
@@ -68,6 +68,8 @@ function do_match {
print_message "$2"
tput setaf 6
grep "$match" $LANGPACKSFOLDER/en/*.php
+ else
+ coincidence=0
fi
}
diff --git a/scripts/langindex.json b/scripts/langindex.json
index c61abf87b..5a015724d 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1705,7 +1705,9 @@
"core.erroropenfilenoextension": "local_moodlemobileapp",
"core.erroropenpopup": "local_moodlemobileapp",
"core.errorrenamefile": "local_moodlemobileapp",
+ "core.errorsitesupport": "local_moodlemobileapp",
"core.errorsomedatanotdownloaded": "local_moodlemobileapp",
+ "core.errorsomethingwrong": "local_moodlemobileapp",
"core.errorsync": "local_moodlemobileapp",
"core.errorsyncblocked": "local_moodlemobileapp",
"core.errorurlschemeinvalidscheme": "local_moodlemobileapp",
@@ -2179,6 +2181,8 @@
"core.settings.colorscheme-system": "local_moodlemobileapp",
"core.settings.colorscheme-system-notice": "local_moodlemobileapp",
"core.settings.compilationinfo": "local_moodlemobileapp",
+ "core.settings.connecttosync": "local_moodlemobileapp",
+ "core.settings.connectwifitosync": "local_moodlemobileapp",
"core.settings.copyinfo": "local_moodlemobileapp",
"core.settings.cordovadevicemodel": "local_moodlemobileapp",
"core.settings.cordovadeviceosversion": "local_moodlemobileapp",
@@ -2200,9 +2204,7 @@
"core.settings.enablefirebaseanalyticsdescription": "local_moodlemobileapp",
"core.settings.enablerichtexteditor": "local_moodlemobileapp",
"core.settings.enablerichtexteditordescription": "local_moodlemobileapp",
- "core.settings.enablesyncwifi": "local_moodlemobileapp",
"core.settings.entriesincache": "local_moodlemobileapp",
- "core.settings.errorsyncsite": "local_moodlemobileapp",
"core.settings.estimatedfreespace": "local_moodlemobileapp",
"core.settings.filesystemroot": "local_moodlemobileapp",
"core.settings.fontsize": "local_moodlemobileapp",
@@ -2219,6 +2221,7 @@
"core.settings.locationhref": "local_moodlemobileapp",
"core.settings.loggedin": "message",
"core.settings.loggedoff": "message",
+ "core.settings.logintosync": "local_moodlemobileapp",
"core.settings.navigatorlanguage": "local_moodlemobileapp",
"core.settings.navigatoruseragent": "local_moodlemobileapp",
"core.settings.networkstatus": "local_moodlemobileapp",
@@ -2233,7 +2236,9 @@
"core.settings.showdownloadoptions": "local_moodlemobileapp",
"core.settings.siteinfo": "local_moodlemobileapp",
"core.settings.sites": "moodle",
+ "core.settings.sitesyncfailed": "local_moodlemobileapp",
"core.settings.spaceusage": "local_moodlemobileapp",
+ "core.settings.syncdatasaver": "local_moodlemobileapp",
"core.settings.synchronization": "local_moodlemobileapp",
"core.settings.synchronizenow": "local_moodlemobileapp",
"core.settings.synchronizenowhelp": "local_moodlemobileapp",
diff --git a/src/addons/block/timeline/tests/behat/basic_usage.feature b/src/addons/block/timeline/tests/behat/basic_usage.feature
index d91bff7f4..573d6c6c3 100644
--- a/src/addons/block/timeline/tests/behat/basic_usage.feature
+++ b/src/addons/block/timeline/tests/behat/basic_usage.feature
@@ -49,7 +49,6 @@ Feature: Timeline block.
@lms_from3.11
Scenario: See courses inside block
Given I entered the app as "student1"
- And I press "Open block drawer" in the app
Then I should find "Assignment 00" within "Timeline" "ion-card" in the app
And I should find "Assignment 02" within "Timeline" "ion-card" in the app
And I should find "Assignment 05" within "Timeline" "ion-card" in the app
diff --git a/src/addons/messages/tests/behat/basic_usage.feature b/src/addons/messages/tests/behat/basic_usage.feature
index 0fcc067db..bd35f8756 100755
--- a/src/addons/messages/tests/behat/basic_usage.feature
+++ b/src/addons/messages/tests/behat/basic_usage.feature
@@ -163,7 +163,7 @@ Feature: Test basic usage of messages in app
And I set the field "Search" to "student1" in the app
And I press "Search" "button" in the app
And I press "Student1 student1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "New message" to "heeey student" in the app
And I press "Send" in the app
Then I should find "heeey student" in the app
@@ -172,7 +172,7 @@ Feature: Test basic usage of messages in app
And I press "Send" in the app
Then I should find "byee" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Student1 student1" in the app
Then I should find "heeey student" in the app
@@ -192,14 +192,14 @@ Feature: Test basic usage of messages in app
And I set the field "Search" to "student1" in the app
And I press "Search" "button" in the app
And I press "Student1 student1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "New message" to "heeey student" in the app
And I press "Send" in the app
And I set the field "New message" to "byee" in the app
And I press "Send" in the app
Then I should find "byee" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I run cron tasks in the app
Given I entered the app as "student1"
@@ -346,12 +346,12 @@ Feature: Test basic usage of messages in app
And I press "Send" in the app
Then I should find "self conversation online" in the app
- When I switch offline mode to "true"
+ When I switch network connection to offline
And I set the field "New message" to "self conversation offline" in the app
And I press "Send" in the app
Then I should find "self conversation offline" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Student1 student1" in the app
And I press "Display options" in the app
diff --git a/src/addons/mod/assign/tests/behat/basic_usage.feature b/src/addons/mod/assign/tests/behat/basic_usage.feature
index ec41e1017..67e535139 100755
--- a/src/addons/mod/assign/tests/behat/basic_usage.feature
+++ b/src/addons/mod/assign/tests/behat/basic_usage.feature
@@ -79,6 +79,7 @@ Feature: Test basic usage of assignment activity in app
# Submit second attempt as a student
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
+ When I pull to refresh in the app
Then I should find "Reopened" in the app
And I should find "2 out of Unlimited" in the app
And I should find "Add a new attempt based on previous submission" in the app
@@ -97,6 +98,7 @@ Feature: Test basic usage of assignment activity in app
# View second attempt as a teacher
Given I entered the assign activity "assignment1" on course "Course 1" as "teacher1" in the app
When I press "Participants" in the app
+ And I pull to refresh in the app
And I press "Student student" near "assignment1" in the app
Then I should find "Online text submissions" in the app
And I should find "Submission test 2nd attempt" in the app
@@ -104,14 +106,14 @@ Feature: Test basic usage of assignment activity in app
Scenario: Add submission offline (online text) & Submit for grading offline & Sync submissions
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
When I press "Add submission" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Online text submissions" to "Submission test" in the app
And I press "Save" in the app
And I press "Submit assignment" in the app
And I press "OK" in the app
Then I should find "This Assignment has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "assignment1" in the app
And I press "Information" in the app
@@ -122,7 +124,7 @@ Feature: Test basic usage of assignment activity in app
Scenario: Edit an offline submission before synchronising it
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
When I press "Add submission" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Online text submissions" to "Submission test original offline" in the app
And I press "Save" in the app
Then I should find "This Assignment has offline data to be synchronised." in the app
@@ -139,7 +141,7 @@ Feature: Test basic usage of assignment activity in app
And I press "OK" in the app
Then I should find "This Assignment has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "assignment1" in the app
Then I should find "Submitted for grading" in the app
diff --git a/src/addons/mod/chat/tests/behat/basic_usage.feature b/src/addons/mod/chat/tests/behat/basic_usage.feature
index 35a4972f6..8b0cd1f65 100755
--- a/src/addons/mod/chat/tests/behat/basic_usage.feature
+++ b/src/addons/mod/chat/tests/behat/basic_usage.feature
@@ -34,6 +34,9 @@ Feature: Test basic usage of chat in app
And I press "Send" in the app
Then I should find "Hi!" in the app
And I should find "I am David" in the app
+ # Confirm leave the page
+ And I press the back button in the app
+ And I press "OK" in the app
# Read messages, view connected users, send beep and reply as student2
Given I entered the chat activity "Test chat name" on course "Course 1" as "student2" in the app
@@ -62,6 +65,9 @@ Feature: Test basic usage of chat in app
When I set the field "New message" to "I am David" in the app
And I press "Send" in the app
Then I should find "I am David" in the app
+ # Confirm leave the page
+ And I press the back button in the app
+ And I press "OK" in the app
# Read messages from past sessions as student2
Given I entered the chat activity "Test chat name" on course "Course 1" as "student2" in the app
diff --git a/src/addons/mod/chat/tests/behat/navigation.feature b/src/addons/mod/chat/tests/behat/navigation.feature
index 3490ea6f0..75b06cd18 100644
--- a/src/addons/mod/chat/tests/behat/navigation.feature
+++ b/src/addons/mod/chat/tests/behat/navigation.feature
@@ -23,6 +23,9 @@ Feature: Test chat navigation
And I set the field "New message" to "Test message" in the app
And I press "Send" in the app
Then I should find "Test message" in the app
+ # Confirm leave the page
+ And I press the back button in the app
+ And I press "OK" in the app
Scenario: Tablet navigation on chat
Given I entered the course "Course 1" as "student2" in the app
diff --git a/src/addons/mod/choice/tests/behat/basic_usage.feature b/src/addons/mod/choice/tests/behat/basic_usage.feature
index 753181599..aae180c51 100755
--- a/src/addons/mod/choice/tests/behat/basic_usage.feature
+++ b/src/addons/mod/choice/tests/behat/basic_usage.feature
@@ -72,7 +72,7 @@ Feature: Test basic usage of choice activity in app
| choice | Test single choice name | Test single choice description | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
And I entered the choice activity "Test single choice name" on course "Course 1" as "student1" in the app
When I select "Option 1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I select "Option 2" in the app
And I press "Save my choice" in the app
Then I should find "Are you sure" in the app
@@ -85,7 +85,7 @@ Feature: Test basic usage of choice activity in app
And I should not find "Option 2: 1" in the app
And I should not find "Option 3: 0" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Test single choice name" in the app
Then I should find "Test single choice description" in the app
@@ -103,7 +103,7 @@ Feature: Test basic usage of choice activity in app
| choice | Test single choice name | Test single choice description | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
And I entered the choice activity "Test single choice name" on course "Course 1" as "student1" in the app
When I select "Option 1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I select "Option 2" in the app
And I press "Save my choice" in the app
Then I should find "Are you sure" in the app
@@ -114,7 +114,7 @@ Feature: Test basic usage of choice activity in app
And I should not find "Option 2: 1" in the app
And I should not find "Option 3: 0" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I run cron tasks in the app
And I wait loading to finish in the app
Then I should find "Option 1: 0" in the app
@@ -133,7 +133,7 @@ Feature: Test basic usage of choice activity in app
Then I should find "Downloaded" within "Test single choice name" "ion-item" in the app
And I press the back button in the app
- When I switch offline mode to "true"
+ When I switch network connection to offline
And I press "Test multi choice name" in the app
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
@@ -152,7 +152,7 @@ Feature: Test basic usage of choice activity in app
And I should not find "Option 2: 1" in the app
And I should not find "Option 3: 0" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Test single choice name" in the app
Then I should find "Option 1: 0" in the app
diff --git a/src/addons/mod/data/tests/behat/sync.feature b/src/addons/mod/data/tests/behat/sync.feature
index 699739215..41b7b9333 100644
--- a/src/addons/mod/data/tests/behat/sync.feature
+++ b/src/addons/mod/data/tests/behat/sync.feature
@@ -33,7 +33,7 @@ Feature: Users can store entries in database activities when offline and sync wh
Scenario: Create entry (offline)
Given I entered the data activity "Web links" on course "Course 1" as "student1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I should find "No entries in database" in the app
When I press "Add entries" in the app
And I set the following fields to these values in the app:
@@ -44,7 +44,7 @@ Feature: Users can store entries in database activities when offline and sync wh
And I should find "Moodle community site" in the app
And I should find "This Database has offline data to be synchronised" in the app
And I press the back button in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Web links" near "General" in the app
And I should find "https://moodle.org/" in the app
And I should find "Moodle community site" in the app
@@ -63,7 +63,8 @@ Feature: Users can store entries in database activities when offline and sync wh
And I press "Information" in the app
And I press "Download" in the app
And I wait until the page is ready
- And I switch offline mode to "true"
+ And I close the popup in the app
+ And I switch network connection to offline
When I press "Edit" in the app
And I set the following fields to these values in the app:
| URL | https://moodlecloud.com/ |
@@ -75,7 +76,7 @@ Feature: Users can store entries in database activities when offline and sync wh
And I should find "Moodle Cloud" in the app
And I should find "This Database has offline data to be synchronised" in the app
And I press the back button in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Web links" near "General" in the app
And I should not find "https://moodle.org/" in the app
And I should not find "Moodle community site" in the app
@@ -85,7 +86,7 @@ Feature: Users can store entries in database activities when offline and sync wh
And I press "Information" in the app
And I press "Refresh" in the app
And I wait until the page is ready
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Delete" in the app
And I should find "Are you sure you want to delete this entry?" in the app
And I press "Delete" in the app
@@ -93,7 +94,7 @@ Feature: Users can store entries in database activities when offline and sync wh
And I should find "Moodle Cloud" in the app
And I should find "This Database has offline data to be synchronised" in the app
And I press the back button in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Web links" near "General" in the app
And I should not find "https://moodlecloud.com/" in the app
And I should not find "Moodle Cloud" in the app
@@ -112,7 +113,8 @@ Feature: Users can store entries in database activities when offline and sync wh
And I press "Information" in the app
And I press "Download" in the app
And I wait until the page is ready
- When I switch offline mode to "true"
+ And I close the popup in the app
+ When I switch network connection to offline
And I press "Delete" in the app
And I should find "Are you sure you want to delete this entry?" in the app
And I press "Delete" in the app
@@ -121,7 +123,7 @@ Feature: Users can store entries in database activities when offline and sync wh
And I should find "This Database has offline data to be synchronised" in the app
And I press "Restore" in the app
And I press the back button in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Web links" near "General" in the app
Then I should find "https://moodle.org/" in the app
And I should find "Moodle community site" in the app
diff --git a/src/addons/mod/forum/tests/behat/basic_usage.feature b/src/addons/mod/forum/tests/behat/basic_usage.feature
index afdb62ebc..eb5750689 100755
--- a/src/addons/mod/forum/tests/behat/basic_usage.feature
+++ b/src/addons/mod/forum/tests/behat/basic_usage.feature
@@ -96,7 +96,7 @@ Feature: Test basic usage of forum activity in app
Then I should find "Reply" in the app
When I press the back button in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Initial discussion" in the app
And I press "Reply" in the app
And I set the field "Message" to "not sent reply" in the app
@@ -110,7 +110,7 @@ Feature: Test basic usage of forum activity in app
Then I should find "Not sent" in the app
And I should find "This Discussion has offline data to be synchronised" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Initial discussion" in the app
Then I should not find "Not sent" in the app
@@ -118,7 +118,7 @@ Feature: Test basic usage of forum activity in app
Scenario: Edit a not sent new discussion offline
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
- When I switch offline mode to "true"
+ When I switch network connection to offline
And I press "Add discussion topic" in the app
And I set the following fields to these values in the app:
| Subject | Auto-test |
@@ -129,7 +129,7 @@ Feature: Test basic usage of forum activity in app
And I press "Post to forum" in the app
Then I should find "This Forum has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press "Auto-test" in the app
Then I should find "Post to forum" in the app
@@ -151,7 +151,7 @@ Feature: Test basic usage of forum activity in app
Then I should find "Edit" in the app
When I press "Edit" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Message" to "Auto-test message edited" in the app
And I press "Save changes" in the app
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
@@ -163,7 +163,7 @@ Feature: Test basic usage of forum activity in app
And I press "Edit" in the app
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press "OK" in the app
And I press "Edit" in the app
And I set the field "Message" to "Auto-test message edited" in the app
@@ -183,7 +183,7 @@ Feature: Test basic usage of forum activity in app
When I press "Delete" in the app
And I press "Cancel" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Display options" near "Reply" in the app
Then I should find "Delete" in the app
@@ -192,7 +192,7 @@ Feature: Test basic usage of forum activity in app
When I press "OK" in the app
And I close the popup in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" near "Reply" in the app
And I press "Delete" in the app
And I press "Delete" in the app
@@ -215,14 +215,14 @@ Feature: Test basic usage of forum activity in app
When I press "Auto-test" in the app
And I press "None" near "Auto-test message" in the app
And I press "1" near "Cancel" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "None" near "test2" in the app
And I press "0" near "Cancel" in the app
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
And I should find "Average of ratings: -" in the app
And I should find "Average of ratings: 1" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
Then I should find "This Forum has offline data to be synchronised." in the app
@@ -244,7 +244,7 @@ Feature: Test basic usage of forum activity in app
Scenario: Reply a post offline
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
When I press "Initial discussion" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
Then I should find "Reply" in the app
When I press "Reply" in the app
@@ -255,7 +255,7 @@ Feature: Test basic usage of forum activity in app
And I should find "Not sent" in the app
When I press the back button in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Initial discussion" in the app
Then I should find "Initial discussion message" in the app
And I should find "ReplyMessage" in the app
@@ -263,7 +263,7 @@ Feature: Test basic usage of forum activity in app
Scenario: New discussion offline & Sync Forum
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
- When I switch offline mode to "true"
+ When I switch network connection to offline
And I press "Add discussion topic" in the app
And I set the following fields to these values in the app:
| Subject | DiscussionSubject |
@@ -273,7 +273,7 @@ Feature: Test basic usage of forum activity in app
And I should find "Not sent" in the app
And I should find "This Forum has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Test forum name" in the app
And I press "Information" in the app
@@ -286,7 +286,7 @@ Feature: Test basic usage of forum activity in app
Scenario: New discussion offline & Auto-sync forum
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
- When I switch offline mode to "true"
+ When I switch network connection to offline
And I press "Add discussion topic" in the app
And I set the following fields to these values in the app:
| Subject | DiscussionSubject |
@@ -296,7 +296,7 @@ Feature: Test basic usage of forum activity in app
And I should find "Not sent" in the app
And I should find "This Forum has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I run cron tasks in the app
And I wait loading to finish in the app
Then I should not find "Not sent" in the app
@@ -314,7 +314,7 @@ Feature: Test basic usage of forum activity in app
Then I should find "Downloaded" within "Test forum name" "ion-item" in the app
When I press the back button in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Test forum name" in the app
Then I should find "Initial discussion" in the app
diff --git a/src/addons/mod/forum/tests/behat/navigation.feature b/src/addons/mod/forum/tests/behat/navigation.feature
index 80bdef26b..89dae9dee 100644
--- a/src/addons/mod/forum/tests/behat/navigation.feature
+++ b/src/addons/mod/forum/tests/behat/navigation.feature
@@ -103,7 +103,7 @@ Feature: Test forum navigation
# Offline
When I press the back button in the app
And I press "Add discussion topic" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the following fields to these values in the app:
| Subject | Offline discussion 1 |
| Message | Offline discussion 1 message |
@@ -199,7 +199,7 @@ Feature: Test forum navigation
# Offline
When I press "Add discussion topic" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the following fields to these values in the app:
| Subject | Offline discussion 1 |
| Message | Offline discussion 1 message |
diff --git a/src/addons/mod/glossary/tests/behat/basic_usage.feature b/src/addons/mod/glossary/tests/behat/basic_usage.feature
index c3103b0a3..6a32f476c 100644
--- a/src/addons/mod/glossary/tests/behat/basic_usage.feature
+++ b/src/addons/mod/glossary/tests/behat/basic_usage.feature
@@ -100,6 +100,7 @@ Feature: Test basic usage of glossary in app
# View comments as a student
Given I entered the glossary activity "Test glossary" on course "Course 1" as "student1" in the app
And I press "Eggplant" in the app
+ When I pull to refresh in the app
Then I should find "Comments (2)" in the app
When I press "Comments" in the app
@@ -111,7 +112,7 @@ Feature: Test basic usage of glossary in app
When I press "Course downloads" in the app
When I press "Download" within "Test glossary" "ion-item" in the app
And I press the back button in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Test glossary" in the app
Then the header should be "Test glossary" in the app
And I should find "Cucumber" in the app
@@ -156,7 +157,7 @@ Feature: Test basic usage of glossary in app
Scenario: Sync
Given I entered the glossary activity "Test glossary" on course "Course 1" as "student1" in the app
And I press "Add a new entry" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the following fields to these values in the app:
| Concept | Broccoli |
| Definition | Brassica oleracea var. italica |
@@ -181,7 +182,7 @@ Feature: Test basic usage of glossary in app
And I should find "Entries to be synced" in the app
And I should find "This Glossary has offline data to be synchronised." in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press "Information" in the app
And I press "Synchronise now" in the app
Then the header should be "Test glossary" in the app
@@ -211,13 +212,13 @@ Feature: Test basic usage of glossary in app
# Rate entries as teacher2
Given I entered the glossary activity "Test glossary" on course "Course 1" as "teacher2" in the app
And I press "Cucumber" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "None" in the app
And I press "0" in the app
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
And I should find "Average of ratings: 1" in the app
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
Then I should find "This Glossary has offline data to be synchronised." in the app
diff --git a/src/addons/mod/glossary/tests/behat/navigation.feature b/src/addons/mod/glossary/tests/behat/navigation.feature
index 04b988ecf..309c0a1f0 100644
--- a/src/addons/mod/glossary/tests/behat/navigation.feature
+++ b/src/addons/mod/glossary/tests/behat/navigation.feature
@@ -173,7 +173,7 @@ Feature: Test glossary navigation
When I press the back button in the app
And I press "Clear search" in the app
And I press "Add a new entry" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the following fields to these values in the app:
| Concept | Tomato |
| Definition | Tomato is a fruit |
@@ -274,7 +274,7 @@ Feature: Test glossary navigation
# Offline
When I press "Clear search" in the app
And I press "Add a new entry" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the following fields to these values in the app:
| Concept | Tomato |
| Definition | Tomato is a fruit |
diff --git a/src/addons/mod/survey/tests/behat/basic_usage.feature b/src/addons/mod/survey/tests/behat/basic_usage.feature
index b6e0755c4..f12395a58 100755
--- a/src/addons/mod/survey/tests/behat/basic_usage.feature
+++ b/src/addons/mod/survey/tests/behat/basic_usage.feature
@@ -233,12 +233,12 @@ Feature: Test basic usage of survey activity in app
| activity | name | intro | template | course | idnumber | groupmode |
| survey | Test survey critical incidents | Test survey1 | 5 | C1 | survey1 | 0 |
Given I entered the survey activity "Test survey critical incidents" on course "Course 1" as "student1" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Submit" in the app
And I press "OK" in the app
Then I should see "This Survey has offline data to be synchronised."
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I press the back button in the app
And I press "Test survey critical incidents" in the app
And I press "Information" in the app
@@ -255,7 +255,7 @@ Feature: Test basic usage of survey activity in app
And I press "Course downloads" in the app
And I press "Download" within "Test survey critical incidents" "ion-item" in the app
And I press the back button in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Test survey name" in the app
Then I should see "There was a problem connecting to the site. Please check your connection and try again."
@@ -266,7 +266,7 @@ Feature: Test basic usage of survey activity in app
And I press "OK" in the app
Then I should see "This Survey has offline data to be synchronised."
- When I switch offline mode to "false"
+ When I switch network connection to wifi
And I run cron tasks in the app
Then I should not see "This Survey has offline data to be synchronised."
And I should see "You have completed this survey."
diff --git a/src/core/classes/errors/errors.ts b/src/core/classes/errors/errors.ts
index 517d91910..9a4b21a23 100644
--- a/src/core/classes/errors/errors.ts
+++ b/src/core/classes/errors/errors.ts
@@ -23,7 +23,7 @@ import { CoreAjaxWSError } from './ajaxwserror';
import { CoreCaptureError } from './captureerror';
import { CoreNetworkError } from './network-error';
import { CoreSiteError } from './siteerror';
-import { CoreErrorWithTitle } from './errorwithtitle';
+import { CoreErrorWithOptions } from './errorwithtitle';
import { CoreHttpError } from './httperror';
export const CORE_ERRORS_CLASSES: Type[] = [
@@ -36,6 +36,6 @@ export const CORE_ERRORS_CLASSES: Type[] = [
CoreSilentError,
CoreSiteError,
CoreWSError,
- CoreErrorWithTitle,
+ CoreErrorWithOptions,
CoreHttpError,
];
diff --git a/src/core/classes/errors/errorwithtitle.ts b/src/core/classes/errors/errorwithtitle.ts
index 0ff83d3b7..8a9e322b5 100644
--- a/src/core/classes/errors/errorwithtitle.ts
+++ b/src/core/classes/errors/errorwithtitle.ts
@@ -12,20 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import { AlertButton } from '@ionic/angular';
import { CoreError } from './error';
/**
* Error with an explicit title describing the problem (instead of just "Error" or a generic message).
* This title should be used to communicate the problem with users, and if it's undefined it should be omitted.
+ * The error also may contain customizable action buttons.
*/
-export class CoreErrorWithTitle extends CoreError {
+export class CoreErrorWithOptions extends CoreError {
title?: string;
+ buttons?: AlertButton[];
- constructor(message?: string, title?: string) {
+ constructor(message?: string, title?: string, buttons?: AlertButton[]) {
super(message);
this.title = title;
+ this.buttons = buttons;
}
}
diff --git a/src/core/components/tabs-outlet/core-tabs-outlet.html b/src/core/components/tabs-outlet/core-tabs-outlet.html
index ff4c3f825..8c92bb295 100644
--- a/src/core/components/tabs-outlet/core-tabs-outlet.html
+++ b/src/core/components/tabs-outlet/core-tabs-outlet.html
@@ -8,9 +8,10 @@
-
+
diff --git a/src/core/components/tabs/core-tabs.html b/src/core/components/tabs/core-tabs.html
index 89e426368..928d55486 100644
--- a/src/core/components/tabs/core-tabs.html
+++ b/src/core/components/tabs/core-tabs.html
@@ -7,10 +7,11 @@
-
+
+ (keyup)="tabAction.keyUp(tab.id, $event)" [layout]="layout" role="tab" [attr.aria-controls]="tab.id"
+ [attr.aria-selected]="selected == tab.id" [tabindex]="selected == tab.id ? 0 : -1">
{{ tab.title | translate}}
diff --git a/src/core/features/comments/tests/behat/basic_usage.feature b/src/core/features/comments/tests/behat/basic_usage.feature
index 9fef313be..f431b3e96 100644
--- a/src/core/features/comments/tests/behat/basic_usage.feature
+++ b/src/core/features/comments/tests/behat/basic_usage.feature
@@ -36,8 +36,9 @@ Feature: Test basic usage of comments in app
| Field description | Test field description |
And I press "Save"
And I close the browser tab opened by the app
- When I entered the course "Course 1" as "teacher1" in the app
- And I press "Data" in the app
+ And I close the popup in the app
+
+ When I pull to refresh in the app
And I press "Add entries" in the app
And I set the field "Test field name" to "Test" in the app
And I press "Save" in the app
@@ -75,7 +76,7 @@ Feature: Test basic usage of comments in app
Scenario: Add comments offline & Delete comments offline & Sync comments (database)
Given I entered the data activity "Data" on course "Course 1" as "teacher1" in the app
- When I press "Information" in the app
+ And I press "Information" in the app
And I press "Open in browser" in the app
And I switch to the browser tab opened by the app
And I log in as "teacher1"
@@ -84,14 +85,15 @@ Feature: Test basic usage of comments in app
| Field description | Test field description |
And I press "Save"
And I close the browser tab opened by the app
+ And I close the popup in the app
- Given I entered the data activity "Data" on course "Course 1" as "teacher1" in the app
- Then I press "Add entries" in the app
+ When I pull to refresh in the app
+ And I press "Add entries" in the app
And I set the field "Test field name" to "Test" in the app
And I press "Save" in the app
And I press "More" in the app
And I press "Comments (0)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Add a comment..." to "comment test" in the app
And I press "Send" in the app
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
@@ -100,7 +102,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (0)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
@@ -109,7 +111,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Toggle delete buttons" in the app
And I press "Delete" in the app
And I press "Delete" near "Cancel" in the app
@@ -120,7 +122,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
@@ -179,7 +181,7 @@ Feature: Test basic usage of comments in app
And I press "Save" in the app
And I press "potato" in the app
And I press "Comments (0)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Add a comment..." to "comment test" in the app
And I press "Send" in the app
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
@@ -188,7 +190,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (0)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
@@ -197,7 +199,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Toggle delete buttons" in the app
And I press "Delete" in the app
And I press "Delete" near "Cancel" in the app
@@ -208,7 +210,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
@@ -262,7 +264,7 @@ Feature: Test basic usage of comments in app
And I should find "Blog body" in the app
When I press "Comments (0)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I set the field "Add a comment..." to "comment test" in the app
And I press "Send" in the app
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
@@ -271,7 +273,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (0)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
@@ -280,7 +282,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "true"
+ And I switch network connection to offline
And I press "Toggle delete buttons" in the app
And I press "Delete" in the app
And I press "Delete" near "Cancel" in the app
@@ -291,7 +293,7 @@ Feature: Test basic usage of comments in app
When I press the back button in the app
And I press "Comments (1)" in the app
- And I switch offline mode to "false"
+ And I switch network connection to wifi
And I press "Display options" in the app
And I press "Synchronise now" in the app
And I close the popup in the app
diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts
index e58699c2a..0e987dc12 100644
--- a/src/core/features/course/services/course.ts
+++ b/src/core/features/course/services/course.ts
@@ -177,7 +177,9 @@ export class CoreCourseProvider {
CorePlatform.resume.subscribe(() => {
// Run the handler the app is open to keep user in online status.
setTimeout(() => {
- CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name);
+ CoreUtils.ignoreErrors(
+ CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name),
+ );
}, 1000);
});
diff --git a/src/core/features/course/tests/behat/basic_usage.feature b/src/core/features/course/tests/behat/basic_usage.feature
index 9be9bcf6d..078555930 100755
--- a/src/core/features/course/tests/behat/basic_usage.feature
+++ b/src/core/features/course/tests/behat/basic_usage.feature
@@ -411,6 +411,8 @@ Feature: Test basic usage of one course in app
And I select "Enrolment methods" from the "jump" singleselect
And I click on "Enable" "icon" in the "Self enrolment (Student)" "table_row"
And I close the browser tab opened by the app
+ And I close the popup in the app
+
Given I entered the app as "student2"
When I press "Site home" in the app
And I press "Available courses" in the app
diff --git a/src/core/features/courses/tests/behat/basic_usage.feature b/src/core/features/courses/tests/behat/basic_usage.feature
index b61cbd65d..803f091fa 100755
--- a/src/core/features/courses/tests/behat/basic_usage.feature
+++ b/src/core/features/courses/tests/behat/basic_usage.feature
@@ -148,9 +148,12 @@ Feature: Test basic usage of courses in app
# Grade assignment as teacher
Given I entered the app as "teacher1"
- When I press "Grade" in the app
+ When I pull to refresh in the app
+ And I press "Grade" in the app
Then the header should be "assignment" in the app
- And I should find "Test assignment description" in the app
+
+ When I pull to refresh in the app
+ Then I should find "Test assignment description" in the app
And I should find "Time remaining" in the app
When I press "Needs grading" in the app
diff --git a/src/core/features/login/tests/behat/basic_usage.feature b/src/core/features/login/tests/behat/basic_usage.feature
index d03a08844..62ffc00fe 100755
--- a/src/core/features/login/tests/behat/basic_usage.feature
+++ b/src/core/features/login/tests/behat/basic_usage.feature
@@ -26,8 +26,7 @@ Feature: Test basic usage of login in app
And I should find "Connect to Moodle" in the app
Scenario: Add a new account in the app & Site name in displayed when adding a new account
- When I enter the app
- And I press the back button in the app
+ When I launch the app
And I set the field "Your site" to "$WWWROOT" in the app
And I press "Connect to your site" in the app
Then I should find "Acceptance test site" in the app
@@ -42,9 +41,7 @@ Feature: Test basic usage of login in app
Scenario: Add a non existing account
When I enter the app
And I log in as "student1"
- And I press the user menu button in the app
- And I press "Log out" in the app
- And I wait the app to restart
+ When I log out in the app
And I press "Add" in the app
And I set the field "Your site" to "Wrong Site Address" in the app
And I press enter in the app
@@ -63,11 +60,16 @@ Feature: Test basic usage of login in app
Then I should find "Cannot connect" in the app
And I should find "Wrong Site Address" in the app
+ Scenario: Log out from the app
+ Given I entered the app as "student1"
+ And I press the user menu button in the app
+ When I press "Log out" in the app
+ And I wait the app to restart
+ Then the header should be "Accounts" in the app
+
Scenario: Delete an account
Given I entered the app as "student1"
- When I press the user menu button in the app
- And I press "Log out" in the app
- And I wait the app to restart
+ When I log out in the app
Then I should find "Acceptance test site" in the app
And I press "Edit accounts list" in the app
And I press "Remove account" near "Acceptance test site" in the app
@@ -75,14 +77,14 @@ Feature: Test basic usage of login in app
Then I should find "Connect to Moodle" in the app
But I should not find "Acceptance test site" in the app
- Scenario: Require minium version of the app for a site
-
+ Scenario: Require minium (previous) version of the app for a site
# Log in with a previous required version
Given the following config values are set as admin:
| minimumversion | 3.8.1 | tool_mobile |
When I enter the app
Then I should not find "App update required" in the app
+ Scenario: Require minium (future) version of the app for a site
# Log in with a future required version
Given the following config values are set as admin:
| minimumversion | 11.0.0 | tool_mobile |
diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.ts b/src/core/features/mainmenu/components/user-menu/user-menu.ts
index c20d16bce..4b02fa22e 100644
--- a/src/core/features/mainmenu/components/user-menu/user-menu.ts
+++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts
@@ -71,7 +71,14 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
// Load the handlers.
if (this.siteInfo) {
- this.user = await CoreUser.getProfile(this.siteInfo.userid);
+ try {
+ this.user = await CoreUser.getProfile(this.siteInfo.userid);
+ } catch {
+ this.user = {
+ id: this.siteInfo.userid,
+ fullname: this.siteInfo.fullname,
+ };
+ }
this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user, CoreUserDelegateContext.USER_MENU)
.subscribe((handlers) => {
diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts
index e817633bf..366ae942d 100644
--- a/src/core/features/pushnotifications/services/pushnotifications.ts
+++ b/src/core/features/pushnotifications/services/pushnotifications.ts
@@ -746,11 +746,7 @@ export class CorePushNotificationsProvider {
CoreEvents.trigger(CoreEvents.DEVICE_REGISTERED_IN_MOODLE, {}, site.getId());
// Insert the device in the local DB.
- try {
- await this.registeredDevicesTables[site.getId()].insert(data);
- } catch (err) {
- // Ignore errors.
- }
+ await CoreUtils.ignoreErrors(this.registeredDevicesTables[site.getId()].insert(data));
}
} finally {
// Remove pending unregisters for this site.
diff --git a/src/core/features/settings/lang.json b/src/core/features/settings/lang.json
index 42114fcd0..1a6550718 100644
--- a/src/core/features/settings/lang.json
+++ b/src/core/features/settings/lang.json
@@ -4,8 +4,8 @@
"appsettings": "App settings",
"appversion": "App version",
"cannotsyncloggedout": "This site cannot be synchronised because you've logged out. Please try again when you're logged in the site again.",
- "cannotsyncoffline": "Cannot synchronise offline.",
- "cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.",
+ "cannotsyncoffline": "Site synchronisation failed because your device is not connected to the internet.",
+ "cannotsyncwithoutwifi": "Your device is not connected to Wi-Fi. Connect to a Wi-Fi network or turn off Data Saver in the app settings.",
"changelanguage": "Change to {{$a}}",
"changelanguagealert": "Changing the language will restart the app.",
"colorscheme-dark": "Dark",
@@ -14,6 +14,8 @@
"colorscheme-system": "System default",
"colorscheme": "Color Scheme",
"compilationinfo": "Compilation info",
+ "connectwifitosync": "Connect to a Wi-Fi network or turn off Data saver to synchronise sites.",
+ "connecttosync": "Your device is offline. Connect to the internet to synchronise sites.",
"copyinfo": "Copy device info on the clipboard",
"cordovadevicemodel": "Cordova device model",
"cordovadeviceosversion": "Cordova device OS version",
@@ -35,9 +37,7 @@
"enablefirebaseanalyticsdescription": "If enabled, the app will collect anonymous data usage.",
"enablerichtexteditor": "Enable text editor",
"enablerichtexteditordescription": "If enabled, a text editor will be available when entering content.",
- "enablesyncwifi": "Allow sync only when on Wi-Fi",
"entriesincache": "{{$a}} entries in cache",
- "errorsyncsite": "Error synchronising site data. Please check your Internet connection and try again.",
"estimatedfreespace": "Estimated free space",
"filesystemroot": "File system root",
"fontsize": "Text size",
@@ -54,6 +54,7 @@
"locationhref": "Web view URL",
"loggedin": "Online",
"loggedoff": "Offline",
+ "logintosync": "Log in to synchronise",
"navigatorlanguage": "Navigator language",
"navigatoruseragent": "Navigator userAgent",
"networkstatus": "Internet connection status",
@@ -68,7 +69,9 @@
"showdownloadoptions": "Show download options",
"siteinfo": "Site info",
"sites": "Sites",
+ "sitesyncfailed": "Site synchronisation failed",
"spaceusage": "Space usage",
+ "syncdatasaver": "Data saver: Synchronise only when on Wi-Fi",
"synchronization": "Synchronisation",
"synchronizenow": "Synchronise now",
"synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.",
diff --git a/src/core/features/settings/pages/site/site.html b/src/core/features/settings/pages/site/site.html
index 0470d5026..215d5f82e 100644
--- a/src/core/features/settings/pages/site/site.html
+++ b/src/core/features/settings/pages/site/site.html
@@ -37,11 +37,20 @@
-
+
+
+
+
+
+ {{ 'core.settings.connectwifitosync' | translate }}
+ {{ 'core.settings.connecttosync' | translate }}
+
+
diff --git a/src/core/features/settings/pages/site/site.ts b/src/core/features/settings/pages/site/site.ts
index 57826c5ea..4a017185b 100644
--- a/src/core/features/settings/pages/site/site.ts
+++ b/src/core/features/settings/pages/site/site.ts
@@ -25,6 +25,11 @@ import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/
import { CoreSettingsHandlersSource } from '@features/settings/classes/settings-handlers-source';
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
import { CoreDomUtils } from '@services/utils/dom';
+import { CoreNetwork } from '@services/network';
+import { Subscription } from 'rxjs';
+import { NgZone } from '@singletons';
+import { CoreConstants } from '@/core/constants';
+import { CoreConfig } from '@services/config';
/**
* Page that displays the list of site settings pages.
@@ -39,8 +44,13 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
handlers: CoreListItemsManager;
+ dataSaver = false;
+ limitedConnection = false;
+ isOnline = true;
+
protected siteId: string;
protected sitesObserver: CoreEventObserver;
+ protected networkObserver: Subscription;
protected isDestroyed = false;
constructor() {
@@ -53,12 +63,25 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
this.refreshData();
}, this.siteId);
+
+ this.isOnline = CoreNetwork.isOnline();
+ this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+
+ this.networkObserver = CoreNetwork.onChange().subscribe(() => {
+ // Execute the callback in the Angular zone, so change detection doesn't stop working.
+ NgZone.run(() => {
+ this.isOnline = CoreNetwork.isOnline();
+ this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ });
+ });
}
/**
- * View loaded.
+ * @inheritdoc
*/
async ngAfterViewInit(): Promise {
+ this.dataSaver = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
+
const pageToOpen = CoreNavigator.getRouteParam('page');
try {
@@ -94,7 +117,7 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
if (this.isDestroyed) {
return;
}
- CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
+ CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true);
}
}
@@ -121,11 +144,12 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
}
/**
- * Page destroyed.
+ * @inheritdoc
*/
ngOnDestroy(): void {
this.isDestroyed = true;
- this.sitesObserver?.off();
+ this.sitesObserver.off();
+ this.networkObserver.unsubscribe();
this.handlers.destroy();
}
diff --git a/src/core/features/settings/pages/synchronization/synchronization.html b/src/core/features/settings/pages/synchronization/synchronization.html
index 7e4911b89..00c94086f 100644
--- a/src/core/features/settings/pages/synchronization/synchronization.html
+++ b/src/core/features/settings/pages/synchronization/synchronization.html
@@ -17,37 +17,97 @@
-
+
{{ 'core.settings.syncsettings' | translate }}
- {{ 'core.settings.enablesyncwifi' | translate }}
-
-
-
-
-
- {{ 'core.settings.sites' | translate }}
-
-
-
-
+ {{ 'core.settings.syncdatasaver' | translate }}
- {{ site.fullName }}
- {{ site.siteUrlWithoutProtocol }}
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {{ 'core.settings.connectwifitosync' | translate }}
+ {{ 'core.settings.connecttosync' | translate }}
+
+
+
+
+
+
+
+ {{ 'core.accounts' | translate }}
+
+
+
+
+
+
+
+
+
+ {{
+ accountsList.currentSite.siteUrlWithoutProtocol }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ sites[0].siteUrlWithoutProtocol }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{site.fullName}}
+ {{ 'core.settings.logintosync' | translate }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/settings/pages/synchronization/synchronization.ts b/src/core/features/settings/pages/synchronization/synchronization.ts
index 5b5b664c3..47e3b6f3f 100644
--- a/src/core/features/settings/pages/synchronization/synchronization.ts
+++ b/src/core/features/settings/pages/synchronization/synchronization.ts
@@ -16,11 +16,15 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { CoreConstants } from '@/core/constants';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
-import { CoreSites, CoreSiteBasicInfo } from '@services/sites';
+import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreConfig } from '@services/config';
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
-import { Translate } from '@singletons';
+import { NgZone, Translate } from '@singletons';
+import { CoreAccountsList, CoreLoginHelper } from '@features/login/services/login-helper';
+import { CoreNetwork } from '@services/network';
+import { Subscription } from 'rxjs';
+import { CoreNavigator } from '@services/navigator';
/**
* Page that displays the synchronization settings.
@@ -28,61 +32,101 @@ import { Translate } from '@singletons';
@Component({
selector: 'page-core-app-settings-synchronization',
templateUrl: 'synchronization.html',
+ styleUrls: ['../../../login/sitelist.scss'],
})
export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
- sites: CoreSiteBasicInfo[] = [];
+ accountsList: CoreAccountsList = {
+ sameSite: [],
+ otherSites: [],
+ count: 0,
+ };
+
sitesLoaded = false;
- currentSiteId = '';
- syncOnlyOnWifi = false;
+ dataSaver = false;
+ limitedConnection = false;
+ isOnline = true;
+
protected isDestroyed = false;
protected sitesObserver: CoreEventObserver;
+ protected networkObserver: Subscription;
constructor() {
- this.currentSiteId = CoreSites.getCurrentSiteId();
-
this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async (data) => {
- const site = await CoreSites.getSite(data.siteId);
+ const siteId = data.siteId;
- const siteEntry = this.sites.find((siteEntry) => siteEntry.id == site.id);
- if (siteEntry) {
- const siteInfo = site.getInfo();
+ let siteEntry = siteId === this.accountsList.currentSite?.id
+ ? this.accountsList.currentSite
+ : undefined;
- siteEntry.siteName = site.getSiteName();
+ if (!siteEntry) {
+ siteEntry = this.accountsList.sameSite.find((siteEntry) => siteEntry.id === siteId);
+ }
- if (siteInfo) {
- siteEntry.siteUrl = siteInfo.siteurl;
- siteEntry.fullName = siteInfo.fullname;
- }
+ if (!siteEntry) {
+ this.accountsList.otherSites.some((sites) => {
+ siteEntry = sites.find((siteEntry) => siteEntry.id === siteId);
+
+ return siteEntry;
+ });
+ }
+
+ if (!siteEntry) {
+ return;
+ }
+
+ const site = await CoreSites.getSite(siteId);
+
+ const siteInfo = site.getInfo();
+
+ siteEntry.siteName = site.getSiteName();
+
+ if (siteInfo) {
+ siteEntry.siteUrl = siteInfo.siteurl;
+ siteEntry.fullName = siteInfo.fullname;
}
});
+
+ this.isOnline = CoreNetwork.isOnline();
+ this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+
+ this.networkObserver = CoreNetwork.onChange().subscribe(() => {
+ // Execute the callback in the Angular zone, so change detection doesn't stop working.
+ NgZone.run(() => {
+ this.isOnline = CoreNetwork.isOnline();
+ this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ });
+ });
+
}
/**
- * View loaded.
+ * @inheritdoc
*/
async ngOnInit(): Promise {
+ const currentSiteId = CoreSites.getCurrentSiteId();
+
try {
- this.sites = await CoreSites.getSortedSites();
+ this.accountsList = await CoreLoginHelper.getAccountsList(currentSiteId);
} catch {
// Ignore errors.
}
this.sitesLoaded = true;
- this.syncOnlyOnWifi = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
+ this.dataSaver = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, true);
}
/**
* Called when sync only on wifi setting is enabled or disabled.
*/
syncOnlyOnWifiChanged(): void {
- CoreConfig.set(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, this.syncOnlyOnWifi ? 1 : 0);
+ CoreConfig.set(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, this.dataSaver ? 1 : 0);
}
/**
- * Syncrhonizes a site.
+ * Synchronizes a site.
*
* @param siteId Site ID.
*/
@@ -95,10 +139,20 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
return;
}
- CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
+ CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true);
}
}
+ /**
+ * Changes site.
+ *
+ * @param siteId Site ID.
+ */
+ async login(siteId: string): Promise {
+ // This navigation will logout and navigate to the site home.
+ await CoreNavigator.navigateToSiteHome({ preferCurrentTab: false , siteId });
+ }
+
/**
* Returns true if site is beeing synchronized.
*
@@ -120,11 +174,12 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
}
/**
- * Page destroyed.
+ * @inheritdoc
*/
ngOnDestroy(): void {
this.isDestroyed = true;
- this.sitesObserver?.off();
+ this.sitesObserver.off();
+ this.networkObserver.unsubscribe();
}
}
diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts
index 1d1ee82ea..18e754265 100644
--- a/src/core/features/settings/services/settings-helper.ts
+++ b/src/core/features/settings/services/settings-helper.ts
@@ -29,6 +29,7 @@ import { CoreCourse } from '@features/course/services/course';
import { makeSingleton, Translate } from '@singletons';
import { CoreError } from '@classes/errors/error';
import { Observable, Subject } from 'rxjs';
+import { CoreTextUtils } from '@services/utils/text';
/**
* Object with space usage and cache entries that can be erased.
@@ -260,6 +261,7 @@ export class CoreSettingsHelperProvider {
const site = await CoreSites.getSite(siteId);
const hasSyncHandlers = CoreCronDelegate.hasManualSyncHandlers();
+ // All these errors should not happen on manual sync because are prevented on UI.
if (site.isLoggedOut()) {
// Cannot sync logged out sites.
throw new CoreError(Translate.instant('core.settings.cannotsyncloggedout'));
@@ -286,6 +288,8 @@ export class CoreSettingsHelperProvider {
try {
await syncPromise;
+ } catch (error) {
+ throw CoreTextUtils.addTitleToError(error, Translate.instant('core.settings.sitesyncfailed'));
} finally {
delete this.syncPromises[siteId];
}
diff --git a/src/core/features/settings/tests/behat/settings_navigation.feature b/src/core/features/settings/tests/behat/settings_navigation.feature
index 9096b1138..5e14ec472 100644
--- a/src/core/features/settings/tests/behat/settings_navigation.feature
+++ b/src/core/features/settings/tests/behat/settings_navigation.feature
@@ -1,4 +1,4 @@
-@app @javascript
+@app @javascript @core_settings
Feature: It navigates properly within settings.
Background:
diff --git a/src/core/features/settings/tests/behat/sync.feature b/src/core/features/settings/tests/behat/sync.feature
new file mode 100644
index 000000000..274706060
--- /dev/null
+++ b/src/core/features/settings/tests/behat/sync.feature
@@ -0,0 +1,150 @@
+@app @javascript @core_settings
+Feature: It synchronise sites properly
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "users" exist:
+ | username | firstname | lastname |
+ | student1 | david | student |
+ | student2 | pau | student2 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber | option | allowmultiple | allowupdate | showresults |
+ | choice | Sync choice | Intro | C1 | choice1 | Option 1, Option 2, Option 3 | 0 | 0 | 1 |
+
+ Scenario: Sync the current site
+ # Add something offline
+ Given I entered the choice activity "Sync choice" on course "Course 1" as "student1" in the app
+ When I switch network connection to offline
+ And I select "Option 1" in the app
+ And I press "Save my choice" in the app
+ And I press "OK" in the app
+ Then I should find "This Choice has offline data to be synchronised." in the app
+
+ # Cannot sync in offline
+ When I press the back button in the app
+ And I press the back button in the app
+ And I press the user menu button in the app
+ And I press "Preferences" in the app
+ Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+
+ When I switch network connection to wifi
+ Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+
+ # Check synced
+ When I press "Synchronise now" "button" in the app
+ And I wait loading to finish in the app
+ And I switch network connection to offline
+ And I press the back button in the app
+ And I entered the course "Course 1" in the app
+ And I press "Sync choice" in the app
+ Then I should not find "This Choice has offline data to be synchronised." in the app
+
+ # Check limited sync.
+ When I switch network connection to cellular
+ And I press the back button in the app
+ And I press the back button in the app
+ And I press the user menu button in the app
+ And I press "Preferences" in the app
+
+ # Cannot sync in cellular
+ Then I should find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+
+ Scenario: Sync sites messages with different network connections
+ Given I entered the app as "student1"
+
+ # Wifi + data saver on.
+ When I press the more menu button in the app
+ And I press "App settings" in the app
+ And I press "Synchronisation" in the app
+ Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should find "Accounts" in the app
+
+ # Limited + data saver on.
+ When I switch network connection to cellular
+ Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should not find "Accounts" in the app
+
+ # Offline + data saver on.
+ When I switch network connection to offline
+ Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should not find "Accounts" in the app
+
+ # Wifi + data saver off.
+ When I press "Data saver: Synchronise only when on Wi-Fi" in the app
+ And I switch network connection to wifi
+ Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should find "Accounts" in the app
+
+ # Limited + data saver off.
+ When I switch network connection to cellular
+ Then I should not find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should find "Accounts" in the app
+
+ # Offline + data saver off.
+ When I switch network connection to offline
+ Then I should find "Your device is offline. Connect to the internet to synchronise sites." in the app
+ And I should not find "Connect to a Wi-Fi network or turn off Data saver to synchronise sites." in the app
+ And I should not find "Accounts" in the app
+
+ Scenario: Sync logged in and logged out sites
+ Given I entered the app as "student1"
+ And I log out in the app
+ And I entered the choice activity "Sync choice" on course "Course 1" as "student2" in the app
+
+ # Add something offline
+ When I switch network connection to offline
+ And I select "Option 1" in the app
+ And I press "Save my choice" in the app
+ And I press "OK" in the app
+ Then I should find "This Choice has offline data to be synchronised." in the app
+
+ When I press the back button in the app
+ And I press the back button in the app
+ And I press the more menu button in the app
+ And I press "App settings" in the app
+ And I press "Synchronisation" in the app
+ And I switch network connection to wifi
+ Then I should find "Accounts" in the app
+
+ # Check synced
+ When I press "Synchronise now" "button" in the app
+ And I wait loading to finish in the app
+ And I switch network connection to offline
+ And I press the back button in the app
+ And I entered the course "Course 1" in the app
+ And I press "Sync choice" in the app
+ Then I should not find "This Choice has offline data to be synchronised." in the app
+
+ # Test log in to sync
+ When I press the back button in the app
+ And I press the back button in the app
+ And I press the more menu button in the app
+ And I press "App settings" in the app
+ And I press "Synchronisation" in the app
+ And I switch network connection to wifi
+ Then I should find "Accounts" in the app
+ And I should find "Log in to synchronise" in the app
+
+ When I press "Log in" in the app
+ Then I should find "Reconnect" in the app
+
+ When I set the field "Password" to "student1" in the app
+ And I press "Log in" in the app
+ And I press the more menu button in the app
+ And I press "App settings" in the app
+ And I press "Synchronisation" in the app
+ Then I should not find "Log in to synchronise" in the app
diff --git a/src/core/features/usertours/components/user-tour/user-tour.scss b/src/core/features/usertours/components/user-tour/user-tour.scss
index 2e37de273..812b509bc 100644
--- a/src/core/features/usertours/components/user-tour/user-tour.scss
+++ b/src/core/features/usertours/components/user-tour/user-tour.scss
@@ -1,7 +1,7 @@
:host {
--popover-background: var(--ion-overlay-background-color, var(--ion-background-color, #fff));
- z-index: 99;
+ z-index: 105; // Main menu is 101.
width: 100%;
height: 100%;
display: none;
diff --git a/src/core/features/usertours/components/user-tour/user-tour.ts b/src/core/features/usertours/components/user-tour/user-tour.ts
index 13a736198..cda15c4d2 100644
--- a/src/core/features/usertours/components/user-tour/user-tour.ts
+++ b/src/core/features/usertours/components/user-tour/user-tour.ts
@@ -62,6 +62,7 @@ export class CoreUserToursUserTourComponent implements AfterViewInit, OnDestroy
@Output() afterDismiss = new EventEmitter();
@HostBinding('class.is-active') active = false;
@HostBinding('class.is-popover') popover = false;
+ @HostBinding('class.backdrop') backdrop = true;
@ViewChild('wrapper') wrapper?: ElementRef;
focusStyles?: string;
diff --git a/src/core/lang.json b/src/core/lang.json
index a3e73bb03..c83d0aed7 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -96,6 +96,7 @@
"downloading": "Downloading",
"edit": "Edit",
"emptysplit": "This page will appear blank if the left panel is empty or is loading.",
+ "endonesteptour": "Got it",
"error": "Error",
"errorchangecompletion": "An error occurred while changing the completion status. Please try again.",
"errordeletefile": "Error deleting the file. Please try again.",
@@ -111,7 +112,9 @@
"erroropenfilenoextension": "Error opening file: the file doesn't have an extension.",
"erroropenpopup": "This activity is trying to open a popup. This is not supported in the app.",
"errorrenamefile": "Error renaming file. Please try again.",
+ "errorsitesupport": "If the problem persists, contact site support.",
"errorsomedatanotdownloaded": "If you downloaded this activity, please notice that some data isn't downloaded during the download process for performance and data usage reasons.",
+ "errorsomethingwrong": "Something went wrong. Please try again.",
"errorsync": "An error occurred while synchronising. Please try again.",
"errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.",
"errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.",
@@ -248,8 +251,8 @@
"resources": "Resources",
"restore": "Restore",
"restricted": "Restricted",
- "retry": "Retry",
"resume": "Resume",
+ "retry": "Retry",
"save": "Save",
"savechanges": "Save changes",
"scanqr": "Scan QR code",
@@ -328,11 +331,10 @@
"user": "User",
"userdeleted": "This user account has been deleted",
"userdetails": "User details",
- "usernotfullysetup": "User not fully set-up",
"usernologin": "Authentication has been revoked for this account",
- "usersuspended": "Registration suspended",
- "endonesteptour": "Got it",
+ "usernotfullysetup": "User not fully set-up",
"users": "Users",
+ "usersuspended": "Registration suspended",
"view": "View",
"viewcode": "View code",
"vieweditor": "View editor",
@@ -351,8 +353,8 @@
"year": "year",
"years": "years",
"yes": "Yes",
- "youreoffline": "You are offline",
- "youreonline": "You are back online",
+ "youreoffline": "Your device is offline",
+ "youreonline": "Your device is back online",
"zoomin": "Zoom In",
"zoomout": "Zoom Out"
}
diff --git a/src/core/services/app.ts b/src/core/services/app.ts
index a734676e8..9a3632274 100644
--- a/src/core/services/app.ts
+++ b/src/core/services/app.ts
@@ -30,7 +30,7 @@ import { CoreDatabaseTable } from '@classes/database/database-table';
import { CorePromisedValue } from '@classes/promised-value';
import { Subscription } from 'rxjs';
import { CorePlatform } from '@services/platform';
-import { CoreNetwork } from '@services/network';
+import { CoreNetwork, CoreNetworkConnection } from '@services/network';
/**
* Factory to provide some global functionalities, like access to the global app database.
@@ -644,10 +644,10 @@ export class CoreAppProvider {
* Set value of forceOffline flag. If true, the app will think the device is offline.
*
* @param value Value to set.
- * @deprecated since 4.1.0. Use CoreNetwork instead.
+ * @deprecated since 4.1.0. Use CoreNetwork.setForceConnectionMode instead.
*/
setForceOffline(value: boolean): void {
- CoreNetwork.setForceOffline(value);
+ CoreNetwork.setForceConnectionMode(value ? CoreNetworkConnection.NONE : CoreNetworkConnection.WIFI);
}
/**
diff --git a/src/core/services/cron.ts b/src/core/services/cron.ts
index df6ec5e33..32ec54823 100644
--- a/src/core/services/cron.ts
+++ b/src/core/services/cron.ts
@@ -21,7 +21,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreConstants } from '@/core/constants';
import { CoreError } from '@classes/errors/error';
-import { makeSingleton } from '@singletons';
+import { makeSingleton, Translate } from '@singletons';
import { CoreLogger } from '@singletons/logger';
import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/database/cron';
import { asyncInstance } from '../utils/async-instance';
@@ -81,10 +81,11 @@ export class CoreCronDelegateService {
protected async checkAndExecuteHandler(name: string, force?: boolean, siteId?: string): Promise {
if (!this.handlers[name] || !this.handlers[name].execute) {
// Invalid handler.
- const message = `Cannot execute handler because is invalid: ${name}`;
- this.logger.debug(message);
+ this.logger.debug(`Cannot execute cron job because is invalid: ${name}`);
- throw new CoreError(message);
+ throw new CoreError(
+ Translate.instant('core.errorsomethingwrong') + '
' + Translate.instant('core.errorsitesupport'),
+ );
}
const usesNetwork = this.handlerUsesNetwork(name);
@@ -92,11 +93,10 @@ export class CoreCronDelegateService {
if (usesNetwork && !CoreNetwork.isOnline()) {
// Offline, stop executing.
- const message = `Cannot execute handler because device is offline: ${name}`;
- this.logger.debug(message);
+ this.logger.debug(`Cron job failed because your device is not connected to the internet: ${name}`);
this.stopHandler(name);
- throw new CoreError(message);
+ throw new CoreError(Translate.instant('core.settings.cannotsyncoffline'));
}
if (isSync) {
@@ -105,11 +105,10 @@ export class CoreCronDelegateService {
if (syncOnlyOnWifi && !CoreNetwork.isWifi()) {
// Cannot execute in this network connection, retry soon.
- const message = `Cannot execute handler because device is using limited connection: ${name}`;
- this.logger.debug(message);
+ this.logger.debug(`Cron job failed because your device has a limited internet connection: ${name}`);
this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL);
- throw new CoreError(message);
+ throw new CoreError(Translate.instant('core.settings.cannotsyncwithoutwifi'));
}
}
@@ -118,7 +117,7 @@ export class CoreCronDelegateService {
try {
await this.executeHandler(name, force, siteId);
- this.logger.debug(`Execution of handler '${name}' was a success.`);
+ this.logger.debug(`Cron job '${name}' was successfully executed.`);
await CoreUtils.ignoreErrors(this.setHandlerLastExecutionTime(name, Date.now()));
@@ -127,11 +126,10 @@ export class CoreCronDelegateService {
return;
} catch (error) {
// Handler call failed. Retry soon.
- const message = `Execution of handler '${name}' failed.`;
- this.logger.error(message, error);
+ this.logger.error(`Cron job '${name}' failed.`, error);
this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL);
- throw new CoreError(message);
+ throw error;
}
});
diff --git a/src/core/services/network.ts b/src/core/services/network.ts
index d8a2f9a64..72f9ec98f 100644
--- a/src/core/services/network.ts
+++ b/src/core/services/network.ts
@@ -18,6 +18,17 @@ import { Network } from '@ionic-native/network/ngx';
import { makeSingleton } from '@singletons';
import { Observable, Subject, merge } from 'rxjs';
+export enum CoreNetworkConnection {
+ UNKNOWN = 'unknown',
+ ETHERNET = 'ethernet',
+ WIFI = 'wifi',
+ CELL_2G = '2g',
+ CELL_3G = '3g',
+ CELL_4G = '4g',
+ CELL = 'cellular',
+ NONE = 'none',
+};
+
/**
* Service to manage network connections.
*/
@@ -28,9 +39,21 @@ export class CoreNetworkService extends Network {
protected connectObservable = new Subject<'connected'>();
protected disconnectObservable = new Subject<'disconnected'>();
- protected forceOffline = false;
+ protected forceConnectionMode?: CoreNetworkConnection;
protected online = false;
+ get connectionType(): CoreNetworkConnection {
+ if (this.forceConnectionMode !== undefined) {
+ return this.forceConnectionMode;
+ }
+
+ if (CorePlatform.isMobile()) {
+ return this.type as CoreNetworkConnection;
+ }
+
+ return this.online ? CoreNetworkConnection.WIFI : CoreNetworkConnection.NONE;
+ }
+
/**
* Initialize the service.
*/
@@ -38,20 +61,25 @@ export class CoreNetworkService extends Network {
this.checkOnline();
if (CorePlatform.isMobile()) {
- this.onChange().subscribe(() => {
+ // We cannot directly listen to onChange because it depends on
+ // onConnect and onDisconnect that have been already overriden.
+ super.onConnect().subscribe(() => {
+ this.fireObservable();
+ });
+ super.onDisconnect().subscribe(() => {
this.fireObservable();
});
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
( window).Connection = {
- UNKNOWN: 'unknown', // eslint-disable-line @typescript-eslint/naming-convention
- ETHERNET: 'ethernet', // eslint-disable-line @typescript-eslint/naming-convention
- WIFI: 'wifi', // eslint-disable-line @typescript-eslint/naming-convention
- CELL_2G: '2g', // eslint-disable-line @typescript-eslint/naming-convention
- CELL_3G: '3g', // eslint-disable-line @typescript-eslint/naming-convention
- CELL_4G: '4g', // eslint-disable-line @typescript-eslint/naming-convention
- CELL: 'cellular', // eslint-disable-line @typescript-eslint/naming-convention
- NONE: 'none', // eslint-disable-line @typescript-eslint/naming-convention
+ UNKNOWN: CoreNetworkConnection.UNKNOWN, // eslint-disable-line @typescript-eslint/naming-convention
+ ETHERNET: CoreNetworkConnection.ETHERNET, // eslint-disable-line @typescript-eslint/naming-convention
+ WIFI: CoreNetworkConnection.WIFI, // eslint-disable-line @typescript-eslint/naming-convention
+ CELL_2G: CoreNetworkConnection.CELL_2G, // eslint-disable-line @typescript-eslint/naming-convention
+ CELL_3G: CoreNetworkConnection.CELL_3G, // eslint-disable-line @typescript-eslint/naming-convention
+ CELL_4G: CoreNetworkConnection.CELL_4G, // eslint-disable-line @typescript-eslint/naming-convention
+ CELL: CoreNetworkConnection.CELL, // eslint-disable-line @typescript-eslint/naming-convention
+ NONE: CoreNetworkConnection.NONE, // eslint-disable-line @typescript-eslint/naming-convention
};
window.addEventListener('online', () => {
@@ -65,12 +93,13 @@ export class CoreNetworkService extends Network {
}
/**
- * Set value of forceOffline flag. If true, the app will think the device is offline.
+ * Set value of forceConnectionMode flag.
+ * The app will think the device is offline or limited connection.
*
* @param value Value to set.
*/
- setForceOffline(value: boolean): void {
- this.forceOffline = !!value;
+ setForceConnectionMode(value: CoreNetworkConnection): void {
+ this.forceConnectionMode = value;
this.fireObservable();
}
@@ -89,20 +118,15 @@ export class CoreNetworkService extends Network {
* @return Whether the app is online.
*/
checkOnline(): void {
- if (this.forceOffline) {
+ if (this.forceConnectionMode === CoreNetworkConnection.NONE) {
this.online = false;
return;
}
- if (!CorePlatform.isMobile()) {
- this.online = navigator.onLine;
+ const type = this.connectionType;
- return;
- }
-
- let online = this.type !== null && this.type != this.Connection.NONE &&
- this.type != this.Connection.UNKNOWN;
+ let online = type !== null && type !== CoreNetworkConnection.NONE && type !== CoreNetworkConnection.UNKNOWN;
// Double check we are not online because we cannot rely 100% in Cordova APIs.
if (!online && navigator.onLine) {
@@ -123,6 +147,7 @@ export class CoreNetworkService extends Network {
/**
* Returns an observable to notify when the app is connected.
+ * It will also be fired when connection type changes.
*
* @return Observable.
*/
@@ -143,12 +168,11 @@ export class CoreNetworkService extends Network {
* Fires the correct observable depending on the connection status.
*/
protected fireObservable(): void {
- const previousOnline = this.online;
-
this.checkOnline();
- if (this.online && !previousOnline) {
+
+ if (this.online) {
this.connectObservable.next('connected');
- } else if (!this.online && previousOnline) {
+ } else {
this.disconnectObservable.next('disconnected');
}
}
@@ -159,18 +183,16 @@ export class CoreNetworkService extends Network {
* @return Whether the device uses a limited connection.
*/
isNetworkAccessLimited(): boolean {
- if (!CorePlatform.isMobile()) {
- return false;
- }
-
- const limited = [
- this.Connection.CELL_2G,
- this.Connection.CELL_3G,
- this.Connection.CELL_4G,
- this.Connection.CELL,
+ const limited: CoreNetworkConnection[] = [
+ CoreNetworkConnection.CELL_2G,
+ CoreNetworkConnection.CELL_3G,
+ CoreNetworkConnection.CELL_4G,
+ CoreNetworkConnection.CELL,
];
- return limited.indexOf(this.type) > -1;
+ const type = this.connectionType;
+
+ return limited.indexOf(type) > -1;
}
/**
diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts
index d508b06b3..15bce3bba 100644
--- a/src/core/services/sites.ts
+++ b/src/core/services/sites.ts
@@ -51,7 +51,7 @@ import { CoreRedirectPayload } from './navigator';
import { CoreSitesFactory } from './sites-factory';
import { CoreText } from '@singletons/text';
import { CoreLoginHelper } from '@features/login/services/login-helper';
-import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
+import { CoreErrorWithOptions } from '@classes/errors/errorwithtitle';
import { CoreAjaxError } from '@classes/errors/ajaxerror';
import { CoreAjaxWSError } from '@classes/errors/ajaxwserror';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
@@ -870,7 +870,7 @@ export class CoreSitesProvider {
const siteUrlAllowed = await CoreLoginHelper.isSiteUrlAllowed(site.getURL(), false);
if (!siteUrlAllowed) {
- throw new CoreErrorWithTitle(Translate.instant('core.login.sitenotallowed'));
+ throw new CoreErrorWithOptions(Translate.instant('core.login.sitenotallowed'));
}
this.currentSite = site;
@@ -1185,6 +1185,7 @@ export class CoreSitesProvider {
siteName: CoreConstants.CONFIG.sitename == '' ? siteInfo?.sitename: CoreConstants.CONFIG.sitename,
avatar: siteInfo?.userpictureurl,
siteHomeId: siteInfo?.siteid || 1,
+ loggedOut: !!site.loggedOut,
};
formattedSites.push(basicInfo);
}
@@ -1277,7 +1278,7 @@ export class CoreSitesProvider {
/**
* Logout the user.
*
- * @param forceLogout If true, site will be marked as logged out, no matter the value tool_mobile_forcelogout.
+ * @param options Logout options.
* @return Promise resolved when the user is logged out.
*/
async logout(options: CoreSitesLogoutOptions = {}): Promise {
@@ -1923,6 +1924,7 @@ export type CoreSiteBasicInfo = {
avatar?: string; // User's avatar.
badge?: number; // Badge to display in the site.
siteHomeId?: number; // Site home ID.
+ loggedOut: boolean; // If Site is logged out.
};
/**
diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts
index 70cd4c47a..3ee12557a 100644
--- a/src/core/services/utils/dom.ts
+++ b/src/core/services/utils/dom.ts
@@ -46,7 +46,6 @@ import { CoreViewerImageComponent } from '@features/viewer/components/image/imag
import { CoreFormFields, CoreForms } from '../../singletons/form';
import { CoreModalLateralTransitionEnter, CoreModalLateralTransitionLeave } from '@classes/modal-lateral-transition';
import { CoreZoomLevel } from '@features/settings/services/settings-helper';
-import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang';
import { CoreSites } from '@services/sites';
import { NavigationStart } from '@angular/router';
@@ -1345,17 +1344,22 @@ export class CoreDomUtilsProvider {
const alertOptions: AlertOptions = {
message: message,
- buttons: [Translate.instant('core.ok')],
};
if (this.isNetworkError(message, error)) {
alertOptions.cssClass = 'core-alert-network-error';
- } else if (error instanceof CoreErrorWithTitle) {
+ } else if (typeof error !== 'string' && 'title' in error) {
alertOptions.header = error.title || undefined;
} else {
alertOptions.header = Translate.instant('core.error');
}
+ if (typeof error !== 'string' && 'buttons' in error && typeof error.buttons !== 'undefined') {
+ alertOptions.buttons = error.buttons;
+ } else {
+ alertOptions.buttons = [Translate.instant('core.ok')];
+ }
+
return this.showAlertWithOptions(alertOptions, autocloseTime);
}
diff --git a/src/core/services/utils/text.ts b/src/core/services/utils/text.ts
index 723b3574c..6c024c41a 100644
--- a/src/core/services/utils/text.ts
+++ b/src/core/services/utils/text.ts
@@ -26,6 +26,7 @@ import { CoreFileHelper } from '@services/file-helper';
import { CoreDomUtils } from './dom';
import { CoreText } from '@singletons/text';
import { CoreUrl } from '@singletons/url';
+import { AlertButton } from '@ionic/angular';
/**
* Different type of errors the app can treat.
@@ -37,6 +38,8 @@ export type CoreTextErrorObject = {
body?: string;
debuginfo?: string;
backtrace?: string;
+ title?: string;
+ buttons?: AlertButton[];
};
/*
@@ -149,6 +152,27 @@ export class CoreTextUtilsProvider {
return error;
}
+ /**
+ * Add some title to an error message.
+ *
+ * @param error Error message or object.
+ * @param title Title to add.
+ * @return Modified error.
+ */
+ addTitleToError(error: string | CoreError | CoreTextErrorObject | undefined | null, title: string): CoreTextErrorObject {
+ let improvedError: CoreTextErrorObject = {};
+
+ if (typeof error === 'string') {
+ improvedError.message = error;
+ } else if (error && 'message' in error) {
+ improvedError = error;
+ }
+
+ improvedError.title = improvedError.title || title;
+
+ return improvedError;
+ }
+
/**
* Given an address as a string, return a URL to open the address in maps.
*
diff --git a/src/testing/services/behat-dom.ts b/src/testing/services/behat-dom.ts
index f872f3bac..fed631c74 100644
--- a/src/testing/services/behat-dom.ts
+++ b/src/testing/services/behat-dom.ts
@@ -18,9 +18,6 @@ import { CoreUtils } from '@services/utils/utils';
import { makeSingleton, NgZone } from '@singletons';
import { TestingBehatElementLocator, TestingBehatFindOptions } from './behat-runtime';
-// Containers that block containers behind them.
-const blockingContainers = ['ION-ALERT', 'ION-POPOVER', 'ION-ACTION-SHEET', 'CORE-USER-TOURS-USER-TOUR', 'ION-PAGE'];
-
/**
* Behat Dom Utils helper functions.
*/
@@ -331,13 +328,14 @@ export class TestingBehatDomUtilsService {
}
// Get containers until one blocks other views.
- containers.find(container => {
+ containers.some(container => {
if (container.tagName === 'ION-TOAST') {
container = container.shadowRoot?.querySelector('.toast-container') || container;
}
topContainers.push(container);
- return blockingContainers.includes(container.tagName);
+ // If container has backdrop it blocks the rest of the UI.
+ return container.querySelector(':scope > ion-backdrop') || container.classList.contains('backdrop');
});
return topContainers;
diff --git a/src/testing/services/behat-runtime.ts b/src/testing/services/behat-runtime.ts
index d8448e035..6161c0fd3 100644
--- a/src/testing/services/behat-runtime.ts
+++ b/src/testing/services/behat-runtime.ts
@@ -25,9 +25,8 @@ import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreComponentsRegistry } from '@singletons/components-registry';
import { CoreDom } from '@singletons/dom';
-import { IonRefresher } from '@ionic/angular';
-import { CoreCoursesDashboardPage } from '@features/courses/pages/dashboard/dashboard';
import { Injectable } from '@angular/core';
+import { CoreSites, CoreSitesProvider } from '@services/sites';
/**
* Behat runtime servive with public API.
@@ -53,6 +52,10 @@ export class TestingBehatRuntimeService {
return CorePushNotifications.instance;
}
+ get sites(): CoreSitesProvider {
+ return CoreSites.instance;
+ }
+
/**
* Init behat functions and set options like skipping onboarding.
*
@@ -348,23 +351,17 @@ export class TestingBehatRuntimeService {
this.log('Action - pullToRefresh');
try {
- // TODO We should generalize this to work with other pages. It's not possible to use
- // an IonRefresher instance because it doesn't expose any methods to trigger refresh,
- // so we'll have to find another way.
-
- const dashboard = this.getAngularInstance(
- 'page-core-courses-dashboard',
- 'CoreCoursesDashboardPage',
+ // 'el' is protected, but there's no other way to trigger refresh programatically.
+ const ionRefresher = this.getAngularInstance<{ el: HTMLIonRefresherElement }>(
+ 'ion-refresher',
+ 'IonRefresher',
);
- if (!dashboard) {
- return 'ERROR: It\'s not possible to pull to refresh the current page '
- + '(the dashboard page is the only one supported at the moment).';
+ if (!ionRefresher) {
+ return 'ERROR: It\'s not possible to pull to refresh the current page.';
}
- await new Promise(resolve => {
- dashboard.refreshDashboard({ complete: resolve } as IonRefresher);
- });
+ ionRefresher.el.dispatchEvent(new CustomEvent('ionRefresh'));
return 'OK';
} catch (error) {
diff --git a/src/tests/behat/navigation_deeplinks.feature b/src/tests/behat/navigation_deeplinks.feature
index 4394a477f..9c963164e 100644
--- a/src/tests/behat/navigation_deeplinks.feature
+++ b/src/tests/behat/navigation_deeplinks.feature
@@ -25,9 +25,7 @@ Feature: It navigates properly using deep links.
Scenario: Receive a push notification
Given I entered the app as "student2"
- When I press the user menu button in the app
- And I press "Log out" in the app
- And I wait the app to restart
+ When I log out in the app
And I press "Add" in the app
And I set the field "Your site" to "$WWWROOT" in the app
And I press "Connect to your site" in the app
@@ -67,9 +65,7 @@ Feature: It navigates properly using deep links.
Scenario: Open a link with a custom URL that calls WebServices for a logged out site
Given I entered the app as "student2"
- When I press the user menu button in the app
- And I press "Log out" in the app
- And I wait the app to restart
+ When I log out in the app
And I open a custom link in the app for:
| forum |
| Test forum |